Python小甲鱼学习笔记36-40

36类和对象:介绍

一、介绍对象
0、对象中的属性和方法,在编程中实际是什么?
  变量和函数。

1、类和对象是什么关系呢?
  类是对象的抽象表达,对象是类的实际表现。

2、如果我们定义了一个猫类,那你能想象出由“猫”类实例化的对象有哪些?
  黑猫,白猫,黑猫警长。

3、类的定义有些时候或许不那么“拟物”,有时候会抽象一些,例如我们定义一个矩阵类,那你会为此添加哪些属性和方法呢?
  添加长度,宽度,坐标,颜色等属性,计算面积和周长等方法。

4、类的属性定义应该尽可能抽象还是尽可能具体?
  抽象,这样才叫面向对象啊。

5、请用一句话概括面向对象的几个特征?
  封装:对外部隐藏对象的工作细节
  继承:子类自动共享父类之间数据和方法的机制
  >>> class MyList(list):
    pass 占位符 无意义
  >>> list1 = MyList()
  >>> list1.append(0)
  >>> list1
  [0]
  多态:可以对不同类的对象调用相同的方法,产生不同的结果,就是不同类的同一个方法名调用和后的结果不一样。

6、函数和方法有什么区别?
  方法多了一个self参数。

二、课后题
0、按照以下提示尝试定义一个Person类并生成类实例对象。

# 属性:姓名(默认姓名为“小甲鱼”)
# 方法:打印姓名
# 提示:放法中对属性的引用形式加上self,如self.name
class Person :
    name = '小甲鱼'
    def print_name(self) :
        print('名字是%s' % self.name)

1、按照以下提示尝试定义一个矩阵类并生成类实例对象。
属性:长和宽
方法:设置长和宽->setRect(self),获得长和宽->getRect(self),获得面积->getArea(self)
提示:方法中对属性的引用形式加上self,如self.width

#属性:长和宽
#方法:设置长和宽->setRect(self),获得长和宽->getRect(self),
#获得面积->getArea(self)
#提示:方法中对属性的引用形式加上self,如self.width
class Matrix:
    length = 5
    width = 4
    
    def setRect(self) :
        print('请输入矩形的长和宽')
        self.length = float(input('长:'))
        self.width = float(input('宽:'))

    def getRect(self) :
        print('这个矩形的长是:%d,宽是:%d' % (self.length,self.width))

    def getArea(self) :
        area = self.length * self.width
        print(area)

37类和对象:面向对象编程

一、self
0、self参数的作用是什么?
  绑定方法。self参数类似于人的身份证,每个实例对象都有唯一的self参数。

二、类的方法
0、如果我们不希望对象的属性或方法被外部直接引用,我们可以怎么做?(私有变量)
  我们可以在属性或方法名字前边加上双下划线,这样子从外部是无法直接访问到,会显示AttributeError错误。

>>> class Person:  
              __name = '小甲鱼'  
           def getName(self):  
               return self.__name  
         
             
>>> p = Person()  
>>> p.__name  
Traceback (most recent call last):  
       File "", line 1, in   
              p.__name  
AttributeError: 'Person' object has no attribute '__name'  
>>> p.getName()  
	'小甲鱼' 

因为加了“__”就变成私有元素,类外部不能直接访问,但可以通过类的方法间接访问。但其实Python只是把元素名改变了而已,可以通过“_类名__变量名”访问,即_Person__name。

1、类在实例化后哪个方法会被自动调用?
  __init__方法会在类实例化时被自动调用,称为魔法方法,又称为构造方法。

三、课后题
0、按照以下要求定义一个游乐园门票的类,并尝试计算2个成人+1个小孩平日票价。
a.平日票价100元
b.周末票价为平日的120%
c.儿童半价

#0、按照以下要求定义一个游乐园门票的类,并尝试计算2个成人+1个小孩平日票价。
#a.平日票价100元
#b.周末票价为平日的120%
#c.儿童半价
class Ticket:
    def __init__(self,people = 1,date = 1) :
        '成人1,平日1'
        if people == 1:
            if date == 1 :
                self.price = 100
            else :
                self.price = 120
        else :
            if date == 1 :
                self.price = 50
            else :
                self.price = 60

    def allprice(self,number = 1) :
        return self.price * number

adults = Ticket()
child = Ticket(2,1)
print(adults.allprice(2) + child.allprice(1))

