Python 所有皆对象,包含类 class 也是对象
家喻户晓 Python 跟 Java 一样,所有皆对象,所以 class 类也是对象,而对象能够动态创建,所以一个 class 类也能够被动态创建进去。
通过 type() 创立类
type 的定义,type 是一个类 class,只不过能够 callable。
class type(name, bases, dict, **kwds)
type 传入一个参数时,返回的是参数的类型。
>>> type(int)
<class 'type'>
>>> type(list)
<class 'type'>
>>> type(T)
<class 'type'>
>>> type(C)
<class 'type'>
>>> type(object)
<class 'type'>
type 传入三个参数时,用来创立类:
第一个参数 name 是被创立的类的名字,str 类型
第二个参数 bases 是被创立的类的父类,tuple 类型,不传默认是 (object,)
第三个参数 dict 是被创立的类的属性和办法,dict 类型
上面两种创立类的形式,后果是一样的
class X:
a = 1
X = type('X', (), dict(a=1))
通过 metaclass 创立类
咱们晓得能够用 class 类来创立 object 对象,而 class 类自身也是对象,那么 class 这个对象由谁来创立呢?答案就是 metaclass,metaclass 是 class 的 class,metaclass 创立 class,class 创立 object,metaclass→class→object:
MyClass = MetaClass()
my_object = MyClass()
a 是对象,对象的类型是 class,class 的类型是 metaclass
>>> a = 1 # 对象
>>> a.__class__ # 对象的类型
<class 'int'> # class
>>> type(a)
<class 'int'>
>>> a.__class__.__class__ # class 的类型
<class 'type'> # metaclass
>>> type(a.__class__)
<class 'type'>
能创立类的类,就是 metaclass 元类,上述的 type 就是一个元类。
Python2 中给一个 class 指定一个创立它的元类:
class Foo(object):
__metaclass__ = something...
[...]
Python3 中语法有变动:
class Foo(object, metaclass=something):
...
# 或
class Foo(object, metaclass=something, kwarg1=value1, kwarg2=value2):
...
metaclass 的存在的意义是动态创建类、拦挡类、批改类
就像动静创建对象一样,你想在哪创立一个对象都能够,同样的你想创立一个自定义的类,而后依据它创立实例。
假如有一个需要是将左右的类的属性,都解决成大写的,演进过程如下:
给 module 指定一个 metaclass 解决办法
# the metaclass will automatically get passed the same argument
# that you usually pass to `type`
def upper_attr(future_class_name, future_class_parents, future_class_attrs):
"""
Return a class object, with the list of its attribute turned
into uppercase.
"""# pick up any attribute that doesn't start with '__' and uppercase it
uppercase_attrs = {attr if attr.startswith("__") else attr.upper(): v
for attr, v in future_class_attrs.items()}
# let `type` do the class creation
return type(future_class_name, future_class_parents, uppercase_attrs)
__metaclass__ = upper_attr # this will affect all classes in the module
class Foo(): # global __metaclass__ won't work with"object" though
# but we can define __metaclass__ here instead to affect only this class
# and this will work with "object" children
bar = 'bip'
自定义一个元类,留神元类是用来创立类的,所以必须继承自 type
# remember that `type` is actually a class like `str` and `int`
# so you can inherit from it
class UpperAttrMetaclass(type):
# __new__ is the method called before __init__
# it's the method that creates the object and returns it
# while __init__ just initializes the object passed as parameter
# you rarely use __new__, except when you want to control how the object
# is created.
# here the created object is the class, and we want to customize it
# so we override __new__
# you can do some stuff in __init__ too if you wish
# some advanced use involves overriding __call__ as well, but we won't
# see this
def __new__(upperattr_metaclass, future_class_name,
future_class_parents, future_class_attrs):
uppercase_attrs = {attr if attr.startswith("__") else attr.upper(): v
for attr, v in future_class_attrs.items()}
return type(future_class_name, future_class_parents, uppercase_attrs)
简写一下:
class UpperAttrMetaclass(type):
def __new__(cls, clsname, bases, attrs):
uppercase_attrs = {attr if attr.startswith("__") else attr.upper(): v
for attr, v in attrs.items()}
return type(clsname, bases, uppercase_attrs)
留神到最初一行,调用的是 type,这还不算 OOP,因为并没有调用父类,所以最好改成:
class UpperAttrMetaclass(type):
def __new__(cls, clsname, bases, attrs):
uppercase_attrs = {attr if attr.startswith("__") else attr.upper(): v
for attr, v in attrs.items()}
return type.__new__(cls, clsname, bases, uppercase_attrs)
或者能够改成应用 super:
class UpperAttrMetaclass(type):
def __new__(cls, clsname, bases, attrs):
uppercase_attrs = {attr if attr.startswith("__") else attr.upper(): v
for attr, v in attrs.items()}
return super(UpperAttrMetaclass, cls).__new__(cls, clsname, bases, uppercase_attrs)
分层思维的体现
metaclass 能够做的事:
- intercept a class creation
- modify the class
- return the modified class
应用层想用类创立实例,而后应用实例。而至于类是怎么来的,应用层并不关怀,创立类这一步就交给元类解决,而在元类这一层中做批改,对下层利用来说是通明的。
metaclass 理论利用场景
最多的是用在定义 API 方面,这个 API 不是广义的利用接口,而是更宽泛意义的接口、协定,相似一种转换器的概念,API 给利用提供了简略的应用接口,而把简单的解决转换暗藏在 metaclass 外部,通过解决的后果最终输入到另一个零碎中。典型的例子就是 Django 中的 ORM:
class Person(models.Model):
name = models.CharField(max_length=30)
age = models.IntegerField()
person = Person(name='bob', age='35')
print(person.age)
当打印 person.age 时,并不是返回 IntegerField 对象,而是返回一个 int 值,而且还能够应用这个对象写入到 db 中,这一些都是因为 models.Model 背地的 metaclass,其讲简单的类型转换等操作暗藏在外部,而给业务提供了一个非常简洁的应用接口。
另外就是须要动静生成类的中央,例如写一个 CacheMeta,能够给各种未知的类加缓存,具体给哪些类加缓存,对于这个元类来说是未知的,所以须要在运行过程中动静生成,此时就能够应用元类的形式。
别忘了批改类其实还能够应用装璜器
装璜器的思路也是分层,在类应用之前,给类套一层外壳。
type 是本人的对象,也是本人的元类
Python 所有皆对象,要么是 class 的对象,要么是 metaclass 的对象,而只有 type 例外。
type 是 type 本人的对象,type 也是 type 本人的 metaclass,这没方法在纯 Python 中实现,而是在语言实现层面做的非凡解决实现的。
type.__new__ type.__init__ 和 type.__call__ 关系
-
type.__new__:
- 是在元类中,依据入参,创立出一般类,即 从类语法定义生成类实体
- metaclass 调用 new 创立 class,就像 class 调用 new 创立 object 一样
-
type.__init__:
- 在 new 实现后,即依据语法定义创立出类之后调用,给该类做初始化操作
- 不产生什么返回值
- metaclass 调用 init 初始化 class,就像 class 调用 init 初始化 object 一样
-
type.__call__:
- 依据一个实体存在的类,创立一个该类的对象
- 在一个具体对象 object 上间接调用
对象名()
,会执行 class 中定义的 call__,同理在 class 上间接调用 class(),会执行 metaclass 中定义的 __call
class CachedMate(type):
"""作用跟 type 一样,就是用来创立 class 的"""
def __new__(mcs, clsname, bases, attrs):
"""
type 的 __new__ 办法作用是依据 类名 clsname、依赖 bases、属性 attrs 这些字面量来创立出一个类
就像一般 class 中定义的 __new__ 办法作用是创立一个 object
metaclass 中定义的 __new__ 办法作用是创立一个 class
返回的 new_class 就是被创立进去的类
"""print(f'CachedMate __new__ start {clsname} || {bases} || {attrs}')
new_class = super().__new__(mcs, clsname, bases, attrs)
print(f'CachedMate __new__ gen class {new_class} || {type(new_class)} || {type(type(new_class))}')
print(f'CachedMate __new__ end')
print('======')
return new_class
def __init__(cls, clsname, bases, attrs):
"""
给创立进去的 class 做一些初始化操作
就像一般 class 中定义的 __init__ 办法作用是给创立的 object 初始化
metaclass 中定义的 __init__ 办法作用是给创立的 class 初始化
"""print(f'CachedMate __init__ start {clsname} {bases} {attrs}')
obj = super().__init__(clsname, bases, attrs)
print(f'CachedMate __init__ gen obj {obj} {type(obj)} {type(type(obj))}')
print(f'CachedMate __init__ end')
print('======')
# self.__cache = weakref.WeakValueDictionary()
cls._cache = {}
def __call__(cls, *args, **kwargs):
"""
持续类比一般 class 中的场景
对象 object 以调用形式呈现时,就是在调用 class 中定义的 __call__,如 object()
而类 class 以调用形式呈现时,就是在调用 metaclass 中定义的 __call__,如 class()
这里就是当 Spam()时,也就是实例化 Spam 时就会调用
这也就是为什么单例模式能够放在这里做的起因,指标类实例化时,必然会调用 __call__ 所以固定返回同一个实例,即实现单例
或者说,想要管制一个类的创立过程,都能够在这里坐解决
"""print(f'CachedMate __call__ start', args, kwargs)
if args in cls._cache:
print('CachedMate __call__ cached')
return cls._cache[args]
else:
print('CachedMate __call__ before super().__call__')
obj = super().__call__(*args, **kwargs)
print('CachedMate __call__ after super().__call__', obj, type(obj))
cls._cache[args] = obj
return obj
# Example
class Spam(metaclass=CachedMate):
def __new__(cls, *args, **kwargs):
print('Spam __new__', args, kwargs)
return super(Spam, cls).__new__(cls)
def __init__(self, *args, **kwargs):
print('Spam __init__', args, kwargs)
self.args = args
self.kwargs = kwargs
def __call__(self, *args, **kwargs):
# 在 Spam 实例上应用 spam() 才会调用这里
print('Spam __call__', args, kwargs)
# 后果中 Spam 实例化参数不变,失去的就是缓存的后果,参数变了则是新接口
print(11111, Spam._cache)
# 11111 {} 一开始没有缓存
sp = Spam(1,2,'test1', 'test2', name='test_name')
print(22222, Spam._cache)
# 22222 {(1, 2, 'test1', 'test2'): <__main__.Spam object at 0x10b71b160>}
# 有了一个缓存
sp2 = Spam(1,2,'test1', 'test2', name='test_name')
print(33333, Spam._cache)
# 33333 {(1, 2, 'test1', 'test2'): <__main__.Spam object at 0x10b71b160>}
# 因为参数一样,所以读的缓存
sp3 = Spam(1,2,'test1', 'test3', name='test_name3')
print(44444, Spam._cache)
# 44444 {(1, 2, 'test1', 'test2'): <__main__.Spam object at 0x10b71b160>, (1, 2, 'test1', 'test3'): <__main__.Spam object at 0x10b71b250>}
# 参数变了,从新生成了新的缓存,独立开来
本文由 mdnice 多平台公布