Hello,大家好,我是你们的老朋友景天,上一章我们聊到了python的面相对象编程的语法,以及一些面相对象的基本操作,今天我们继续深入探讨下面向对象的其他一些常用方法和规范
1.如何在类外访问私有成员
class Plane(): # 公有成员 captian = "赵沈阳" # 私有成员 __air_sister = "3名空姐" # 公有绑定方法 def fly(self): print("飞机要非要平流层,才能减少震动",self.__air_sister) # 私有绑定方法 def __age(self): print("空姐年龄保密") # 公有无参方法 def fly2(): print("航天飞机飞到天空层,翱翔太空") # 私有无参方法 def __earn(): print("机长的收入保密") def pub_get1(self): print(self.__air_sister) self.__age() def pub_get2(): print(Plane.__air_sister) Plane.__earn() #实例化对象 obj = Plane()
方法一.访问私有成员 (不推荐) 使用改名方法,_类名+私有属性名
#python私有化,其实是: 采取了改名策略 => _类名 + __air_sister #print(obj.__air_sister) print(obj._Plane__air_sister) print(Plane.__dict__) {'__module__': '__main__', 'captian': '赵沈阳', '_Plane__air_sister': '3名空姐', 'fly': <function Plane.fly at 0x7f2774616158>, '_Plane__age': <function Plane.__age at 0x7f27746161e0>, 'fly2': <function Plane.fly2 at 0x7f2774616268>, '_Plane__earn': <function Plane.__earn at 0x7f27746162f0>, '__dict__': <attribute '__dict__' of 'Plane' objects>, '__weakref__': <attribute '__weakref__' of 'Plane' objects>, '__doc__': None}
方法二.访问私有成员 (使用类中的公有方法,间接访问私有成员,包括调用私有属性和私有方法) (推荐)
obj = Plane() obj.pub_get1() Plane.pub_get2()
2.使用类对象删除相应的成员
1.对象可以访问类中的公有成员,但是无权修改或者删除该类中的成员
2.对象在访问成员时,优先访问该对象自己的成员,如果没有在访问类的,类如果也没有直接报错;
使用对象删除类的属性,方法,报错
#删除对象成员属性,但可以删除对象自己添加,修改的属性,方法
obj.captian = "赵世超" del obj.captian print(obj.captian)
#删除对象成员方法
obj.basketball = lambda : print("我的私人飞机可以在天上打篮球") print(obj.__dict__) obj.basketball() del obj.basketball print(obj.__dict__) #obj.basketball() error
#删除类中成员属性 要通过类来删除
del Plane.captian print(Plane.__dict__) #Plane.captian #print(obj.captian) error
#删除类中成员方法,删除类成员方法 不能加括号
del Plane.fly2 #Plane.fly2() error
#注意: 对象无法调无参方法!! 返回来,类可以调用对象的绑定方法么? 可以!!
Plane.fly(obj)
#类调用有参方法,要把类对象传进去
要删除私有的属性,方法。要在类内里面删除
3.构造方法
(1)init 构造方法 也就是初始化方法
所有编程语言都有构造方法
触发时机:实例化对象,初始化的时候触发 功能:为对象添加成员 参数:参数不固定,至少一个self参数 返回值:无
一般添加成员方法都是在类外,构造方法可以在创建对象的同时给对象创建成员
(2) 基本语法,至少加一个参数,本对象
class MyClass(): def __init__(self): print("构造方法被触发 ... ") self.color = "屎黄色" #实例化对象 obj = MyClass() print(obj.__dict__) print(obj.color)
如果定义了构造方法,则实例化对象的时候,该方法就会被调用。执行该方法的代码,可以为对象添加成员属性。只是给对象添加成员,并没有给类添加成员
只要创建对象,构造方法就被自动调用。 不是手动调用的
可以看到test对象被自动添加了color成员,并且__init__方法里面的代码也被自动执行
现在构造函数是把所有创建的对象都添加了color:蓝色 这个属性,并不是我们想要的结果。
此时就需要让构造函数带多个参数。创建对象时,可以带上参数,这样每个对象 都可以按传参不同而成员不同
(3) 带有多个参数的构造方法
class MyClass(): def __init__(self,color): self.color = color # 实例化对象 obj1 = MyClass("狗屎绿") print(obj1.color) obj2 = MyClass("粉嫩红") print(obj2.color)
设置成员属性时,self.设置的成员属性名 = 传参传进来的值
这样就给对象添加了成员
这样,针对不同对象,就可以通过传参赋予不同的属性值
(3)类可以是一个,对象可以是多个,创造的对象彼此是独立的,可以给构造函数带多个参数,分别赋予不同的值;
class Children(): def __init__(self,name,skin): self.name = name self.skin = skin def cry(self): print("小孩一下生久哇哇哇的哭") def la(self): print("小孩一下生久拉粑粑") def __eat(self): print("小孩一下生就要吃奶奶..") #这种成员方法,通过self.参数名 调用对象属性,创建对象的时候可通过类传参,对象调用的时候,通过对象传参,不用手动传参 def info(self): print("小孩的名字:{},小孩的肤色{}".format(self.name,self.skin)) #这种成员方法,直接调用参数名 对象调用该成员方法的时候,需要手动传参 。可以覆盖构造函数在创建对象时的值 def info2(self,name,skin): print("小孩的名字:{},小孩的肤色{}".format(name,skin))
#实例化对象
afanda = Children("阿凡达","深蓝色") afanda.cry() afanda.info() haoke = Children("绿巨人","绿色的") haoke.la() haoke.info() wangbaoqiang = Children("王宝强","亮绿色") wangbaoqiang.info() # wangbaoqiang.__eat() error wangbaoqiang.info2("张保张","黄色")
构造函数除了self外有几个参数,创建对象的时候,就必须带几个参数,不能少也不能多。否则创建对象失败
不能少参数
也不能多
成员方法带参数时,调用成员方法应该带参数
当成员方法里面是通过self.属性名 时,是通过对象来传参,调用函数时,传的参不会传进去
成员方法
调用:
当成员方法直接通过变量名来调用,调用时传参,会把创建对象时的参数覆盖
#这种成员方法,通过变量名调用参数。此时的参数是成员方法的形参,不是类或对象的成员属性
对象调用的时候,需要手动传参,会把创建对象时的参数覆盖。 但是这样调用比较麻烦,应该让对象来操作一切
4.继承
一个类除了自身所拥有的属性方法之外,还获取了另外一个类的成员属性和方法 是一种继承关系
被继承的类叫做父类(基类,超类),继承的类叫做子类(衍生类)
在python中所有类都继承object这个父类
继承: (1) 单继承 (2) 多继承
子类继承父类所有的属性和方法
节省了代码量
不由任意内置类型派生的类,称之为经典类
class 类名:
代码
…
新式类:
class 类名(object):
代码
…
Python面向对象的继承指的是多个类之间的从属关系,即子类默认继承父类所有的属性和方法
如果一个类不去写继承哪个类,默认继承的是object, object类是顶级类或基类。其他子类叫派生类
1.单继承
(1) 子父继承之后,子类可以调用父类的公有成员
class Human(object): eye = "黑色的" def jump(self): print("古代人类都能上树") def beat_animal(self): print("古代人类都会打猎") def __makefire(self): print("古代人类会生火") class Man(Human): pass obj = Man() obj.jump()
(2) 子父继承之后,子类不能调用父类的私有成员
class Woman(Human): def pub_func(self): self.__makefire() obj2 = Woman() # obj2.__makefire() 不行 # obj2.pub_func() 不行
(3) 子父继承之后,子类可以重写父类的同名公有方法和属性
class Children(Human): def beat_animal(self): print("小孩天生只会打豆豆,不会打猎") obj3 = Children() obj3.beat_animal()
2.多继承
多继承就是一个类同时继承了多个父类
当一个类继承多个父类的时候,默认使用第一个父类的同名属性和方法
(1) 基本语法
class Father(): property = "风流倜傥,才华横溢,玉树临风,才高八斗,学富五车,英姿洒窗" def f_hobby(self): print("吃喝嫖赌抽,坑蒙拐骗偷,抽烟喝酒烫头") class Mother(): property = "倾国倾城,貌美如花,沉鱼落雁,闭月羞花,婀娜多姿,前凸后翘" def m_hobby(self): print("蹦野迪,社会摇,打麻将,网红勾引小鲜肉") class Daughter(Father,Mother): pass obj = Daughter() print(obj.property) obj.m_hobby()
#当一个类继承多个父类的时候,默认使用第一个父类的同名属性和方法。按继承括号里面的先后顺序,谁在前先调用谁的
(2) 多继承的成员调用
class Father(): property = "风流倜傥,才华横溢,玉树临风,才高八斗,学富五车,英姿洒窗" def f_hobby(): print("吃喝嫖赌抽,坑蒙拐骗偷,抽烟喝酒烫头") class Mother(): property = "倾国倾城,貌美如花,沉鱼落雁,闭月羞花,婀娜多姿,前凸后翘" def m_hobby(self): print(self.property) print("蹦野迪,社会摇,打麻将,网红勾引小鲜肉") super()调用父类方法: 语法一:super(当前类名, self).函数() 语法二:super().函数() (1)super本身是一个类 super()是一个对象 传子类对象 用于调用父类的绑定方法 (2)super() 只应用在绑定方法中,默认自动传递self对象 (前提:super所在作用域存在self) (3)super用途: 解决复杂的多继承调用顺序 使用super()可以自动查找父类。调用的顺序遵循类.mro()方法返回的的列表顺序。比较适合单继承 class Son(Father,Mother): property = "打游戏,吃小零食" def m_hobby(self): print("son中m_hobby方法") # 用类调用成员 def skill1(self): Father.f_hobby() print(Mother.property) # 用对象调用成员 """self按照顺序找: 对象本身 => 类 => 父类 对应的成员 """ def skill2(self): print(self.property) self.m_hobby() # 用super调用成员 """super()只调用父类的相关成员,顺带传递对象参数""" def skill3(self): print(super()) print(super().property) super().m_hobby() obj2 = Son() # obj2.skill1()
通过类调用指定类下的成员属性,类名.属性名
obj2.property = "喜欢看lol,dnf,wow,跑跑卡丁车,ddo,霸王大陆,澄海3c" # obj2.skill2() obj2.skill3()
即便在子类中存在同名方法或属性,也不调用。只调用父类方法和属性
super() 只应用在绑定方法中,默认自动传递self对象 (前提:super所在作用域存在self)
如果调用非绑定方法,会报错
f_hobby()方法非绑定方法
通过super()调用报错
通过super调用父类方法,是把子类对象传过去,执行,如果方法中调用对象成员属性,则调用的是子类成员属性
(3).菱形继承 (钻石继承) super深度理解
“”"
Human
Man Woman
Children
“”"
四个类中都有同名方法,该怎么调
class MyClass(): pass class Human(): pty = 1 def feelT(self): print("古代人类,天热了,光腚1") print(self.pty) print("古代人类,天冷了,穿寿衣2") class Man(Human): # pty = 2 def feelT(self): print("男人,天热了,光膀子3") print(super(),"<==2==>") super().feelT() print("男人,天冷了,光腚4") class Woman(Human): # pty = 3 def feelT(self): print("女人,天热了,脱毛5") print(super(),"<==3==>") super().feelT() print("女人,天冷了,穿貂6") class Children(Man,Woman): # pty = 4 def feelT(self): print("小孩,天热了,光腚7") print(super(),"<==1==>") super().feelT() print("小孩,天冷了,多喝热水8") # ### super的深层理解 obj = Children() obj.feelT() # 73512648
super调用顺序
#mro: 方法解析顺序 (c3算法计算的)
#语法: 类.mro() => 列表
m :method
r :resolution
o :order
super 会自动根据mro列表返回出来的顺序关系,依次调用
super作用:专门用于解决复杂的多继承调用顺序关系;依照mro返回的列表顺序,依次调用;
super调用的顺序:会按照c3算法的广度优先原则进行调用
super传参:会默认在调用方法时,传递该对象参数;
lst = Children.mro() print(lst) [ <class '__main__.Children'>, <class '__main__.Man'>, <class '__main__.Woman'>, <class '__main__.Human'>, <class 'object'> ]
根据 类.mro方法返回的列表顺序,来调用。遇到super().feelT()暂停,查找下一个调用
当不确定调用顺序时,可以打印下这个列表,来确认
# ### issubclass与isinstance # issubclass 判断类的子父关系(应用在类与类之间) 第一个参数是子,第二个参数是父 是父子关系返回True,非父子关系返回False """只要在一条继承链上满足关系即可""" res = issubclass(Children,Man) res = issubclass(Children,Human) res = issubclass(Children,MyClass) # 如果元组当中有一个父类满足,即返回真 res = issubclass(Children, (Man,Human,MyClass) ) print(res)
# isinstance 判断对象的类型 (应用在类与对象之间) 第一个参数是对象,第二个参数是类 """只要在一条继承链上满足关系即可""" res = isinstance(obj,Children) res = isinstance(obj,Human) res = isinstance(obj,MyClass) # 如果元组当中有一个类满足,即返回真 res = isinstance(obj, (Man,Human,MyClass) ) print(res)
#该对象只要是该类,或者该类的子类创建的,返回的都是True
(4).多继的承弊端
多继承的弊端会造成菱形继承这种情况,找不清调用顺序
super对象按照mro列表的顺序依次调用,解决菱形继承存在的问题
经典类:深度优先 (python2.x)
新式类:广度优先 (python3.x)
写多继承时,尽量避免造成不同类相同方法名的情况,提高代码质量 高内聚,低耦合
高内聚:一个模块只完成一个任务,专一性高
低耦合:模块与模块之间可以彼此独立不冲突,方便移植复用.