0%

Python元类

在python官方文档有一句话:

By default, classes are constructed using type(). The class body is executed in a new namespace and the class name is bound locally to the result of type(name, bases, namespace).

这句话的大概意思是说:在默认情况下,类都是由type构建而来。

1
2
3
4
5
6
7
A = type('A', (object,), {})

class A(object):
pass

class A(object, metaclass=type):
pass

以上三种创建类A的方式等价。

那么我们是否可以通过继承type的方式实现自己的mytype?答案是可以:

1
2
class mytype(type):
pass

然后:

1
2
3
4
B = mytype('B', (object,), {})

class BB(object, metaclass=mytype):
pass

我们将mytype改造一下:

1
2
3
4
class mytype(type):
def __init__(cls, name, bases, namespace):
super().__init__(name, bases, namespace)
setattr(cls, 'hello', lambda self: 'hello world!')

然后:

1
2
3
4
5
6
7
8
>>> print(B().hello())
hello world!
>>> print(B.__dict__)
{'__module__': 'test', '__dict__': <attribute '__dict__' of 'B' objects>, '__weakref__': <attribute '__weakref__' of 'B' objects>, '__doc__': None, 'hello': <function mytype.__init__.<locals>.<lambda> at 0x108d74b80>}
>>> print(BB().hello())
hello world!
>>> print(BB.__dict__)
{'__module__': 'test', '__dict__': <attribute '__dict__' of 'BB' objects>, '__weakref__': <attribute '__weakref__' of 'BB' objects>, '__doc__': None, 'hello': <function mytype.__init__.<locals>.<lambda> at 0x108d74d30>}

mytype再次改造:

1
2
3
4
5
class mytype(type):
def __new__(mcls, name, bases, namespace):
cls = super().__new__(mcls, name, bases, namespace)
setattr(cls, 'hello', lambda self: 'hello world!')
return cls

再次执行以下代码,可以发现结果几乎相同:

1
2
3
4
5
6
7
8
>>> print(B().hello())
hello world!
>>> print(B.__dict__)
{'__module__': 'test', '__dict__': <attribute '__dict__' of 'B' objects>, '__weakref__': <attribute '__weakref__' of 'B' objects>, '__doc__': None, 'hello': <function mytype.__init__.<locals>.<lambda> at 0x104c6baf0>}
>>> print(BB().hello())
hello world!
>>> print(BB.__dict__)
{'__module__': 'test', '__dict__': <attribute '__dict__' of 'BB' objects>, '__weakref__': <attribute '__weakref__' of 'BB' objects>, '__doc__': None, 'hello': <function mytype.__init__.<locals>.<lambda> at 0x104c6bca0>}

最后,再提一句,元类的实现上,倾向于使用__new__而不是__init____new____init__的区别,大家可以自行去了解。