1、游戏编程:按以下要求定义一个乌龟类和鱼类并尝试编写游戏。(初学者不一定可以完整实现,但请务必先自己动手,你会从中学习到很多知识的)

#1、游戏编程:按以下要求定义一个乌龟类和鱼类并尝试编写游戏。
#(初学者不一定可以完整实现,但请务必先自己动手,你会从中学习到很多知识的)
#a.假设游戏场景为范围(x,y)为0<=x<=10,0<=y<=10
#b.游戏生成1只乌龟和10条鱼
#c.它们的移动方向均随机
#d.乌龟的最大移动能力是2(Ta可以随机选择1还是2移动),鱼儿的最大移动能力是1
##e.当移动到场景边缘,自动向反方向移动
#f.乌龟初始化体力为100(上限)
#g.乌龟每移动一次,体力消耗1
#h.当乌龟和鱼坐标重叠,乌龟吃掉鱼,乌龟体力增加20
#i.鱼暂不计算体力
#j.当乌龟体力值为0(挂掉)或者鱼儿的数量为0游戏结束
'''      结构
1.设置游戏范围(x,y)为0<=x<=10,0<=y<=10
让位置为(x,y),一直在范围内

2.生成1只乌龟和10条鱼,乌龟体力值为100,且最大为100,鱼无体力

3.乌龟和鱼都移动,移动方向随机,乌龟走1或2步,体力消耗1,鱼走1步
如果移动到边界的自动反方向

4.如果乌龟和鱼坐标重叠,乌龟吃掉鱼,乌龟体力增加20

5.当乌龟体力值为0(挂掉)或者鱼儿的数量为0游戏结束
'''

import random

class Turtle :
    def __init__(self) :
        self.HP = 100  #乌龟体力值为100
        (self.x,self.y) = (random.randint(0,10),random.randint(0,10))  #位置随机
        self.GPS = (self.x,self.y)
    
    def crawl(self) : #乌龟移动
        stepcount = random.randint(1,2)
        if stepcount == 1 :  #一次移动一步
            direction = random.randint(1,4)
            if direction == 1 :  #上
                self.y += 1
                self.GPS = (self.x,self.y)
            elif direction == 2 :  #下
                self.y -= 1
                self.GPS = (self.x,self.y)
            elif direction ==3 :  #左
                self.x -= 1
                self.GPS = (self.x,self.y)
            else :                #右
                self.x += 1
                self.GPS = (self.x,self.y)
        else :            #一次移动两步
            direction = random.randint(1,4)
            if direction == 1 :   #上
                self.y += 2
                self.GPS = (self.x,self.y)
            elif direction == 2 :  #下
                self.y -= 2
                self.GPS = (self.x,self.y)
            elif direction ==3 :  #左
                self.x -= 2
                self.GPS = (self.x,self.y)
            else :                #右
                self.x += 2
                self.GPS = (self.x,self.y)
        #超出边界,当移动到场景边缘,自动向反方向移动
        if self.x < 0 :
            self.x = self.x * (-1)
            self.GPS = (self.x,self.y)
        elif self.x > 10 :
            self.x = 10 - (self.x - 10)
            self.GPS = (self.x,self.y)
        elif self.y < 0 :
            self.y = self.y * (-1)
            self.GPS = (self.x,self.y)
        elif self.y > 10 :
            self.y = 10 - (self.y - 10)
            self.GPS = (self.x,self.y)
            
    def reduceHP(self) :  #乌龟每移动一次,体力消耗1
        self.HP -= 1
            
    def eat(self) :  #如果乌龟和鱼坐标重叠,乌龟吃掉鱼,乌龟体力增加20
        self.HP += 20
        if self.HP > 100 :
            self.HP =100

class Fish :
    def __init__(self) :
        (self.x,self.y) = (random.randint(0,10),random.randint(0,10))  #位置随机
        self.GPS = (self.x,self.y)
    
    def tour(self) :  #鱼移动
        direction = random.randint(1,4)
        if direction == 1 :  #上
            self.y += 1
            self.GPS = (self.x,self.y)
        elif direction == 2 :  #下
            self.y -= 1
            self.GPS = (self.x,self.y)
        elif direction ==3 :  #左
            self.x -= 1
            self.GPS = (self.x,self.y)
        else :                #右
            self.x += 1
            self.GPS = (self.x,self.y)
        #超出边界,当移动到场景边缘,自动向反方向移动
        if self.x < 0 :
            self.x = self.x * (-1)
            self.GPS = (self.x,self.y)
        elif self.x > 10 :
            self.x = 10 - (self.x - 10)
            self.GPS = (self.x,self.y)
        elif self.y < 0 :
            self.y = self.y * (-1)
            self.GPS = (self.x,self.y)
        elif self.y > 10 :
            self.y = 10 - (self.y - 10)
            self.GPS = (self.x,self.y)
        
    def beeated(self) :  #如果乌龟和鱼坐标重叠,乌龟吃掉鱼
        del self.GPS
        
