1. 普通类
1.1 普通类的执行顺序
class Putong:
def __new__(cls, *args, **kwargs):
print("执行了 __new__ 方法。")
new = super().__new__(cls, *args, **kwargs)
return new # 必须有返回值
def __init__(self):
print("执行了 __init__ 方法。") # 可以不用有返回值
def __call__(self, arg):
print(f"执行了 __call__ 方法,参数为:{arg}") # 可以不用有返回值
p = Putong()
p('hello world')
执行结果为:
执行了 __new__ 方法。
执行了 __init__ 方法。
执行了 __call__ 方法,参数为:hello world
说明普通类实例化的执行顺序是先调用 new,再 init。
且自定义的new方法必须继承父类的new方法,还必须返回一个生成的类,才能调用 init 生成一个实例。
如果再对实例进行调用,就会调用类的 call 方法。
1.2 type与object的关系
print(issubclass(type, object)) # >>> True :说明 object是type的父类。
print(type(object)) # >>> <class 'type'> :说明 object是type创建的。
print(type(type)) # >>> <class 'type'> :说明type是由type自己创建的。
print(type.__bases__) # >>> (<class 'object'>,) :说明type的基类是object
print(object.__bases__) # >>> () : 说明 object没有基类。
说明:object是由type创建的,但object又是type的父类。两者是一个互补的关系,type创建了object时,又继承了自己创建的object,type自身创建了type。
2. 元类
2.1 用type创建类
type既可以查看对象的类型,也可以用来创建对象。
type参数说明:
- 第一个参数:要创建的类的名字,以字符串的形式传入。
- 第二个参数:所继承的父类,如有多个父类,则以元组形式传入。
- 第三个参数:类所关联的属性和方法。以字典的形式传入。
def init(self, name):
self.name = name
@classmethod
def copy(cls, person):
return cls(person.name)
# 创建类,第二个参数如果是继承自 Object的,则元组可以为空元组。 默认继承object,如第10行代码。
Person = type("person", (), {"desc": "有关人的个体的共性描述", "__init__": init, "copy": copy})
# Person = type("person", (object,), {"desc":"有关人的个体的共性描述", "__init__":init})
# 实例化
p = Person("小红")
p1 = Person.copy(p)
print(p1.name)
2.2 自定义元类
元类是比类更抽象的类,用来生成类。
第一步:定义一个元类 和 一个使用元类的类。
#定义元类,需显示的继承 type 类。
class MyMetaClass(type):
# 这里的第一个传入参数为类,所以用 cls。
def __new__(cls, name, base, attrs):
print("调用了元类的__new__方法")
print(f"name={name},base={base},attrs={attrs}。")
print(f"cls={cls}, type(cls)={type(cls)}")
# 因为__new__在父类type中是特殊的静态方法,所以继承时需要填入所有参数,不能省略cls。
t = super(MyMetaClass, cls).__new__(cls, name, base, attrs)
print(f"t={t}, type(t)={type(t)}")
return t
def __init__(cls, *args, **kwargs):
print("调用了元类的 __init__ 方法。")
print(f"__init__方法中的 cls为:{cls}")
# 指定元类创建类
class A(metaclass=MyMetaClass): # 默认为 metaclass=type
pass
此时不创建A的实例,直接运行代码,结果为:
5 行: 调用了元类的__new__方法
6 行: name=A, base=(), attrs={'__module__': '__main__', '__qualname__': 'A'}。
7 行: cls=<class '__main__.MyMetaClass'>, type(cls)=<class 'type'>
10 行: t=<class '__main__.A'>, type(t)=<class '__main__.MyMetaClass'>
14 行: 调用了元类的 __init__ 方法。
15 行: __init__方法中的 cls为:<class '__main__.A'>
说明只要定义了一个 A 类,就会调用 元类的 __new__( ) 和 __init__( ) 方法,
既如果再定义一个 B 类,那么就还会调用一次与B类相关的元类 __new__( ) 和 __init__( )方法。
def __new__(cls, name, base, attrs) 中参数说明:
- name:类名,等同于上面type中的第一个参数。
- base:继承的类,等同于上面type中的第二个参数。上面的A类因为没有继承任何类,所有base为空元组。
- attrs:属性,等同于上面type中的第三个参数。
下面单独看下定义的 A 类内容:
print(f"A={A}, type(A)={type(A)}")
输出结果为:
A=<class '__main__.A'>, type(A)=<class '__main__.MyMetaClass'>
说明 类 A 就是元类 t = super().new(self, name, base, attrs) 的 t 。
第二步:实例化 A 类
实例化 A 类,就是对 A 的调用,既 A()。
class MyMetaClass(type):
def __new__(cls, name, base, attrs):
print("调用了元类的__new__方法")
print(f"name={name},base={base},attrs={attrs}。")
print(f"self={cls}, type(self)={type(cls)}")
# 因为__new__在父类type中是特殊的静态方法,所以继承时需要填入所有参数,不能省略cls。
t = super(MyMetaClass, cls).__new__(cls, name, base, attrs)
print(f"t={t}, type(t)={type(t)}")
return t # 必须有返回才能调用 __call__ 方法。
def __init__(cls, *args, **kwargs): # >>> 这里的第一个传入参数为类,所以用 cls。
print("调用了元类的 __init__ 方法。")
print(f"__init__方法中的 cls为:{cls}")
def __call__(cls, *args, **kwargs):
print("调用了元类的__call__方法。")
print(f"call中的self为:{cls}") # >>> 这里的第一个传入参数为类,所以用 cls。
return super(MyMetaClass, self).__call__(*args, **kwargs) # 元类的call必须有返回值。
class A(metaclass=MyMetaClass): # 注意,这是使用MyMetaClass为元类,而不是继承它。继承的是 object。
def __call__(self, *args, **kwargs):
print("调用了A类的__call__方法")
print(f"A 类中call中的self为:{self}") # >>> 这里的第一个传入参数为实例,所以用 self。
a = A() # 实例化A类。A()会调用 元类 的 __call__() 方法。
对A加括号进行调用既A() ,就会生成一个实例。因为A()会调用元类的__call__() 方法。既创建实例,是通过元类的__call__( )方法创建的,_call_( )方法return的东西,就是 A()生成的东西。
如果再对a进行调用 a()
,则会调用自己实例的 __call__()
方法,既会输出 >>>:调用了A类的__call__方法
。
元类里 __call__( ) 第一个传入参数为类,所以用 __call__(cls)__ 。普通类里的call传入的是 实例,所以用 __call__(self)。
print(f"A={A}, type(A)={type(A)}")
输出为:
>>> A=<class '__main__.A'>, type(A)=<class '__main__.MyMetaClass'>
# 说明 A 是一个类, 说明 A 是通过 MyMetaClass 类创建的。
print(f"a={a}, type(A)={type(a)}")
输出为:
>>> a=<__main__.A object at 0x0000021D58DB1E20>, type(a)=<class '__main__.A'>
# 说明 a 是一个实例, 说明 a 是通过 A 类创建的。
3. 元编程例子
3.1 利用元类设置类的初始属性
# 自定义元类
class MyMetaClass(type):
def __new__(cls, name, base, attrs, *args, **kwargs):
# 创建类时会自动添加 create_time属性。
attrs['create_time'] = datetime.now()
return super().__new__(cls, name, base, attrs, *args, **kwargs)
# 利用元类创建类
class A(metaclass=MyMetaClass):
def __init__(self):
print(f"__init__() 创建了一个A类 : {datetime.now()}")
a = A()
print(f"自动添加的a.create_time属性: {a.create_time}")
运行值为:
__init__() 创建了一个A类 : 2021-05-05 23:01:33.793554
自动添加的a.create_time属性: 2021-05-05 23:01:33.793554
3.2 使用元类创建抽象类
抽象类: 只能被继承,无法实例化对象。
例子1:元类call方法返回None
class NoInstance(type):
def __new__(mcs, *args, **kwargs):
return super().__new__(mcs, *args, **kwargs)
def __call__(cls, *args, **kwargs):
# return super().__call__(*args, **kwargs)
return None
class Abstract(metaclass=NoInstance):
def __str__(self):
return "这是一个抽象类。"
a = Abstract()
print(a, type(a))
运行结果为:
None <class 'NoneType'>
说明,此时的a是None实例,既为元类__call__()方法返回的值。
例子2:元类call方法raise Error
class NoInstance(type):
def __new__(mcs, *args, **kwargs):
return super().__new__(mcs, *args, **kwargs)
def __call__(cls, *args, **kwargs):
# return super().__call__(*args, **kwargs)
raise TypeError("抽象类无法被实例化")
class Abstract(metaclass=NoInstance):
def __str__(self):
return "这是一个抽象类。"
a = Abstract()
此时 实例化 Abstract 就会报错。
例子3:直接调用静态方法和类方法
class NoInstance(type):
def __new__(mcs, *args, **kwargs):
return super().__new__(mcs, *args, **kwargs)
def __call__(cls, *args, **kwargs):
# return super().__call__(*args, **kwargs)
raise TypeError("抽象类无法被实例化")
class Abstract(metaclass=NoInstance):
@staticmethod
def static():
print("调用了静态方法。")
@classmethod
def cls_method(cls):
print("调用了类方法。")
def __str__(self):
return "这是一个抽象类。"
# 虽然不能创建实例,但能直接调用静态方法和类方法。
Abstract.static()
Abstract.cls_method()
class B(Abstract): # 可以被B类继承,此时的B类也无法创建对象。
pass
# B类也能调用静态方法和类方法。
B.static()
B.cls_method()
3.3 final类/终极类
既生成不能被继承的类
内置函数 isinstance 两个作用说明:
- isinstance(a, A): 可以判断实例a是不是由类A生成的。
- isinstance(A, MyMetaclass):判断类A是不是由元类MyMetaclass创建。
利用这个特性,就可以创建一个无法被继承的类。
因为 元类 的 __new__(cls, name, base, attrs) 方法中 base 参数是一个元组,会放入将要创建类的父类。
代码演示:
class MyMetaclass(type):
def __new__(cls, name, base, attrs):
print(name, base)
for i in base:
if isinstance(i, MyMetaclass): # 判断i是否由MyMetaclass创建。
raise TypeError("当前类为终极类,不能被继承。")
return super().__new__(cls, name, base, attrs)
class A(metaclass=MyMetaclass):
pass
class B(A):
pass
上面代码会运行两次元类的 new 方法,
既第一次A类调用new时 base 是空元组,
第二次B类调用new时,base是(<class ‘main.A’>,)
此时只要判断 base 中的元素是不是由 元类创建的,既可以设置是否可以被继承。
3.4 元类创建单例
既该类只能创建一个实例。
class MyMetaclass(type):
def __init__(cls, *args, **kwargs):
cls.__instance = None
super().__init__(*args, **kwargs)
def __call__(cls, *args, **kwargs):
if not cls.__instance:
cls.__instance = super(MyMetaclass, cls).__call__(*args, **kwargs)
return cls.__instance
else:
return cls.__instance
class A(metaclass=MyMetaclass):
pass
a1 = A()
a2 = A()
print(id(a1)) # a1 和 a2 的id地址一样。
print(id(a2))
代码分析:
因为元类的init函数传入的第一个参数是类,而不是实例,所有此处用cls来区别self。
第7行 cls.__instance 判断类的私有属性 __instance 是否为空,如果为空,则 第8行代码 继承type的call方法,生成一个实例,然后把实例赋值给 cls.__instance 属性,再第9行返回该实例。
如果不为空,则第11行代码,直接返回 属性cls.__instance指向的实例。
小技巧,不用元类创建单例:
class A:
__instance = None
def __new__(cls, *args, **kwargs):
if not cls.__instance:
cls.__instance = super(A, cls).__new__(cls, *args, **kwargs)
return cls.__instance
a1 = A()
a2 = A()
print(a1 is a2)
注:上面的元类 new 方法中的 if else 代码有点冗余,也可以这样优化:只需一个if,然后就return。