类的定义

类的定义的语法

class ClassName:    <statement-1>    .    .    .    <statement-N>

示例代码

class Door:    def __init__(self, number, status):        self.number = number        self.status = status

<!--more-->

类的实例化

class Door:    def __init__(self, number, status):        self.number = number        self.status = statusdoor = Door(1001, 'open')door.numberdoor.status
  • 创建对象应用类名(__init__ 函数除第一个参数外的参数列表)
  • 创建对象的时候理论执行了 __init__函数
  • __init__ 函数并不会创建对象

函数创立及初始化的过程

  1. 首先创建对象
  2. 对象作为self参数传递给__init__函数
  3. 返回self

作用域

类变量

示例代码

In [1]: class A:   ...:         NAME = 'A'  # 类的间接上级作用域 叫做类变量   ...:         def __init__(self, name):   ...:             self.name = name  # 关联到实例的变量 叫做实例变量   ...:          In [2]: a = A('a')In [3]: a.NAMEOut[3]: 'A'In [4]: a.nameOut[4]: 'a'In [5]: A.NAMEOut[5]: 'A'In [6]: A.name---------------------------------------------------------------------------AttributeError                            Traceback (most recent call last)<ipython-input-6-61c1cc534250> in <module>()----> 1 A.nameAttributeError: type object 'A' has no attribute 'name'In [7]: a2 = A('a2')In [8]: a2.NAMEOut[8]: 'A'In [9]: a2.NAME = 'A2'  # 给示例a2的类变量NAME赋值In [10]: a2.NAMEOut[10]: 'A2'In [11]: a.NAMEOut[11]: 'A'In [12]: A.NAME  # 类变量没有变动Out[12]: 'A'In [13]: a2.xxx = 3In [14]: a2.xxx  # 赋值之后a2多了xxx属性Out[14]: 3In [15]: A.NAME = 'AA'  # 间接批改类的类变量In [16]: A.NAMEOut[16]: 'AA'In [17]: a.NAME  # 对应的实例的类变量也产生了扭转Out[17]: 'AA'In [18]: a2.NAME  # a2的类变量在之前的赋值被笼罩了,因而扭转类变量的并不会影响a2Out[18]: 'A2'

所以

  • 类变量对类和实例都可见
  • 所有实例共享类变量
  • 当给实例的类变量赋值时,相当于动静的给这个实例减少了一个属性,笼罩了类变量

属性查找程序

  • __dict__: 实例变量的字典
  • __class__: 失去实例对应的类
  • 先查找__dict__在查找__class__

代码

In [1]: class A:   ...:     NAME = 'A'   ...:     def __init__(self, name):   ...:         self.name = name   ...:         In [2]: a = A('a')In [3]: a.NAMEOut[3]: 'A'In [4]: a.__class__.NAMEOut[4]: 'A'In [5]: a.__dict__Out[5]: {'name': 'a'}In [6]: a.__class__  # a.__class__示意实例对应的类Out[6]: __main__.AIn [7]: a.NAME = 'AA'In [8]: a.__dict__  # 笼罩类变量之后__dict__减少了一个键值对Out[8]: {'NAME': 'AA', 'name': 'a'}In [9]: a.__dict__['NAME'] = 'AAA'  # 能够间接批改__dict__In [10]: a.__dict__Out[10]: {'NAME': 'AAA', 'name': 'a'}In [11]: a.__class__.NAMEOut[11]: 'A'In [12]: a.__class__.__dict__Out[12]: mappingproxy({'NAME': 'A',              '__dict__': <attribute '__dict__' of 'A' objects>,              '__doc__': None,              '__init__': <function __main__.A.__init__>,              '__module__': '__main__',              '__weakref__': <attribute '__weakref__' of 'A' objects>})

类装璜器

参数是一个类,并且返回一个类的函数就能够是一个类装璜器。

类装璜器通常用于给类减少属性,如果减少办法,则都是类级的办法。

代码1:给类减少属性

函数办法减少:定义set_name函数给类F减少一个NAME属性