turtle1 = Turtle()
fish1 = Fish()
fish2 = Fish()
fish3 = Fish()
fish4 = Fish()
fish5 = Fish()
fish6 = Fish()
fish7 = Fish()
fish8 = Fish()
fish9 = Fish()
fish10 =Fish()
list1 = [fish1,fish2,fish3,fish4,fish5,fish6,fish7,fish8,fish9,fish10]
count = 0

print('=========================初始的游戏状况:====================')
print('乌龟的位置为:'+ str(turtle1.GPS))
print('乌龟的体力为:%d' % turtle1.HP)
for i in list1 :
    if i.GPS == turtle1.GPS :
        turtle1.eat()
        i.beeated()
        del i
    print('鱼的位置是:'+ str(i.GPS))
    
while turtle1.HP > 0 and len(list1) > 0 :
    turtle1.crawl()
    turtle1.reduceHP()
    for i in list1 :
        i.tour()
        if i.GPS == turtle1.GPS :
            turtle1.eat()
            i.beeated()
            list1.remove(i)
    count += 1
    print('=======================移动%d次之后的游戏状况:=================' % count)
    print('乌龟的位置为:'+ str(turtle1.GPS))
    print('乌龟的体力为:%d' % turtle1.HP)
    for i in list1 :
        print('鱼的位置是:'+ str(i.GPS))    

38类和对象:继承

一、格式
class 类名(父类类名):

二、测试题
0. 继承机制给程序猿带来最明显的好处是?
答:
  如果一个类 A 继承自另一个类 B,就把这个 A 称为 B 的子类,把 B 称为 A 的父类、基类或超类。继承可以使得子类具有父类的各种属性和方法,而不需要再次编写相同的代码(偷懒)。
  在子类继承父类的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类的原有属性和方法,使其获得与父类不同的功能。另外,为子类追加新的属性和方法也是常见的做法。
  但是,这里覆盖的是子类实例化对象里面的方法而已,对父类的方法没有影响。

  1. 如果按以下方式重写魔法方法 init,结果会怎样?
class MyClass:
    def __init__(self):
        return "I love FishC.com!"

答:会报错,因为 init 特殊方法不应当返回除了 None 以外的任何对象。

  1. 当子类定义了与相同名字的属性或方法时,Python 是否会自动删除父类的相关属性或方法?

答:不会删除!Python 的做法跟其他大部分面向对象编程语言一样,都是将父类属性或方法覆盖,子类对象调用的时候会调用到覆盖后的新属性或方法,但父类的仍然还在,只是子类对象“看不到”。

  1. 假设已经有鸟类的定义,现在我要定义企鹅类继承于鸟类,但我们都知道企鹅是不会飞的,我们应该如何屏蔽父类(鸟类)中飞的方法?

答:覆盖父类方法,例如将函数体内容写 pass,这样调用 fly 方法就没有任何反应了。

  1. super 函数有什么“超级”的地方?

答:super 函数超级之处在于你不需要明确给出任何基类的名字,它会自动帮您找出所有基类以及对应的方法。由于你不用给出基类的名字,这就意味着你如果需要改变了类继承关系,你只要改变 class 语句里的父类即可,而不必在大量代码中去修改所有被继承的方法。

  1. 多重继承使用不当会导致重复调用(也叫钻石继承、菱形继承)的问题,请分析以下代码在实际编程中有可能导致什么问题?
class A():
    def __init__(self):
        print("进入A…")
        print("离开A…")
 
class B(A):
    def __init__(self):
        print("进入B…")
        A.__init__(self)
        print("离开B…")
        
class C(A):
    def __init__(self):
        print("进入C…")
        A.__init__(self)
        print("离开C…")
 
class D(B, C):
    def __init__(self):
        print("进入D…")
        B.__init__(self)
        C.__init__(self)
        print("离开D…")

