类可以被动态声明
通常我们声明类的方式是静态的,如下:
class A:
age = 18
def f(self):
print(1)
调用类(不是实例)的__dict__
属性将会返回一个字典,该字典以键值对的形式存储了类的属性和方法。使用.
调用方法或属性的时候,实际是在__dict__
中寻找。
print(A.__dict__)
# {'__module__': '__main__', 'age': 18, 'f': <function A.f at 0x00000168d5dbcfe0>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
打印类(不是实例)的类型,我们会发现它的类型是type
。
print(type(A))
# <class 'type'>
使用type
可以以动态的方式声明一个类,type
要求三个参数:类名 、父类 和__dict__字典。下面以同态的方式重新声明A类。
def f(self):
print(1)
d = {
"f": f,
"age": 18,
}
A = type("A", (), d)
和静态声明相同,我们可以对其进行实例化。
a = A()
元类什么?
在了解元类之前先介绍类的__new__
方法和__init__
方法。
类在被实例化的过程中,首先会调用__new__
方法,接着会调用__init__
方法:
class A:
def __new__(cls, *args, **kwargs):
print("__new__ method called")
print(f"cls: {cls}")
return super().__new__(cls)
def __init__(self) -> None:
print("__init__ method called")
a = A()
__new__ method called
cls: <class '__main__.A'>
__init__ method called
声明一个类并继承type
,它就能像type
一样声明其它类,继承了type
的类称之为元类。
- 动态声明
class M(type):
def __new__(cls, name, bases, attrs):
print("__new__ is called")
return type.__new__(cls, name, bases, attrs)
A = M('A', (), {})
# 注意这个过程中`__new__`被调用了
# __new__ is called
- 静态声明
class M(type):
def __new__(cls, name, bases, attrs):
print("__new__ is called")
return type.__new__(cls, name, bases, attrs)
class A(metaclass=M):
pass
# 注意这个过程中`__new__`被调用了,不过是隐式调用。
# __new__ is called
元类有什么作用?
元类(metaclass)一般用于解决继承无法解决的问题,比如我想规定一个类中不能有以test_
开头的方法。
class M(type):
def __new__(cls, name, bases, attrs):
for key in attrs.keys():
if key.startswith('test_'):
raise ValueError('test_开头的方法不允许')
return type.__new__(cls, name, bases, attrs)
class A(metaclass=M):
def test_case(self):
pass
我们在元类中定义__new__
, __init__
,__call__
方法,它们被调用的时机是不同的,可以自己测试一下。
from typing import Any
class M(type):
def __new__(cls, name, bases, attrs):
return type.__new__(cls, name, bases, attrs)
def __init__(self, name, bases, attrs):
return type.__init__(self, name, bases, attrs)
def __call__(cls, *args: Any, **kwds: Any) -> Any:
return super().__call__(cls, *args, **kwds)
class A(metaclass=M):
def test_case(self):
pass
a = A()