In [1]: class F:   ...:     pass   ...: In [2]: def set_name(cls, name):  # 给cls减少属性NAME=name   ...:     cls.NAME = name   ...:     return cls   ...: In [3]: F1 = set_name(F, 'F')  # 返回F自身,并且F1指向FIn [4]: F1.NAMEOut[4]: 'F'In [5]: f1 = F1()In [6]: f1.NAMEOut[6]: 'F'In [7]: F1.__dict__Out[7]: mappingproxy({'NAME': 'F',              '__dict__': <attribute '__dict__' of 'F' objects>,              '__doc__': None,              '__module__': '__main__',              '__weakref__': <attribute '__weakref__' of 'F' objects>})In [8]: f1.__dict__Out[8]: {}In [9]: f1.__class__Out[9]: __main__.FIn [10]: F.__dict__  # 实质上减少的还是类FOut[10]: mappingproxy({'NAME': 'F',              '__dict__': <attribute '__dict__' of 'F' objects>,              '__doc__': None,              '__module__': '__main__',              '__weakref__': <attribute '__weakref__' of 'F' objects>})

对set_name函数进行柯里化,实现带参数的类装璜器

In [2]: def set_name(name):   # 传入参数name   ...:     def wrap(cls):   # 装璜器是wrap   ...:         cls.NAME = name   ...:         return cls   ...:     return wrap   ...: In [3]: @set_name('G')   ...: class G:   ...:     pass   ...: In [4]: G.NAMEOut[4]: 'G'In [5]: class G:   ...:     pass   ...: In [6]: G = set_name('G')(G)  # 装璜器的函数调用办法In [7]: G.NAMEOut[7]: 'G'

代码2:给类减少办法

类装璜器get_name给类H减少一个办法__get_name__

In [1]: def get_name(cls):   ...:     def _get_name(self):   ...:         return cls.__name__   ...:     cls.__get_name__ = _get_name  # 给cls减少__get_name__指向_get_name   ...:     return cls   ...: In [2]: @get_name   ...: class H:   ...:     pass   ...: In [3]: h = H()In [4]: h.__get_name__()Out[4]: 'H'In [5]: H.__dict__Out[5]: mappingproxy({'__dict__': <attribute '__dict__' of 'H' objects>,              '__doc__': None,              '__get_name__': <function __main__.get_name.<locals>._get_name>,              '__module__': '__main__',              '__weakref__': <attribute '__weakref__' of 'H' objects>})

类办法/静态方法

办法的定义都是类级的,然而有的办法应用实例调用,有的办法应用类来调用

  • 类办法:当一个办法,被classmethod装璜时, 第一个参数会变成类自身, 这样的办法叫类办法
  • 当一个办法, 被staticmethod装璜的时候,不会主动传递第一个参数, 这样的办法叫静态方法

代码

class I:    def print(self):  # 实例办法        print('instance method')    @classmethod    def class_print(cls):  # 类办法        print(id(cls))        print('class method')    @staticmethod     def static_print():  # 静态方法        print('static method')    def xxx_print():  # 一个一般办法        print('this is a function')
  • 实例办法只能由实例调用
  • 类办法能够被类和实例应用,并且被实例应用时,传入的第一个参数还是类
  • 静态方法能够被类和实例应用,都不会传入第一个参数
  • 类中的一般办法,因为没有传入self,因而只能被类应用,实例无奈应用
  • 各种办法依据首参来决定。

访问控制

双下划线

  • 所有双下划线开始,非双下划线结尾的成员,都是公有成员
  • 严格的说, Python里没有真正公有成员
  • Python的公有成员是通过改名实现的:_类名 + 带双下划綫的属性
  • 除非真的有必要,并且革除明确的晓得会有什么结果,否则不要通过改名规定批改公有成员