答:多重继承容易导致重复调用问题,下边实例化 D 类后我们发现 A 被前后进入了两次(有童鞋说两次就两次憋,我女朋友还不止呢……)。
这有什么危害?我举个例子,假设 A 的初始化方法里有一个计数器,那这样 D 一实例化,A 的计数器就跑两次(如果遭遇多个钻石结构重叠还要更多),很明显是不符合程序设计的初衷的(程序应该可控,而不能受到继承关系影响)。

>>> d = D()
进入D…
进入B…
进入A…
离开A…
离开B…
进入C…
进入A…
离开A…
离开C…
离开D…
  1. 如何解决上一题中出现的问题?
class A():
    def __init__(self):
        print("进入A…")
        print("离开A…")
 
class B(A):
    def __init__(self):
        print("进入B…")
        super().__init__()
        print("离开B…")
        
class C(A):
    def __init__(self):
        print("进入C…")
        super().__init__()
        print("离开C…")
 
class D(B, C):
    def __init__(self):
        print("进入D…")
        super().__init__()
        print("离开D…")
 
>>> d = D()
进入D…
进入B…
进入C…
进入A…
离开A…
离开C…
离开B…
离开D…

三、错误覆盖父类方法时的解决方案
  这是一个有错误的程序代码:

import random as r
class Fish:
        def __init__(self):
                self.x = r.randint(0, 10)
                self.y = r.randint(0, 10)
        def move(self):
                self.x -= 1
                print("我的位置是:", self.x, self.y)
class Goldfish(Fish):
        pass
class Shark(Fish):
        def __init__(self):
                self.hungry = True
        def eat(self):
                if self.hungry:
                        print("吃货的梦想就是天天有吃的^_^")
                        self.hungry = False
                else:
                        print("太撑了,吃不下了")

但是上面的程序是存在错误的。如下:

>>> fish = Fish()
>>> fish.move()
我的位置是: 2 9
>>> goldfish = Goldfish()
>>> goldfish.move()
我的位置是: -1 7
>>> shark = Shark()
>>> shark.eat()
吃货的梦想就是天天有吃的^_^
>>> shark.move()
Traceback (most recent call last):
  File "", line 1, in <module>
    shark.move()
  File "C:/Users/XiangyangDai/Desktop/上课代码/38-1.py", line 7, in move
    self.x -= 1
AttributeError: 'Shark' object has no attribute 'x'

我们在调用shark.move()方法的时候会报错,这是为什么呢?错误信息告诉我们,Shark 对象没有 x 的属性,Goldfish 和 Shark 都是继承Fish,Fish是有x的属性的,但是Shark重写了 init(self)方法,子类重写了父类的方法,就会把父类的方法给覆盖。要解决这个问题的话,我们应该在Shark 的类里面重写 init(self)方法的时候先调用父类的__init__(self),实现这样子的继承总共有两种技术。  
  1.调用未绑定的父类方法(该方法不重要)

import random as r
class Fish:
        def __init__(self):
                self.x = r.randint(0, 10)
                self.y = r.randint(0, 10)
        def move(self):
                self.x -= 1
                print("我的位置是:", self.x, self.y)
class Goldfish(Fish):
        pass
class Shark(Fish):
        def __init__(self):
                #调用未绑定的父类方法
                Fish.__init__(self)
                self.hungry = True
        def eat(self):
                if self.hungry:
                        print("吃货的梦想就是天天有吃的^_^")
                        self.hungry = False
                else:
                        print("太撑了,吃不下了")

这样就不会报错了,需要注意的是, Fish.init(self) 中的 self 是调用它的父类的方法,但是这个 self 是子类的实例对象。
  就相当于:Fish.init(shark)。实际上,在上面出错的程序代码运行之后,我们输入下面的语句可是可以的:这里就相当于重新进行了一次初始化。

>>> shark = Shark()
>>> Fish.__init__(shark)
>>> shark.move()
我的位置是: 6 1

2.使用 super 函数(完美方法)

import random as r
class Fish:
        def __init__(self):
                self.x = r.randint(0, 10)
                self.y = r.randint(0, 10)
        def move(self):
                self.x -= 1
                print("我的位置是:", self.x, self.y)
class Goldfish(Fish):
        pass
class Shark(Fish):
        def __init__(self):
                #使用super函数
                super().__init__()
                self.hungry = True
        def eat(self):
                if self.hungry:
                        print("吃货的梦想就是天天有吃的^_^")
                        self.hungry = False
                else:
                        print("太撑了,吃不下了")

