类
封装,继承,多态,动态生成类
封装
python类中的封装特性是通过命名来实现的
- private 用双下划线开头,表示变量或者函数只在当前类中可见
- protect 用单下划线开头,表示变量或者函数只在当前类以及其子类中可见
- public 不以下划线开头,表示变量或者函数可以在任意类中使用
继承
-
继承多个类
class myClass(cls1,cls2): pass
-
super机制
-
super() 函数用于调用下一个父类(超类)并返回该父类实例的方法。
-
直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。总之前人留下的经验就是:保持一致性。要不全部用类名调用父类,要不就全部用 super,不要一半一半。
super(type[, object-or-type])type指的是子类,object-or-type一般是self
-
经典类和新式类的方法解析顺序(MRO,Method Resolution Order)
-
经典类,类自身或者其 Python 2.x中默认都是经典类,只有显式继承了object才是新式类,但是在Python 3.x中按照如下写法,仍然会被认为是新式类
class A: pass
-
新式类,类自身或者其父类继承了object则为新式类。 Python 3.x中默认都是新式类,不必显式的继承object
class A(object): pass
-
经典类解析原则
从左至右的深度优先遍历,但是如果遍历中出现重复的类,保留第一个
举例:
class G:attr = 1 class P1(G): pass class P2(G): attr = 2 class D(P1,P2): pass # attr的顺序为[D,P1,G,P2,G] # 因为重复的类只保留第一个,所以最终的解析顺序为[D,P1,G,P2] print(D.attr)
- 新式类解析原则
新式类和经典类一样都是从左至右的深度优先遍历,但是如果遍历中出现重复的类,只保留最后一个
举例:
class G1(object): attr = 1 class G2(object): attr = 3 class P1(G1,G2): pass class P2(G1,G2): attr = 2 class D(P1,P2): pass # attr顺序为[D,P1,G1,G2,P2,G1,G2] # 因为重复的类只保留最后一个,所以最终解析顺序为[D,P1,P2,G1,G2] print(D.attr) # 输出为2
- 新式类多重继承容易出错的位置
如果C继承的两个类P1和P2都继承了相同的类,但是P1和P2的MRO顺序不一样的话,会报错- 类的示例图
(object) / \ G1 G2 P1(G1,G2) P2(G2,G1) \ / C(P1,P2)
- 实例
class G1(object): attr = 1 class G2(object): attr = 3 class P1(G1,G2): pass class P2(G2,1): attr = 2 class D(P1,P2): pass #P1的MRO顺序为[P1,G1,G2] #P2的MRO顺序为[P2,G2,G1] print(D.attr) # 输出报错
- 报错信息:
Traceback (most recent call last): File "mro.py", line 4, in <module> class P2(G2,1): attr = 2 TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
-
-
__new__函数和__init__函数
- __new__函数是实例化类的时候调用的,为__init__函数提供类对象,也就是__init__函数的第一个参数,所以_new__函数必须返回类对象,通常可以用super(A,cls)._new(cls)获取
- __init__函数是初始化对象的时候调用的,其中的self参数即是__new__函数返回的。
多态
-
类具有继承关系,并且子类类型可以向上转型看做父类类型,如果父类中有这种方法,只要传入相应参数就可以使用。
-
开闭原则
- 对扩展开放(Open for extension):允许子类重写方法函数
- 对修改封闭(Closed for modification):不重写,直接继承父类方法函数
-
__call__函数
- 作用是把类的实例当作函数进行调用
- 举例
class Fib(object): def __call__(self,num): return num f = Fib() # 此处f是类的实例 print f(10) # 此处把类的实例当成函数在用,实际上进行处理的就是__call__函数
动态生成类
-
类对象的类型,python中一切都是对象,类也是对象,如果是类,则为type。如果是对象,其类型的类型也还是type
>>> class MyCls(object):pass ... >>> MyCls.__class__ <class 'type'> >>> a=379 >>> a.__class__ <class 'int'> >>> a.__class__.__class__ <class 'type'> >>> a.__class__.__class__.__class__ <class 'type'>
-
type 函数,动态生成类
type(类名, 父类的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))
举例
>>> class pClass(object):pass ... >>> child=type('ChildClass',(pClass,),{}) >>> child.__name__ 'ChildClass' >>> child.__class__ <class 'type'>
-
使用metaclass,生成动态类
- metaclass 作用
- metaclass的调用顺序
类Foo中有metaclass这个属性吗?如果是,Python会在内存中通过metaclass创建一个名字为Foo的***类对象***(我说的是类对象)。如果没有找到metaclass,会在(父类)中继续寻找metaclass属性,并尝试做和前面同样的操作。如果还是找不到metaclass,Python就会用内置的type来创建这个类对象。
- metaclass 使用方法
- 首先自定义metaclass,继承type。
- 在自定义元类中的__new__函数中返回自定义类,也就是type(class_name,class_parents,class_attr)。
- 在想使用元类的类定义中设置metaclass属性为目标元类。
- metaclass实例
class myMetaClass(type): def __new__(metaname, class_name, class_parents, class_attr): print("Hello hello") return super(myMetaClass,metaname).__new__(metaname, class_name, class_parents, class_attr) class Foo(object,metaclass=myMetaClass): pass f = Foo() print(type(Foo))
输出
Hello hello <class '__main__.myMetaClass'>
扩展题
- ORM是如何实现的(代码在连接,by 廖雪峰)
- 描述器
- 动态生成类