第十一章 类和对象
本章内容如下:
1、对象=属性+方法
2、面向对象编程
2.1 self是什么
2.2 你听说过python的魔法方法吗
2.3 公有和私有
3、继承
3.1 调用未绑定的父类方法
3.2 使用super函数
4、多重继承
5、组合
6、类、类对象和实例对象
7、到底什么是绑定
8、一些相关的BIF
9、作业
10、扩展阅读
++++++++++++++++++++++++++++++++
1、对象=属性+方法

一个对象的特征称为“属性”,一个对象的行为称为“方法”。通过下面的例子来介绍对象:
class Turtle: # Python 中的类名约定以大写字母开头 # 特征的描述我们称为属性,在代码层面来看其实就是变量 color = 'green' weight = 10 legs = 4 shell = True mouth = '大嘴' # 方法实际就是函数,通过调用这些函数来完成某些工作 def climb(self): print("我正在很努力的向前爬......") def run(self): print("我正在飞快的向前跑......") def bite(self): print("咬死你咬死你!!") def eat(self): print("有得吃,真满足^_^") def sleep(self): print("困了,睡了,晚安,Zzzz") """ 1、类:以上代码定义了对象的特征(属性)和行为(方法),但还不是一个完整的对象,将定义的这些称为类(Class)。 2、实例对象(Instance Objects):需要使用类来创建一个真正的对象,这个对象就叫作这个类的一个实例(Instance)。 如:Turtle() 3、类的实例化:创建一个对象,也叫作类的实例化,如:tt = Turtle() """ #类的实例化 tt = Turtle() """" 注意: 1、类名后面跟着笑括号,与函数的调用一样的 2、类名约定用大写字母开头,函数用小写字母开头 3、赋值操作不是必需的,但如果没有把创建好的实例对象赋值给一个变量,那这个对象就没法使用, 因为没有任何引用指向这个实例,最终会被python的垃圾收集机制自动回收 """ """ 1、要调用对象里的方法,使用点操作符(.)即可 2、调用对象里的属性,与方法类似 """
#调用方法 tt.climb() #我正在很努力的向前爬...... tt.bite() #咬死你咬死你!! tt.sleep() #困了,睡了,晚安,Zzzz
#调用属性 print(tt.color) # green print(tt.weight) # 10 print(tt.legs) # 4
2、面向对象编程
2.1 self是什么
self相当于C++的this指针,告诉python所调用的方法和属性,是属于哪一个实例化对象的
""" 如果把类比作是图纸,那么类的实例化的对象才是真正可以住的房子。 self就相当于每个房子的门牌号,告诉python这个房子是那个实例化对象的 由同一个类可以生成无数个对象,当一个对象的方法被调用的时候, 对象会将自身的引用作为第一个参数传给该方法, 那么python就知道需要操作那个对象的方法了。 """ class Ball: def setName(self,name): self.name = name def kick(self): print("我叫%s,噢~谁踢我?!"%self.name) a = Ball() #实例化一个对象a , a.setName("飞火流星") #在调用a的方法时,self会告诉python调用的是a的方法 b = Ball() b.setName('团对之星') c = Ball() c.setName("土豆") a.kick() #我叫飞火流星,噢~谁踢我?! b.kick() #我叫团对之星,噢~谁踢我?! c.kick() #我叫土豆,噢~谁踢我?! """ 如果没有self指名,调用的方法是那个实例化对象的,就会乱了套 """
2.2 你听说过python的魔法方法吗
构造函数__init__
""" #python的这些具有魔力的方法,总是被双下化线所包围。 __init__() 方法称为构造方法,__init__()方法的魔力体现在只要实例化一个对象,这个方法就会在对象被创建时自动调用。 实例化对象时是可以传入参数的,这些参数会自动传入__init__()方法中,可以通过重写这个方法来自定义对象的初始化操作。 """ class Potato: def __init__(self,name): #初始化时,传入参数name self.name = name def kick(self): print("我叫%s,噢~谁踢我?!"%self.name) p = Potato('土豆') p.kick() # 我叫土豆,噢~谁踢我?!
2.3 公有和私有
公有
#公有 #默认上对象的属性和方法都是公开的,可以直接通过点操作符(.)进行方法 class Person: name = "小甲鱼" p = Person() print(p.name) #属性调用不加括号,方法的调用要加括号 # 小甲鱼
私有
#定义私有变量只需要在变量名或函数名前加上“__”两个下划线,那么这个函数或者变量就会变成私有的了 class Person: __name = "小甲鱼" # 变量name 变为私有的了 p = Person() print(p.__name) #报错,不能直接访问 # AttributeError: 'Person' object has no attribute '__name'
#私有函数 class __Person: #函数变成私有的了 name = "小甲鱼" p = __Person() print(p.name) #运行正常
这样在外部将变量名“隐藏”起来了,理论上如果要访问,就需要从内部进行
class Person: def __init__(self,name): self.__name = name def getName(self): return self.__name p = Person('小甲鱼') #p.__name # 不能直接访问 #AttributeError: 'Person' object has no attribute '__name' print(p.getName()) # 可以间接访问 #小甲鱼
可以使用"_类名__变量名"即可访问双下横线开头的私有变量
#_类名__变量名 p._Person__name ''' Python目前的私有机制其实是伪私有的,Python的类是没有权限控制的,所有变量都是可以被外部调用的 '''
3、继承
继续的定义
""" 语法: class 类名(被继承的类): ... 被继承的类称为基类,父类或超类;继承者称为子类,一个子类可以继承它的父类的任何属性和方法 """ class Parent: def hello(self): print("正在调用父类的方法...") class Child(Parent): pass p = Parent() p.hello() # 正在调用父类的方法... c = Child() c.hello() #正在调用父类的方法...
子类定义与父类同名的方法或属性,则会自动覆盖父类对应的方法或属性
""" 注意: 如果子类定义与父类同名的方法或属性,则会自动覆盖父类对应的方法或属性 """ class Parent: def hello(self): print("正在调用父类的方法...") class Child(Parent): def hello(self): #与父类同名的方法 print("正在调用子类的方法...") p = Parent() p.hello() # 正在调用父类的方法... c = Child() c.hello() #正在调用子类的方法...
3.1 调用未绑定的父类方法
定义一个金鱼(Goldfish)、鲤鱼(Carp)、三文鱼(Salmon),还有鲨鱼(Shark)
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 Carp(Fish): pass class Salmon(Fish): pass # 上边几个都是食物,食物不需要有个性,所以我们直接继承Fish类的全部属性和方法即可 # 下边我们定义鲨鱼类,这个是吃货,除了继承Fish类的属性和方法,我们还要添加一个吃的方法
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() #我的位置是: 9 6 goldfish = Goldfish() goldfish.move() #我的位置是: 5 6 shark = Shark() shark.eat() #吃货的梦想就是天天有得吃^_^ shark.move() # AttributeError: 'Shark' object has no attribute 'x'
会发现 shark.move()会报错,为什么?同样是继承Fish类,为什么金鱼(Goldfish)可以移动,而鲨鱼(Shark)一移动就报错呢?
""" 其实这里的抛出的异常说的很清楚了:Shark对象没有X属性。原因其实是这样的:在Shark类中,重写了魔法方法__init__,但新的__init__ 方法里边没有初始化鲨鱼的X坐标和Y坐标,因此调用move()方法就会出错。那么解决这个问题的方案很明显了,应该在鲨鱼类中 重写__init__方法的时候先调用基类Fish的__init__方法。 """
实现方法一:调用未绑定的父类方法
class Shark(Fish): def __init__(self): Fish.__init__(self) #重写父类Fish的__init__ self.hungry = True def eat(self): if self.hungry: print("吃货的梦想就是天天有得吃^_^") self.hungry = False else: print("太撑了,吃不下!")
最终代码及运行结果:
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 Carp(Fish): pass class Salmon(Fish): pass # 上边几个都是食物,食物不需要有个性,所以我们直接继承Fish类的全部属性和方法即可 # 下边我们定义鲨鱼类,这个是吃货,除了继承Fish类的属性和方法,我们还要添加一个吃的方法 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 = Fish() fish.move() #我的位置是: 9 6 goldfish = Goldfish() goldfish.move() #我的位置是: 5 6 shark = Shark() shark.eat() #吃货的梦想就是天天有得吃^_^ shark.move() #我的位置是: 9 7
3.2 使用super函数
方法二:使用super函数
#super函数能够帮我自动找到基类的方法 class Shark(Fish): def __init__(self): super().__init__() self.hungry = True def eat(self): if self.hungry: print("吃货的梦想就是天天有得吃^_^") self.hungry = False else: print("太撑了,吃不下!") """ super函数的"超级"之处在于你不需要明确给出任何基类的名字,它会自动帮您找出所有基类以及对应的方法。 """
4、多重继承
""" 多重继承 class 类名(父类1,父类2,父类3,...) ... """ class Base1: def fool(self): print('我是fool,我在Base1中...') class Base2: def foo2(self): print('我是foo2,我在Base2中...') class C(Base1,Base2): pass c = C() c.fool() #我是fool,我在Base1中... c.foo2() #我是foo2,我在Base2中...
5、组合
同一类型的用继承,不同类型的用组合
#组合,就是把需要的类放进来实例化就可以了 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))
6、类、类对象和实例对象