super().init(),super 函数的超级之处就在于你不用给定任何父类的名字,如果继承有多重继承或者父类的名字太过复杂的时候,也不用给出父类的名字,就可以自动帮你一层一层的找出它所有父类里面对应的方法,由于你不需要给出父类的名字,也就意味着如果你要改变类的继承关系,你只需要修改 class Shark(Fish): 里面的父类的名字即可。
  
四、多重继承

>>> class Base1:
	def foo1(self):
		print("我是foo1,我为Base1代言...")
 
		
>>> class Base2:
	def foo2(self):
		print("我是foo2,我为Base2代言...")
 
		
>>> class C(Base1, Base2):
	pass
 
>>> c = C()
>>> c.foo1()
我是foo1,我为Base1代言...
>>> c.foo2()
我是foo2,我为Base2代言...

多重继承可以同时继承多个父类的属性和方法,但是多重继承很容易导致代码混乱,所以当你不确定你真的必须要使用多重继承的时候,请尽量避免使用。

四、课后题
0. 定义一个点(Point)类和直线(Line)类,使用 getLen 方法可以获得直线的长度。

#设点A(X1,Y1)、点B(X2,Y2),则两点构成的直线长度|AB| =根号下坐标差的平方和
#计算开根号用 math模块中的sqrt函数
#初始化两个点对象作为参数
import math

class Point :
    def __init__(self,x=0,y=0) :
        self.x = x
        self.y = y

class Line :
    def __init__(self,p1,p2) :
        self.x = p1.x - p2.x
        self.y = p1.y - p2.y
        
    def getlen(self):
        result = math.sqrt(self.x*self.x + self.y*self.y)
        return result

039类和对象:拾遗

一、组合
  组合的用法很简单,举例说明:

class Turtle:
        def __init__(self, x):
                self.num = x
class Fish:
        def __init__(self, x):
                self.num = x
class Pool:
        def __init__(self, x, y):
                self.turtle = Turtle(x)
                self.fish = Fish(y)
        def print_num(self):
                print("水池里有乌龟 %d 只,小鱼 %d 条!" %(self.turtle.num, self.fish.num))
 
>>> pool = Pool(1, 10)
>>> pool.print_num()
水池里有乌龟 1 只,小鱼 10 条!

所谓的组合,就是把类的实例化放到新类里面,那么它就把旧类给组合进去了,不用使用继承了,没有什么风险了。组合一般来说就是把几个没有继承关系,没有直线关系的几个类放在一起,就是组合。要实现纵向关系之间的类,就使用继承。
  Python 的特性还支持另外一种很流行的编程模式,叫做 Mix-in,叫做混入的意思,有兴趣的可以参见-> Python Mixin 编程机制。

二、类、类对象和实例对象
  类、类对象和实例对象是三个不同的物种。先看代码:

>>> class C:
	count = 0
 	
>>> a = C()
>>> b = C()
>>> c = C()
>>> a.count
0
>>> b.count
0
>>> c.count
0
>>> c.count += 10
>>> c.count
10
>>> a.count
0
>>> b.count
0
>>> C.count
0
>>> C.count += 100
>>> C.count
100
>>> a.count
100
>>> b.count
100
>>> c.count
10

我们这里有一个 C 类,只有一个属性 count ,初始化为0。实例化一个 a,一个 b,一个 c,显然 a.count = 0,b.count = 0,c.count = 0。如果对 c.count += 10,现在 c.count = 10,但是 a.count = 0,b.count = 0。因为 C 是一个类,在写完 C 之后就变成了一个类对象,因为Python无处不对象,所有的东西都是对象,方法也是对象,所以我们这里 C.count = 0 也是等于 0 。此时我们对这个类对象加等于100 , C.count += 100,此时 a.count = 100,b.count = 100,但是 c.count = 10。为什么会这样呢?
  其实是因为 c.count += 10 这里 c.count 被赋值的时候,我们是对实例化对象 c 的属性进行赋值,相当于我们生成了一个 count 来覆盖类对象的 count。
  类定义到类对象,还有实例对象a,b,c,需要注意的是,类中定义的属性都是静态属性,就像 C 里面的count,类属性和类对象是相互绑定的,并不会依赖于下面的实例对象,所以当 c.count += 10 的时候,并不会影响到 C,只是改变了 c 自身,因为在 c.count += 10 的时候,是实例对象 c 多了一个count 的属性,也就是实例属性,它把类属性给覆盖了。这在以后还会继续讲解,在此之前,我们先谈一下:如果属性的名字和方法相同时,属性会把方法覆盖掉。举例说明:

>>> class C:
	def x(self):
		print("X-man")
 
		
>>> c = C()
>>> c.x()
X-man
>>> c.x = 1
>>> c.x
1
>>> c.x()
Traceback (most recent call last):
  File "", line 1, in <module>
    c.x()
TypeError: 'int' object is not callable

这就是初学者容易发生的一个问题,如果属性的名字和方法名相同,属性会覆盖方法。为了避免名字上的冲突,大家应该遵守一些约定俗成的规矩:
  1.不要试图在一个类里边定义出所有能想到的特征和方法,应该使用继承和组合机制来进行扩展。
  2.用不同词性命名,如属性名用名词,方法名用动词。
  
三、到底什么是绑定?
  Python 严格要求方法需要有实例才能被调用,这种限制其实就是Python 所谓的绑定概念。

四、测试题
0. 什么是组合(组成)?
答:Python 继承机制很有用,但容易把代码复杂化以及依赖隐含继承。因此,经常的时候,我们可以使用组合来代替。在Python里组合其实很简单,直接在类定义中把需要的类放进去实例化就可以了。

// 乌龟类
class Turtle:
    def __init__(self, x):
        self.num = x
// 鱼类
class Fish:
    def __init__(self, x):
        self.num = x
// 水池类
class Pool:
    def __init__(self, x, y):
        self.turtle = Turtle(x)        // 组合乌龟类进来
        self.fish = Fish(y)        // 组合鱼类进来
     
    def print_num(self):
        print("水池里总共有乌龟 %d 只,小鱼 %d 条!" % (self.turtle.num, self.fish.num))
 
>>> pool = Pool(1, 10)
>>> pool.print_num()
  1. 什么时候用组合,什么时候用继承?
    答:根据实际应用场景确定。简单的说,组合用于“有一个”的场景中,继承用于“是一个”的场景中。例如,水池里有一个乌龟,天上有一个鸟,地上有一个小甲鱼,这些适合使用组合。青瓜是瓜,女人是人,鲨鱼是鱼,这些就应该使用继承啦。

  2. 类对象是在什么时候产生?
    答:当你这个类定义完的时候,类定义就变成类对象,可以直接通过“类名.属性”或者“类名.方法名()”引用或使用相关的属性或方法。

  3. 如果对象的属性跟方法名字相同,会怎样?
    答:如果对象的属性跟方法名相同,属性会覆盖方法。

class C:
        def x(self):
                print('Xman')
 
>>> c = C()
>>> c.x()
Xman
>>> c.x = 1
>>> c.x
1
>>> c.x()
Traceback (most recent call last):
  File "", line 1, in <module>
    c.x()
TypeError: 'int' object is not callable
  1. 请问以下类定义中哪些是类属性,哪些是实例属性?
class C:
        num = 0
        def __init__(self):
                self.x = 4
                self.y = 5
                C.count = 6

答:num 和 count 是类属性(静态变量),x 和 y 是实例属性。大多数情况下,你应该考虑使用实例属性,而不是类属性(类属性通常仅用来跟踪与类相关的值)。

  1. 请问以下代码中,bb 对象为什么调用 printBB() 方法失败?
class BB:
        def printBB():
                print("no zuo no die")
 
>>> bb = BB()
>>> bb.printBB()
Traceback (most recent call last):
  File "", line 1, in <module>
    bb.printBB()
TypeError: printBB() takes 0 positional arguments but 1 was given

答:因为 Python 严格要求方法需要有实例才能被调用,这种限制其实就是 Python 所谓的绑定概念。所以 Python 会自动把 bb 对象作为第一个参数传入,所以才会出现 TypeError:“需要 0 个参数,但实际传入了 1 个参数“。
正确的做法应该是:

class BB:
        def printBB(self):
                print("no zuo no die")
 
>>> bb = BB()
>>> bb.printBB()
no zuo no die

五、课后题
0.

#请动手在一个类中定义一个变量,用于跟踪该类有多少个实例被创建,
#当实例化一个对象,这个变量+1,当销毁一个对象,这个变量自动减一

class Myclass:
    count = 0
    def __init__(self) :
        Myclass.count += 1
    def __del__(self) :
        Myclass.count -= 1
'''
>>> a = Myclass()
>>> b = Myclass()
>>> c = Myclass()
>>> Myclass.count
3
>>> del a
>>> Myclass.count
2
>>> del b,c
>>> Myclass.count
0
'''
  1. 定义一个栈(Stack)类,用于模拟一种具有后进先出(LIFO)特性的数据结构。
