类的定义
类的定义的语法
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 = status
door = Door(1001, 'open')
door.number
door.status
- 创建对象应用
类名(__init__ 函数除第一个参数外的参数列表)
- 创建对象的时候理论执行了
__init__
函数 __init__
函数并不会创建对象
函数创立及初始化的过程
- 首先创建对象
- 对象作为 self 参数传递给
__init__
函数 - 返回 self
作用域
类变量
示例代码
In [1]: class A:
...: NAME = 'A' # 类的间接上级作用域 叫做类变量
...: def __init__(self, name):
...: self.name = name # 关联到实例的变量 叫做实例变量
...:
In [2]: a = A('a')
In [3]: a.NAME
Out[3]: 'A'
In [4]: a.name
Out[4]: 'a'
In [5]: A.NAME
Out[5]: 'A'
In [6]: A.name
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-6-61c1cc534250> in <module>()
----> 1 A.name
AttributeError: type object 'A' has no attribute 'name'
In [7]: a2 = A('a2')
In [8]: a2.NAME
Out[8]: 'A'
In [9]: a2.NAME = 'A2' # 给示例 a2 的类变量 NAME 赋值
In [10]: a2.NAME
Out[10]: 'A2'
In [11]: a.NAME
Out[11]: 'A'
In [12]: A.NAME # 类变量没有变动
Out[12]: 'A'
In [13]: a2.xxx = 3
In [14]: a2.xxx # 赋值之后 a2 多了 xxx 属性
Out[14]: 3
In [15]: A.NAME = 'AA' # 间接批改类的类变量
In [16]: A.NAME
Out[16]: 'AA'
In [17]: a.NAME # 对应的实例的类变量也产生了扭转
Out[17]: 'AA'
In [18]: a2.NAME # a2 的类变量在之前的赋值被笼罩了,因而扭转类变量的并不会影响 a2
Out[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.NAME
Out[3]: 'A'
In [4]: a.__class__.NAME
Out[4]: 'A'
In [5]: a.__dict__
Out[5]: {'name': 'a'}
In [6]: a.__class__ # a.__class__示意实例对应的类
Out[6]: __main__.A
In [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__.NAME
Out[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 指向 F
In [4]: F1.NAME
Out[4]: 'F'
In [5]: f1 = F1()
In [6]: f1.NAME
Out[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__.F
In [10]: F.__dict__ # 实质上减少的还是类 F
Out[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.NAME
Out[4]: 'G'
In [5]: class G:
...: pass
...:
In [6]: G = set_name('G')(G) # 装璜器的函数调用办法
In [7]: G.NAME
Out[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.__status
AttributeError: 'Door' object has no attribute '__status'
In [4]: door.__dict__ # door 对象含有的属性_Door__status
Out[4]: {'_Door__status': 'closed', 'number': 1001}
In [5]: door.__status = 'hahaha' # 给对象创立了新的属性,并没有批改到__status
In [6]: door.__status
Out[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__status
Out[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._a
Out[3]: 3
In [4]: a._a = 4
In [5]: a._a
Out[5]: 4
In [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_number
和set_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 # 返回 1001
door.number = 1002
door.number # 返回 1002
del 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):
pass
sub = 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:
pass
class A(object):
pass
class A():
passs
多继承
- 在继承列表里存在多个类的时候示意多继承
- 多继承会把继承列表里的所有私有成员都继承过去
class A:
def method(self):
print('method of A')
class B:
def method(self):
print('method of B')
class C(A, B):
pass
c = 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):
pass
class E(A):
def method(self):
print('method of E')
class F(E, A):
pass
F().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 pass
TypeError: Cannot create a consistent method resolution
order (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 的两个准则:
- 本地优先:本人定义或重写的办法优先,依照继承列表,从左到右查找
- 枯燥性:所有子类,也要满足查找程序
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 的条件,间接抛出异样。
论断:
- 应该尽量避免多继承
- 多继承会对程序的心智累赘造成十分大的压力
Mixin 类
参考
- 廖雪峰 - 多重继承与 MixIn
- 知乎 -Mixin 是什么概念?
- Python Cookbook- 利用 Mixins 扩大类性能
在编程中,mixin 是指为继承自它的 class 提供额定的性能, 但它本身却是不独自应用的类.。在具备多继承能力的编程语言中,mixin 能够为类减少额定性能或办法。
因而,MixIn 模式的目标就是给一个类减少多个性能,这样,在设计类的时候,咱们优先思考通过多重继承来组合多个 MixIn 的性能,而不是设计多层次的简单的继承关系。
在 Python 3.5.2 源代码 socketserver.py 中的 639 到 643 行能够看到以下四个类的定义
class ForkingUDPServer(ForkingMixIn, UDPServer): pass
class ForkingTCPServer(ForkingMixIn, TCPServer): pass
class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
class 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 自带了 TCPServer
和UDPServer
这两类网络服务,而要同时服务多个用户就必须应用多过程或多线程模型,这两种模型由 ForkingMixIn
和ThreadingMixIn
提供。通过组合,就能够失去以上四个类。
这几个类之间的关系如下图:
能够看到,从 BaseServer 开始逐层继承的过程中,混入 (MixIn) 了 ForkingMixIn 类和 ThreadingMixIn 类。
这样的多重继承的技巧称为 MixIn。
如果不采纳 MixIn 技术,而是采纳档次简单的单继承实现,则类的数量会呈指数增长。
具体不采纳 MixIn 技术设计的继承档次关系参见:廖雪峰 - 多重继承与 MixIn 中的 Animal 类的设计思路。
MixIn 总结
MixIn 其实也是一种组合的形式。通常来说,组合优于继承
Mixin 类的限度
- Mixin 类不应该有初始化办法
- Mixin 类通常不能独立工作
- Mixin 类的先人也应该是 Mixin 类
通常状况下,Mixin 类总在继承列表的第一位
记得帮我点赞哦!
精心整顿了计算机各个方向的从入门、进阶、实战的视频课程和电子书,依照目录正当分类,总能找到你须要的学习材料,还在等什么?快去关注下载吧!!!
朝思暮想,必有回响,小伙伴们帮我点个赞吧,非常感谢。
我是职场亮哥,YY 高级软件工程师、四年工作教训,回绝咸鱼争当龙头的斜杠程序员。
听我说,提高多,程序人生一把梭
如果有幸能帮到你,请帮我点个【赞】,给个关注,如果能顺带评论给个激励,将不胜感激。
职场亮哥文章列表:更多文章
自己所有文章、答复都与版权保护平台有单干,著作权归职场亮哥所有,未经受权,转载必究!