class C: count = 0 a = C() #类的实例化 b = C() #类的实例化 c = C() #类的实例化 c.count +=10 # print(a.count,b.count,c.count) # 0 0 10 C.count += 100 print(a.count,b.count,c.count) #100 100 10
需要注意的是,如果属性的名字跟方法名相同,属性会覆盖方法。
class C: def x(self): print("Xman") c = C() c.x() c.x = 1 # 属性和方法名相同,覆盖方法,重写赋值 print(c.x) c.x() #方法被覆盖,所以报错 # TypeError: 'int' object is not callable
为了避免一些名字上的冲突,应该遵守一些约定成熟的规矩:
1、类的定义要“少吃多餐”,尽量用继承和组合机制来进行扩展。
2、不同的词性命名,如属性名用名词,方法名用动词,并使用骆驼命名法等
7、到底什么是绑定
python严格要求方法需要由实例才能被调用,这种限制其实就是python所谓的绑定概念。
案例一
class BB: def printBB(): #缺失self print("no zuo no die") BB.printBB() #如果进行实例化,由于没有self指明是那个实例对象的,所以根本无法调用里边的函数 bb = BB() bb.printBB() #TypeError: printBB() takes 0 positional arguments but 1 was given
#这是由于绑定机制,会自动的把bb对象作为第一个参数传入,所以才会出现TypeError
深入理解
class CC: def setXY(self,x,y): self.x = x self.y = y def printXY(self): print(self.x,self.y) dd = CC() print(dd.__dict__) # 查看__dict__所拥有的属性 # {} 一个空字典 print(CC.__dict__) """ 结果: {'__module__': '__main__', 'setXY': <function CC.setXY at 0x00000000022E9510>, 'printXY': <function CC.printXY at 0x00000000022E9598>, '__dict__': <attribute '__dict__' of 'CC' objects>, '__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None} __dict__属性是由一个字典组成,字典中仅有实例对象的属性,不显示类属性和特殊属性,键表示的是属性名,值表示属性相应的数据值 """ dd.setXY(4,5) #赋值 print(dd.__dict__) #这时就有值了 # {'x': 4, 'y': 5} # 实例对象dd有了两个新的属性,而且这两个属性仅属于实例对象的 print(CC.__dict__) #没有 {'x': 4, 'y': 5} """ 为什么会这样? 完全归功于self参数:当实例对象dd去调用setXY方法的时候,它传入 的第一个参数就是dd,那么self.x = 4,self.y=5也就相当于 dd.x= 4,dd.y=5,所以你在实例对象,甚至类对象中都看不到x和y,因为这两个属性是只属于实例对象dd的。 """ # 接着往下,删除类实例,实例对象dd还能否调用printXY方法 del CC #删除类实例 print(dd.printXY()) # 4 5
8、一些相关的BIF
1、issubclass(clas,classinfo)
issubclass(class,classinfo) ''' 如果第一个参数(class)是第二个参数(classinfo)的一个子类,则返回True,否则返回False (1)一个类被认为是其自身的子类 (2)classinfo可以是类对象组成的元组,只要class是其中任何一个候选类的子类,则返回True. (3)在其他情况下,会抛出一个TypeError异常 ''' class A: pass class B(A): pass print(issubclass(B ,A)) # True print(issubclass(B ,B )) # True print(issubclass(B,object)) #object是所有类的基类 # True class C: pass print(issubclass(B ,C)) # True
2、isinstance(object,classinfo)
isinstance(object,classinfo) """ 如果第一个参数(object)是第二个参数(classinfo)的实例对象,则返回True,否则返回False: (1)如果object是classinfo的子类的一个实例,也符合条件 (2)如果第一个参数不是对象,则永远返回False (3)classinfo可以是类对象组成的元组,只要object是其中任何一个候选对象的实例,则返回 True (4)如果第二个参数不是类或者由于类对象组成的元组,会抛出一个TypeError异常 """ class A: pass class B(A): pass class C: pass print(isinstance(B ,C)) # False b1 = B() print(isinstance(b1,B)) # True print(isinstance(b1,C)) # False print(isinstance(b1,A)) # True print(isinstance(b1,(A,B,C))) # True
3、hasattr(object,name)
hasattr(object,name) #作用就是测试一个对象里是否有指定的属性 #第一个参数(object)是对象,第二个参数(name)是属性名(属性的字符串名字) class C: def __init__(self,x =0): self.x = x c1 = C() print(hasattr(c1,'x')) #注意,属性名要用引号扩起来 #True
4、getattr(object,name[,default])
getattr(object,name[,default]) #返回对象指定的属性值,如果指定的属性不存在,则返回default(可选参数)的值;若没有设置default参数,则抛出ArttributeError异常。 class C: def __init__(self,x =0): self.x = x c1 = C() print(getattr(c1,'x')) # 0 print(getattr(c1,'y')) # AttributeError: 'C' object has no attribute 'y' print(getattr(c1,'y','您所访问的属性不存在...')) # 您所访问的属性不存在...
5、setattr(object,name,value)
setattr(object,name,value) #设置对象中指定属性的值,如果指定的属性不存在,则会新建属性并赋值 class C: def __init__(self,x =0): self.x = x c1 = C() setattr(c1,'y','Fishc') print(getattr(c1,'y')) # Fishc
6、delattr(object,name)
delattr(object,name) #用于删除对象中指定的属性,如果属性不存在,则会抛出AttributeError异常 class C: def __init__(self,x =0): self.x = x c1 = C() setattr(c1,'y','Fishc') delattr(c1,'y') delattr(c1,'z') # AttributeError: z print(getattr(c1,'y')) # Fishc
7、property(fget=None,fset=None,fdel=None,doc=None)
property(fget=None,fset=None,fdel=None,doc=None) #他的作用是通过属性来设置属性 #第一个参数是获得属性的方法名,第二个参数是设置属性的方法名,第三个参数是删除属性的方法名 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) c = C() print(c.x) c.x = 13 print(c.size) del c.x print(c.size) # AttributeError: 'C' object has no attribute 'size'
9、作业
[课后作业] 第036讲:类和对象:给大家介绍对象 | 课后测试题及答案
[课后作业] 第037讲:类和对象:面向对象编程 | 课后测试题及答案
[课后作业] 第038讲:类和对象:继承 | 课后测试题及答案
[课后作业] 第039讲:类和对象:拾遗 | 课后测试题及答案
[课后作业] 第040讲:类和对象:一些相关的BIF | 课后测试题及答案
10、扩展阅读

浙公网安备 33010602011771号