#定义一个栈类,用于模拟一种具有后进先出的特性的数据结构。
#方法名:      含义:
#isEmpty() ;判断当前栈是否为空(返回True或False)
#push();   往栈的顶部压入一个数据项
#pop()   ;  从栈顶弹出一个数据项(并在栈中删除)
#top()  ;   显示当前栈顶的一个数据项
#bottom() ; 显示当前栈底的一个数据项
class Stack :
    def __init__(self, start=[]):
        self.list1 = []
        for x in start:
            self.push(x)

    def isEmpty(self) :
        if len(self.list1) :
            return True
        else :
            return False

    def push(self,x) :
        self.list1.append(x)

    def pop(self) :
        if len(self.list1) == 0 :
            return '栈为空'
        else :
            self.list1.pop()

    def top(self) :
        length = len(self.list1)
        if length == 0 :
            return '栈为空'
        else :
            return self.list1[length - 1]#self.list1[-1]更简单

    def bottom(self) :
        length = len(self.list1)
        if length == 0 :
            return '栈为空'
        else :
            return self.list1[0]

040类和对象:一些相关的BIF

一、issubclass(class, classinfo)
  如果第一个参数 class 是第二个参数 classinfo 的子类,就返回 True,关于这个函数有几点需要注意的:
  1.一个类被认为是其自身的子类
  2.classinfo 可以是类对象组成的元组,只要 class 是其中一个候选类的子类,就返回 True
  
二、isinstance(object, classinfo)
  检查一个实例对象 object 是否属于一个类 classinfo,关于这个函数有几点需要注意的:
  1.如果第一个参数不是对象,则永远返回 False
  2.如果第二个参数不是类或者由类对象组成的元组,则抛出一个 TypeError 异常
  
另外,Python 提供了几个BIF让我们访问对象的属性:

三、hasattr(object, name) attr = attribute:属性
  测试一个对象是否有指定的属性。name 要用引号把属性名引起来。

>>> class C:
	def __init__(self, x = 0):
		self.x = x
 		
>>> c1 = C()
>>> hasattr(c1, "x")
True
>>> hasattr(c1, x)
Traceback (most recent call last):
  File "", line 1, in <module>
    hasattr(c1, x)
NameError: name 'x' is not defined

四、getattr(object, name[ , default] )
  返回对象指定的属性值。如果指定的属性不存在,如果你有设置 default,它会把这个default 参数打印出来,否则会抛出一个AttributeError异常。

>>> class C:
	def __init__(self, x = 0):
		self.x = x
 		
>>> c1 = C()
>>> getattr(c1, 'x')
0
>>> getattr(c1, 'y')
Traceback (most recent call last):
  File "", line 1, in <module>
    getattr(c1, 'y')
AttributeError: 'C' object has no attribute 'y'
>>> getattr(c1, 'y', '你所访问的属性不存在')
'你所访问的属性不存在'

五、setattr(object, name, value)
  设定对象中指定属性的值,如果指定的属性不存在,会新建一个新的属性,并给其赋值。

>>> setattr(c1, 'y', '来自江南的你')
>>> getattr(c1, 'y', '你所访问的属性不存在')
'来自江南的你'

六、delattr(object, name)
  删除对象中指定的属性,如果属性不存在,就抛出一个AttributeError异常。
  
七、property(fget = None, fset = None, fdel = None, doc = None)  
  俗话说,条条大路通罗马。Python 其实提供了好几个方式供你选择,property 是一个BIF,作用是通过属性设置属性,
  property 函数的作用就是设置一个属性,这个属性就是去设置定义好的属性,它的第一个参数 fget 是获取属性的方法,第一个参数 fset 是设置属性的方法,第一个参数 fdel 是删除属性的方法。举例说明:

>>> class C:
	def __init__(self, size = 10):
		self.size = size
	def getSize(self):
		return self.size
	def setSize(self, value):
		self.size = value
	def delSize(self):
		del self.size
	x = property(getSize, setSize, delSize)
	
>>> c1 = C()
>>> c1.x
10
>>> c1.getSize()
10
>>> c1.x = 18
>>> c1.getSize()
18
>>> c1.setSize(20)
>>> c1.x
20
>>> del c1.x
>>> c1.getSize()
Traceback (most recent call last):
  File "", line 1, in <module>
    c1.getSize()
  File "", line 5, in getSize
    return self.size