In [1]: class Door:   ...:     def __init__(self, number, status):   ...:         self.number = number   ...:         self.__status = status  # 双下划线开始, 非双下划綫结尾的都是公有的, 在类内部无法访问   ...:     def open(self):   ...:         self.__status = 'opening'   ...:     def close(self):   ...:         self.__status = 'closed'   ...:     def status(self):   ...:         return self.__status   ...:     def __set_number(self, number):  # # 双下滑先开始, 非双下划线结尾的办法也是公有办法   ...:         self.number = number   ...:         In [2]: door = Door(1001, 'closed')In [3]: door.__status  # 无法访问公有属性---------------------------------------------------------------------------AttributeError                            Traceback (most recent call last)<ipython-input-3-d55234f04e7f> in <module>()----> 1 door.__statusAttributeError: 'Door' object has no attribute '__status'In [4]: door.__dict__  # door对象含有的属性_Door__statusOut[4]: {'_Door__status': 'closed', 'number': 1001}In [5]: door.__status = 'hahaha'  # 给对象创立了新的属性,并没有批改到__statusIn [6]: door.__statusOut[6]: 'hahaha'In [7]: door.__dict__Out[7]: {'_Door__status': 'closed', '__status': 'hahaha', 'number': 1001}In [8]: door.status()Out[8]: 'closed'In [9]: door.open()In [10]: door.status()Out[10]: 'opening'In [11]: door.__set_number(1002)---------------------------------------------------------------------------AttributeError                            Traceback (most recent call last)<ipython-input-11-888a73f63746> in <module>()----> 1 door.__set_number(1002)AttributeError: 'Door' object has no attribute '__set_number'In [12]: door._Door__statusOut[12]: 'opening'In [13]: door._Door__status = 'hehehe'  # _类名 + 带双下划綫的属性的形式间接批改公有成员In [14]: door.status()Out[14]: 'hehehe'

单下划线

  • 单下划线是一种习用法, 人为标记此成员为公有, 然而解释器不不做任何解决
In [1]: class A:   ...:     def __init__(self):   ...:         self._a = 3   ...:         In [2]: a = A()In [3]: a._aOut[3]: 3In [4]: a._a = 4In [5]: a._aOut[5]: 4In [6]: a.__dict__Out[6]: {'_a': 4}

property装璜器

引入property装璜器

class Door:    def __init__(self, number):        self.__number = number    def get_number(self):        return self.__number    def set_number(self, number):        self.__number = number

当把number属性变成公有属性__number之后,无奈间接拜访失去,只能通过get_numberset_number两个函数拜访__number属性。

如果既能限度参数拜访,又能够用相似属性这样简略的形式来拜访类的变量,这个时候就能够应用property装璜器了。

  • Python内置的@property装璜器就是负责把一个办法变成属性调用的

property装璜器应用

class Door:    def __init__(self, number):        self.__number = number    # property 装璜器会把一个仅有self参数的函数,变成一个属性, 属性的值,为办法的返回值    @property    def number(self):        return self.__number    # property setter 装璜器, 能够把一个办法转化为对此赋值,但此办法有肯定要求    # 1.同名 2.必须接管两个参数 self 和 value, value为所赋的值    @number.setter    def number(self, number):        self.__number = number    @number.deleter    def number(self):        print('cannot remove number property')door = Door(1001)door.number  # 返回1001door.number = 1002door.number  # 返回1002del door.number  # 输入cannot remove number property

继承

单继承

  • 在类名后加括号 括号中是继承列表, 称之为父类或者基类或者超类
  • 继承一个显著的益处就是能够获取父类的属性和办法
class Base:    PUBLIC_CLASS_VAR = 'PUBLIC_CLASS_VAR'    __PRIVATE_CLASS_VAR = 'PRIVATE_CLASS_VAR'    def __init__(self):        self.public_instance_var = 'public_instance_var'        self.__private_instance_var = 'private__instance_var'    @classmethod    def public_class_method(cls):        return 'public_class_method'    @classmethod    def __private_class_method(cls):        return 'private_class_method'    @staticmethod    def public_static_method():        return 'public static method'    @staticmethod    def __private_static_method():        return 'private static method'    def public_instance_method(self):        return 'public_instance_method'    def __private_instance_method(self):        return 'private_instance_method'class Sub(Base):    passsub = Sub()sub.__dict__# 输入{'_Base__private_instance_var': 'private__instance_var', 'public_instance_var': 'public_instance_var'}
  • 但凡私有的都能继承
  • 但凡公有的都不能继承
  • 原来是什么,继承过去还是什么

