一、你需要了解下面知识
Python 中的类也是对象(type)
Python 中对象的创建(__call__)
Python 中对象的创建 (__new__ 和 __init__)
1. Python 中的类也是对象(type)
下面验证,可以发现 Python 中的 自定义类、内置类 都属于 type 类型的对象:
(base) liujie@liujie-virtual-machine:~/Workspace/python_work/python_core/meta$ ipython
Python 3.7.4 (default, Aug 13 2019, 20:35:49)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.8.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: class A:
...: pass
...:
In [2]: type(A)
Out[2]: type
In [3]: type(int)
Out[3]: type
In [4]: type(list)
Out[4]: type
In [5]: type(object)
Out[5]: type
In [6]: type(type)
Out[6]: type
In [7]:
可以发现自定义的类 A,内置的 int、list、object 都是 type 类型,type 也是 type 类型的。
可以通过 type 创建类对象(动态创建类):
In [1]: A = type('A', (), {})
In [2]: def greet(msg):
...: print(msg)
...:
In [3]: a = A()
In [4]: a.greet = greet
In [5]: a.greet('hello')
hello
In [6]:
class type(object) 传入一个参数时,返回 object 的类型。
class type(name, bases, dict) 传入三个参数时,返回一个新的 type 对象。
name :类的名称,用字符串表示;
bases :承的父类集合,注意 Python 支持多重继承,如果只有一个父类,别忘了 tuple 的单元素写法 (元素,);
dict :类的属性集合,比如说这里添加 greet(msg) 方法也能写成 A = type('A', (), {‘greet’: greet}),添加一个变量 a 可以写成 A = type('A', (), dict(a=1)) 通过这种方式添加的变量是一个类变量。
2. Python 中对象的创建(__call__)
如果一个类中实现了 __call__ 方法,那么通过这个类创建的对象就是一个可调用的对象:
In [1]: class A:
...: def __call__(self, *args, **kwargs):
...: print('A.__call__: \n\tself={} \n\targs={} \n\tkwargs={}'.format(self, args, kwargs))
...:
In [2]: a = A()
In [3]: a(1, 2, s='hello')
A.__call__:
self=<__main__.a object at>
args=(1, 2)
kwargs={'s': 'hello'}
In [4]:
可以看到在 A 类中实现了 __call__ 方法后,可以直接通过 a(*args, **kwargs) 的方式来调用 __call__ 方法。
这里留个疑惑,创建对象的时候是通过 A() 的方式来返回一个 A 类的对象的,那么对象的创建是否也是通过实现 type 中 __call__ 方法的方式来实现的呢?
3. Python 中对象的创建 (__new__ 和 __init__)
通过 __new__ 方法创建一个对象后,再使用 __init__ 方法初始化对象:
In [1]: class A:
...: def __init__(self, *args, **kwargs):
...: print('A.__init__: \n\tself={} \n\targs={} \n\tkwargs={}'\
...: .format(self, args, kwargs))
...: self.s = 'hello'
...:
...: def __new__(cls, *args, **kwargs):
...: print('A.__new__: \n\tself={} \n\targs={} \n\tkwargs={}'\
...: .format(cls, args, kwargs))
...: return object.__new__(cls, *args, **kwargs)
...:
In [2]: a = A()
A.__new__:
self=
args=()
kwargs={}
A.__init__:
self=<__main__.a object at>
args=()
kwargs={}
In [3]: a.s
Out[3]: 'hello'
In [4]:
可以看到创建对象 a 的过程,是先通过 __new__ 方法返回一个对象,再通过 __init__ 方法来初始化 a 对象的属性 s。
如果 __new__ 中没有返回对象(函数不返回对象时默认返回 None) __init__ 方法是不会被调用的:
In [1]: class A:
...: def __init__(self, *args, **kwargs):
...: print('A.__init__: \n\tself={} \n\targs={} \n\tkwargs={}'\
...: .format(self, args, kwargs))
...: self.s = 'hello'
...:
...: def __new__(cls, *args, **kwargs):
...: print('A.__new__: \n\tself={} \n\targs={} \n\tkwargs={}'\
...: .format(cls, args, kwargs))
...:
In [2]: a = A()
A.__new__:
self=
args=()
kwargs={}
In [3]:
二、Python3 中的 metaclass
1. metaclass 用来指定一个用来创建该类的类对象
先来看一个简单的例子:
# meta01.py
class MetaList(type):
def __new__(cls, name, bases, methods):
print('MetaList.__new__, \n\tcls={} \n\tname={} \
\n\tbases={} \n\tmethods={}'.format(cls, name, bases, methods))
methods['add'] = lambda self, value: self.append(value)
print('MetaList.__new__, \n\tcls={} \n\tname={} \
\n\tbases={} \n\tmethods={}'.format(cls, name, bases, methods))
return return super(MetaList, cls).__new__(cls, name, bases, methods)
class MyList1(list, metaclass=MetaList):
pass
class MyList2(list, metaclass=type):
pass
class MyList3(list):
pass
l1 = MyList1()
l1.add(1)
print(l1)
print('############################')
print('l1.__class__ = {}'.format(l1.__class__))
print('l1.__class__.__class__ = {}'.format(l1.__class__.__class__))
print('l1.__class__.__class__.__class__ = {}'.format(
l1.__class__.__class__.__class__))
print('############################')
l2 = MyList2()
l3 = MyList3()
print('l2.__class__ = {}'.format(l2.__class__))
print('l2.__class__.__class__ = {}'.format(l2.__class__.__class__))
print('l3.__class__ = {}'.format(l3.__class__))
print('l3.__class__.__class__ = {}'.format(l3.__class__.__class__))
运行结果:
(base) liujie@liujie-virtual-machine:~/Workspace/python_work/python_core/meta$ python meta01.py
MetaList.__new__,
cls=
name=MyList1
bases=(,)
methods={'__module__': '__main__', '__qualname__': 'MyList1'}
MetaList.__new__,
cls=
name=MyList1
bases=(,)
methods={'__module__': '__main__', '__qualname__': 'MyList1', 'add': . at 0x7ff5ba8b7c20>}
[1]
############################
l1.__class__ =
l1.__class__.__class__ =
l1.__class__.__class__.__class__ =
############################
l2.__class__ =
l2.__class__.__class__ =
l3.__class__ =
l3.__class__.__class__ =
(base) liujie@liujie-virtual-machine:~/Workspace/python_work/python_core/meta$
通过 一、你需要了解下面知识 中的了解我们知道了,类实际上就是一个 type 类型的对象,对象的创建是在 __new__ 方法中实现的。
type.__new__(cls, name, bases, methods) 中的参数:
cls 当前准备创建的类的对象;
name 类的名字;
bases 类继承的父类集合;
methods 类的方法集合。
MetaList 类中,通过 __new__ 方法创建 MyList1 类对象,其实这个创建过程是通过 type.__new__(cls, name, bases, methods) 来创建的。我们可以在 __new__ 方法中通过 methods[method_name] = method 的方式来给创建的类对象(这里是 MyList1)添加方法,在不改变原类的情况下上给类添加新的功能,有点类似与装饰器(在不改变原有函数的情况下给函数添加新的功能)。
因为 MetaList 是继承自 type 的,所以这里的 super(MetaList, cls).__new__(cls, name, bases, methods) 等价于 type.__new__(cls, name, bases, methods) 。
通过 print 打印 __class__ 参数可知,l1 是 MyList1 类的对象,MyList1 是 MetaList 类的对象,MetaList 是 type 类的对象。MyList2 和 MyList3 都是 type 类的对象。
对象的创建过程:
l1 <== MyList1 <== MetaList <== type
l2 <== MyList2 <== type
l3 <== MyList3 <== type
所以 metaclass 通过指定的类中的 __new__ 方法来创建对象的,当找不到 metaclass 时,默认使用 metaclass=type 来创建类。
当前类中如果没有设置 metaclass,就会使用父类中设置的 metaclass,如果父类中也没设置就是用默认的 type 。
instance.__class__ 表示类实例所属的类。
metaclass 实际上就是给类指定一个创建类的对象,这个对象是一个继承自 type 的类。类都是根据 metaclass 来创建的。
再来看一个例子:
# meta02.py
class MetaList(type):
def __new__(cls, name, bases, methods):
print('MetaList.__new__, \n\tcls={} \n\tname={} \
\n\tbases={} \n\tmethods={}'.format(cls, name, bases, methods))
return super(MetaList, cls).__new__(cls, name, bases, methods)
class MyList1(metaclass=MetaList):
pass
class MyList2(metaclass=type):
pass
class MyList3(MyList1, MyList2):
pass
class MyList4(list, MyList2):
pass
print('#######################################')
l3 = MyList3()
print('l3.__class__ = {}'.format(l3.__class__))
print('l3.__class__.__class__ = {}'.format(l3.__class__.__class__))
print('l3.__class__.__class__.__class__ = {}'
.format(l3.__class__.__class__.__class__))
print('#######################################')
l4 = MyList4()
print('l4.__class__ = {}'.format(l4.__class__))
print('l4.__class__.__class__ = {}'.format(l4.__class__.__class__))
print('l4.__class__.__class__.__class__ = {}'
.format(l4.__class__.__class__.__class__))
运行结果:
(base) liujie@liujie-virtual-machine:~/Workspace/python_work/python_core/meta$ python meta02.py
MetaList.__new__,
cls=
name=MyList1
bases=()
methods={'__module__': '__main__', '__qualname__': 'MyList1'}
MetaList.__new__,
cls=
name=MyList3
bases=(, )
methods={'__module__': '__main__', '__qualname__': 'MyList3'}
#######################################
l3.__class__ =
l3.__class__.__class__ =
l3.__class__.__class__.__class__ =
#######################################
l4.__class__ =
l4.__class__.__class__ =
l4.__class__.__class__.__class__ =
(base) liujie@liujie-virtual-machine:~/Workspace/python_work/python_core/meta$
为什么 MetaList.__new__ 这里执行了两次?
上面设置了 metaclass=MetaList 的类有两个, MyList1 和 MyList3。其中 MyList3 是继承了 MyList1 中的 metaclass,所以也有了 metaclass=MetaList 。在定义 MyList1 和 MyList3 这两个类的时候就会使用 MetaList 来创建类对象,所以这里就打印了两次。
MyList3 因为继承了 MyList1,而 MyList1 中的 metaclass=MetaList 所以创建 l3 对象时的过程是:
l3 <== MyList3 <== MetaList <== type
2. 创建一个对象时,内部是怎么调用的
通过一个例子来进行说明:
# meta03.py
class MetaList(type):
def __init__(cls, name, bases, methods):
print('MetaList.__init__, \n\tcls={} \n\tname={} \
\n\tbases={} \n\tmethods={}'.format(cls, name, bases, methods))
print('######################################')
def __new__(cls, name, bases, methods):
print('MetaList.__new__, \n\tcls={} \n\tname={} \
\n\tbases={} \n\tmethods={}'.format(cls, name, bases, methods))
print('######################################')
return type.__new__(cls, name, bases, methods)
def __call__(cls, *args, **kwargs):
print('MetaList.__call__: \n\tself={} \n\targs={} \n\tkwargs={}'
.format(cls, args, kwargs))
print('######################################')
obj = cls.__new__(cls)
cls.__init__(cls)
return obj
# 创建 MetaList 类对象, MyList 是通过 MetaList.__new__() 返回的一个 MetaList 类型对象
class MyList(list, metaclass=MetaList):
def __init__(self, *args, **kwargs):
print('MyList.__init__, self={} args={} kwargs={}'.format(
self, args, kwargs))
print('######################################')
def __new__(cls, *args, **kwargs):
print('MyList.__new__, cls={} args={} kwargs={}'.format(
cls, args, kwargs))
print('######################################')
return list.__new__(cls)
# MyList() 就相当于是调用 MyList.__call__() 也就是调用 MetaList 里面的 __call__()
mylist = MyList()
运行结果:
(base) liujie@liujie-virtual-machine:~/Workspace/python_work/python_core/meta$ python meta03.py
MetaList.__new__,
cls=
name=MyList
bases=(,)
methods={'__module__': '__main__', '__qualname__': 'MyList', '__init__': , '__new__': }
######################################
MetaList.__init__,
cls=
name=MyList
bases=(,)
methods={'__module__': '__main__', '__qualname__': 'MyList', '__init__': , '__new__': }
######################################
MetaList.__call__:
self=
args=()
kwargs={}
######################################
MyList.__new__, cls= args=() kwargs={}
######################################
MyList.__init__, self= args=() kwargs={}
######################################
(base) liujie@liujie-virtual-machine:~/Workspace/python_work/python_core/meta$
通过上面可以看到,定义然后创建一个类的步骤:
定义类时使用指定的 metaclass 创建类对象,创建这个类对象的步骤和创建普通对象的步骤是一样的,先调用 __new__ 创建对象,再调用 __init__ 初始化这个对象;
然后使用这个类创建对象的时候通过 类名() 的方式,就相当于是使用 类.__call__ 调用类对象的 __call__ 方法;
最后是实例化对象,也是通过先调用 __new__ 创建对象,再调用 __init__ 初始化这个对象,这种方式来创建的。
创建 mylist 对象的步骤:
MetaList ==> __new__ ==> __init__ ==> MyList ==> __new__ ==> __init__ ==> mylist
参考链接
作者:LiuJ的博客
出处:https://www.jianshu.com/p/7b707928feed
本文版权归作者和简书共有,欢迎转载,但未经作者同意必须保留此段声明,否则保留追究法律责任的权利。