AttributeError: 'C' object has no attribute 'size'

property 的优势:举个例子,在上面这个例子中,这个程序慢慢写的很复杂了,有一天,你想把这个程序进行大改,把函数名进行改写,如果没有 property,那你提供给用户的调用接口就西药修改,就会降低用户体验,但是有了property,问题就不存在了,因为提供给用户的接口都是 x,程序里面无论如何修改,property里面的参数跟着改进行了,用户还是只用调用 x 来设置或者获取 size 属性就可以了。

八、测试题
  0. 如何判断一个类是否为另一个类的子类?
答:使用 issubclass(class, classinfo) 函数,如果第一个参数(class)是第二个参数(classinfo)的一个子类,则返回 True,否则返回 False。

1. 如何判断对象 a 是否为 类 A 的实例对象?
答:使用 isinstance(object, classinfo) 函数,如果第一个参数(object)是第二个参数(classinfo)的实例对象,则返回 True,否则返回 False。
另外以下这些常识你应该知道:
  如果 object是 classinfo 的子类的一个实例,也符合条件
  如果第一个参数不是对象,则永远返回False
  classinfo 可以是类对象组成的元祖,只要class与其中任何一个候选类的子类,则返回 True
  如果第二个参数不是类或者由类对象组成的元祖,会抛出一个 TypeError 异常
  
  2. 如何优雅地避免访问对象不存在的属性(不产生异常)?
答:有两种方法可以做到。
  第一种先使用 hasattr(object, name) 函数判断属性是否存在,如果存在再访问(第一个参数(object)是对象,第二个参数(name)是属性名的字符串形式);
  第二种方法是直接使用 getattr(object, name[, default]) 函数并设置 default 参数(返回对象指定的属性值,如果指定的属性不存在,返回default(可选参数)的值)。

3. Python 的一些 BIF 很奇怪,但却十分有用。请问 property() 函数的作用是什么?
答:property() 函数允许编程人员轻松、有效地管理属性访问。

4. 请补充以下代码,使程序可以正常运行:

class C:
    def __init__(self, size=10):
        self.size = size
 
    def getXSize(self):
        return self.size
 
    def setXSize(self, value):
        self.size = value
 
    def delXSize(self):
        del self.size
 
        # 此处应该补充一句代码,程序才能正常运行
 
>>> c.x
10
>>> c.x = 12
>>> c.x
12

答:x = property(getXSize, setXSize, delXSize)

5. 通过自学【Python扩展阅读】Python 函数修饰符(装饰器).的使用,使用修饰符修改以下代码:
代码A:

class CodeA:
    def foo():
        print("调用静态方法 foo()")
 
        # 将 foo() 方法设置为静态方法
        foo = staticmethod(foo)

代码B:

class CodeB:
    def foo(cls):
        print("调用类方法 foo()")
 
        # 将 foo() 方法设置为类方法
        foo = classmethod(foo)

答:其实正是因为设置静态方法和类方法过于讨人吐槽,因此 Python 的作者才开发出了函数修饰符的形式替代。

代码A:

class CodeA:
        @staticmethod
    def foo():
        print("调用静态方法 foo()")

代码B:

class CodeB:
        @classmethod
    def foo(cls):
        print("调用类方法 foo()")
  1. 你真的理解了修饰符的用法吗?那请你写出以下代码没有用上修饰符的等同形式:
@something
def f():
    print("I love FishC.com!")

答:其实 Python 的修饰符就是一种优雅的封装,但要注意的是只可以在模块或类定义内对函数进行修饰,不允许修饰一个类。

一个修饰符就是一个函数,它将被修饰的函数做为参数,并返回修饰后的同名函数或其它可调用的东西。

@something
def f():
    print("I love FishC.com!")
 
# 相当于
 
def f():
    print("I love FishC.com!")
 
f = something(f)
  1. 通过自学【Python扩展阅读】property 的详细使用方法,将第 4 题的代码修改为“使用属性修饰符创建描述符”的方式实现。
    答:可能你还没听说过描述符(这个概念在你学完接下来的几节课自然会了解),但这一点都影响聪明的你修改这个程序。

代码清单:

class C:
    def __init__(self, size=10):
        self.size = size
        
    @property
    def x(self):
        return self.size
 
    @x.setter
    def x(self, value):
        self.size = value
 
    @x.deleter
    def x(self):
        del self.size

你可能感兴趣的:(Python小甲鱼学习笔记,python)