办法重写

  • 当子类和父类有同名成员的时候, 子类的成员会笼罩父类的同名成员
  • 当父类含有一个带参数的初始化办法的时候,子类肯定须要一个初始化办法,并且在初始化办法中调用父类的初始化办法
  • super办法:super(type, obj) =》type:类名,obj:传递给后续办法的第一个参数
class Base:    def __init__(self):        self.__a = 4    def print(self):        print('Base.print')    @classmethod    def cls_print(cls):        print('Base.cls_print')class Sub(Base):    def print(self):  ## 当子类和父类有同名成员的时候, 子类的成员会笼罩父类的同名成员        print('Sub.print')    @classmethod    def cls_print(cls):        print('Sub.cls_print')    def foo(self):        # 调用父类的print        super().print()        # super(Sub, self).print()    @classmethod    def cls_foo(cls):        #cls.cls_print()        #Base.cls_print()        super().cls_print()class SubSub(Sub):    def print(self):        print('SubSub.print')    @classmethod    def cls_print(cls):        print('SubSub.cls_print')    def foo(self):        # 调用Base的print        super(SubSub, self).print()        # 代理 TYPE 的父类的办法, 并且应用 obj 绑定  第一个参数 指定调用谁的间接父类, 第二个参数指定当调用时,传递什么作为办法的第一个参数        super(Sub, self).print()        super(SubSub, SubSub).cls_print()  # 类办法的时候能够传递类,也能够传递实例self    @classmethod    def cls_foo(cls):        # Base.cls_print()        super(Sub, cls).cls_print()

多继承与MRO

等效的类定义

class A:    passclass A(object):    passclass A():    passs

多继承

  • 在继承列表里存在多个类的时候示意多继承
  • 多继承会把继承列表里的所有私有成员都继承过去
class A:    def method(self):        print('method of A')class B:    def method(self):        print('method of B')class C(A, B):    passc = C()c.method()  # 输入method of A

MRO

定义一个多继承,如下

class A:    def method(self):        print('method of A')class B:    def method(self):        print('method of B')class C(A, B):    passclass E(A):    def method(self):        print('method of E')class F(E, A):    passF().method()  # 输入method of E

如果定义类G继承自(A, E),如下

class G(A, E):  # 在定义的时候会间接报错    pass---------------------------------------------------------------------------TypeError                                 Traceback (most recent call last)<ipython-input-51-dcac33a3d00c> in <module>()----> 1 class G(A, E):      2     passTypeError: Cannot create a consistent method resolutionorder (MRO) for bases E, A

报错显示:Cannot create a consistent method resolution order (MRO) for bases E, A

办法解析程序(MRO)不满足报错

剖析基类E,A的MRO

>>> A.__mro__(__main__.A, object)>>> E.__mro__(__main__.E, __main__.A, object)>>> F.__mro__(__main__.F, __main__.E, __main__.A, object)

所以,mro序列就是继承的先后顺序

那么G类的mro序列应该就是(G, A, E, object),Python通过C3算法来确定多继承的时候是否满足mro的两个准则

  1. 本地优先: 本人定义或重写的办法优先,依照继承列表,从左到右查找
  2. 枯燥性:所有子类,也要满足查找程序

C3算法的次要作用是:在多继承时判断属性来自于哪个类,无奈判断时抛出TypeError

C3算法

class B(O) :则B的mro序列为: [B, O]class B(A1, A2, ..., An) :则B的mro序列为: [B] + merge(mro(A1), mro(A2), ..., mro(An), [A1, A2, ..., An, O])

merge操作就是C3算法的外围,merge步骤如下:

