python3 class写法_Python3 中的 metaclass

一、你需要了解下面知识

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

本文版权归作者和简书共有,欢迎转载,但未经作者同意必须保留此段声明,否则保留追究法律责任的权利。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值