* 遍历列表* 看第一个列表的首元素    * 它在其余列表中也是首元素    * 或者它在其余列表不存在* 满足以上条件,则移除该首元素,合并到mro中* 不满足,则抛出异样

C3算法剖析F类的mro

mro(F) -> [F] + merge(mro(E), mro(A), [E, A, O])    -> [F] + merge([E, A, O], [A, O], [E, A, O])    -> [F, E] + merge([A, O], [A, O], [A, O])    -> [F, E, A] + merge([O], [O], [O])    -> [F, E, A, O]

merge操作胜利,mro解析正确,最终mro为[F, E, A, O]

C3算法剖析G类的mro

mro(G) -> [G] + merge(mro(A), mro(E), [A, E, O])    -> [G] + merge([A, O], [E, A, O], [A, E, O])    -> raise TypeError:

第一个列表的首元素为A,在第二个列表中存在但不是首元素,不满足merge的条件,间接抛出异样。

论断

  1. 应该尽量避免多继承
  2. 多继承会对程序的心智累赘造成十分大的压力

Mixin类

参考

  1. 廖雪峰-多重继承与MixIn
  2. 知乎-Mixin是什么概念?
  3. Python Cookbook-利用Mixins扩大类性能

在编程中,mixin是指为继承自它的class提供额定的性能, 但它本身却是不独自应用的类.。在具备多继承能力的编程语言中, mixin能够为类减少额定性能或办法。

因而,MixIn模式的目标就是给一个类减少多个性能,这样,在设计类的时候,咱们优先思考通过多重继承来组合多个MixIn的性能,而不是设计多层次的简单的继承关系。

在Python 3.5.2 源代码 socketserver.py 中的639到643行能够看到以下四个类的定义

class ForkingUDPServer(ForkingMixIn, UDPServer): passclass ForkingTCPServer(ForkingMixIn, TCPServer): passclass ThreadingUDPServer(ThreadingMixIn, UDPServer): passclass ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
  • BaseServer:server类的基类
  • UDPServer:UDP server class,继承自BaseServer
  • TCPServer:TCP server class,继承自BaseServer
  • ForkingMixIn:Mix-in class to handle each request in a new process.
  • ThreadingMixIn:Mix-in class to handle each request in a new thread.

Python自带了TCPServerUDPServer这两类网络服务,而要同时服务多个用户就必须应用多过程或多线程模型,这两种模型由ForkingMixInThreadingMixIn提供。通过组合,就能够失去以上四个类。

这几个类之间的关系如下图:

能够看到,从BaseServer开始逐层继承的过程中,混入(MixIn)了ForkingMixIn类和ThreadingMixIn类。

这样的多重继承的技巧称为MixIn。

如果不采纳MixIn技术,而是采纳档次简单的单继承实现,则类的数量会呈指数增长。

具体不采纳MixIn技术设计的继承档次关系参见:廖雪峰-多重继承与MixIn中的Animal类的设计思路。

MixIn总结

MixIn其实也是一种组合的形式。通常来说,组合优于继承

Mixin 类的限度

  • Mixin类不应该有初始化办法
  • Mixin类通常不能独立工作
  • Mixin类的先人也应该是Mixin类

通常状况下,Mixin类总在继承列表的第一位


记得帮我点赞哦!

精心整顿了计算机各个方向的从入门、进阶、实战的视频课程和电子书,依照目录正当分类,总能找到你须要的学习材料,还在等什么?快去关注下载吧!!!

朝思暮想,必有回响,小伙伴们帮我点个赞吧,非常感谢。

我是职场亮哥,YY高级软件工程师、四年工作教训,回绝咸鱼争当龙头的斜杠程序员。

听我说,提高多,程序人生一把梭

如果有幸能帮到你,请帮我点个【赞】,给个关注,如果能顺带评论给个激励,将不胜感激。

职场亮哥文章列表:更多文章

自己所有文章、答复都与版权保护平台有单干,著作权归职场亮哥所有,未经受权,转载必究!