在 Python 中,我们经常使用 type 来查看对象的类型,如下:
def test01():
number = 100
print(type(number))
if __name__ == '__main__':
test03()
type 除此用法之外,还可以动态创建类。所谓动态创建类指的是指在运行时通过编程方式创建新的类,而不是在源代码中静态定义类。
- type 类的动态构建 {#title-0} =========================
我们创建的静态类如下:
class ClassName:
class_attr = 100
def __init__(self, number):
self.number = number
def instance_method(self):
print('hello instance method', self.number)
@classmethod
def class_method(cls):
print('hello class method', cls.class_attr)
@staticmethod
def static_method():
print('hello static method')
如果,我们使用编程的方式动态创建这样的类,可以使用 type 来实现。首先,初始化 type 类型对象。然后通过 type 对象创建实例对象。type 类的 init 方法参数如下:
# name: 字符串,类名
# bases:元祖,基类
# dict: 字典,类属性和方法
type.__init__(name, bases, dict)
def test():
def instance_init(self, number):
print('hello __init__ method')
self.number = number
ClassName = type('ClassName', (), {
# 绑定类属性
'class_attr': 100,
# 绑定实例方法
'instance_method': lambda self: print('hello instance method', self.number),
# 绑定魔术方法
'__init__': instance_init,
# 绑定类方法
'class_method': classmethod(lambda cls: print('hello class method', cls.class_attr)),
# 绑定静态方法
'static_method': staticmethod(lambda : print('hello static method'))
})
# 调用类方法
ClassName.class_method()
# 调用静态方法
ClassName.static_method()
# 调用实例方法
object = ClassName(200)
object.instance_method()
if __name__ == '__main__':
test()
hello class method 100
hello static method
hello __init__ method
hello instance method 200
从这个例子可以看出,所有的 Python 类对象都是由 type 创建出来,再由各个 Python 类对象创建各种各样的实例对象。上例中:首先由 type 动态创建出 ClassName 类,然后由 ClassName 类创建 object 实例对象。
- 自定义 type 类 {#title-1} ========================
我们先了解下 type 动态创建实例对象的过程:
- 首先,调用 type 类的 new 方法创建自定义类对象;
- 然后,调用 type 类的 init 方法对类对象进行初始化;
- 此时,得到自定义类对象,即:得到类型;
- 接着,调用 type 类的 call 方法创建实例对象;
- 最后,调用自定义类对象的 init 方法初始化实例化对象;
- 此时,完成动态类的实例对象的创建。
下面自定义 Meta 类并继承 type 类。当创建类对象(调用 new 方法)时,我们通过 attrs 字典参数给类对象绑定相应的成员:
- 类属性 class_attr
- 实例方法 instance_function
- 类方法 class_function
- 静态方法 static_function
class Meta(type):
def __new__(cls, cname, bases, attrs):
# 绑定类属性
attrs['class_attr'] = 100
# 绑定实例方法
attrs['instance_function'] = lambda self: print('Meta 动态绑定实例方法')
# 绑定类方法
attrs['class_function'] = classmethod(lambda self: print('Meta 动态绑定实例方法'))
# 绑定静态方法
attrs['static_function'] = staticmethod(lambda : print('Meta 动态绑定静态方法'))
# 调用父类 type.__new__ 方法创建类对象
class_object = super().__new__(cls, cname, bases, attrs)
print('Meta new ', cls, class_object, cname, bases, attrs)
通过 init 方法对创建好的类对象进行初始化。这个函数我们仅仅是调用父类 type 的 init 方法,我们自己并没有什么需要对类对象进行操作的行为。
class Meta(type):
# 初始化类对象
def __init__(cls, cname, bases, attrs):
super().__init__(cname, bases, attrs)
print('Meta init', cls, cname, bases, attrs)
接下来,调用调用 Meta 的 new 、init 方法构建相应的类对象。除此之外,我们也手动给两个不同的类对象分别 param1、param2 类属性,以及 init 初始化方法。
def test():
Object1 = Meta('Object1', (), {
'param1': 100,
'__init__': lambda self, x, y: print('object1 init', x, y),
})
Object2 = Meta('Object2', (), {
'param2': 200,
'__init__': lambda self, x, y: print('object2 init', x, y),
})
接下来,使用 Object1、Object2 类创建实例对象,注意:Object1、Object2 两个类对象是由 Meta 创建出来的。当我们使用这个类创建实例对象时候,如下代码所示:
def test():
o1 = Object1(10, 20)
o1.instance_function()
o1.class_function()
o1.static_function()
o2 = Object2(30, 40)
o2.instance_function()
o2.class_function()
o2.static_function()
此时,就会自动调用 Meta 的 call 方法来创建具体的实例对象,然后调用实例对象的 init 方法完成初始化,call 方法如下:
class Meta(type):
# 创建实例对象
def __call__(cls, *args, **kwargs):
# 当使用类对象创建实例属性时,传递的参数会传递到 call 方法中
print('Meta call', args, kwargs)
# 调用父类 type.__call__ 方法创建实例对象
instance = super().__call__(*args, **kwargs)
return instance
下面是完成的案例代码:
class Meta(type):
def __new__(cls, cname, bases, attrs):
# 绑定类属性
attrs['class_attr'] = 100
# 绑定实例方法
attrs['instance_function'] = lambda self: print('Meta 动态绑定实例方法')
# 绑定类方法
attrs['class_function'] = classmethod(lambda self: print('Meta 动态绑定实例方法'))
# 绑定静态方法
attrs['static_function'] = staticmethod(lambda : print('Meta 动态绑定静态方法'))
# 调用父类 type.__new__ 方法创建类对象
class_object = super().__new__(cls, cname, bases, attrs)
print('Meta new ', cls, class_object, cname, bases, attrs)
return class_object
# 初始化类对象
def __init__(cls, cname, bases, attrs):
super().__init__(cname, bases, attrs)
print('Meta init', cls, cname, bases, attrs)
# 创建实例对象
def __call__(cls, *args, **kwargs):
# 当使用类对象创建实例属性时,传递的参数会传递到 call 方法中
print('Meta call', args, kwargs)
# 调用父类 type.__call__ 方法创建实例对象
instance = super().__call__(*args, **kwargs)
return instance
def test():
Object1 = Meta('Object1', (), {
'param1': 100,
'param2': 200,
# '__init__': lambda self: print('object1 init'),
'__init__': lambda self, x, y: print('object1 init', x, y),
})
Object2 = Meta('Object2', (), {
'param1': 100,
'param2': 200,
# '__init__': lambda self,: print('object2 init'),
'__init__': lambda self, x, y: print('object2 init', x, y),
})
o1 = Object1(10, 20)
o1.instance_function()
o1.class_function()
o1.static_function()
o2 = Object2(30, 40)
o2.instance_function()
o2.class_function()
o2.static_function()
if __name__ == '__main__':
test()
执行结果:
Meta new <class '__main__.Meta'> <class '__main__.Object1'> Object1 () {'param1': 100, 'param2': 200, '__init__': <function test.<locals>.<lambda> at 0x0000029BB6157AF0>, 'class_attr': 100, 'instance_function': <function Meta.__new__.<locals>.<lambda> at 0x0000029BB6157940>, 'class_function': <classmethod object at 0x0000029BB61479D0>, 'static_function': <staticmethod object at 0x0000029BB616CE50>}
Meta init <class '__main__.Object1'> Object1 () {'param1': 100, 'param2': 200, '__init__': <function test.<locals>.<lambda> at 0x0000029BB6157AF0>, 'class_attr': 100, 'instance_function': <function Meta.__new__.<locals>.<lambda> at 0x0000029BB6157940>, 'class_function': <classmethod object at 0x0000029BB61479D0>, 'static_function': <staticmethod object at 0x0000029BB616CE50>}
Meta new <class '__main__.Meta'> <class '__main__.Object2'> Object2 () {'param1': 100, 'param2': 200, '__init__': <function test.<locals>.<lambda> at 0x0000029BB63A33A0>, 'class_attr': 100, 'instance_function': <function Meta.__new__.<locals>.<lambda> at 0x0000029BB63A3430>, 'class_function': <classmethod object at 0x0000029BB631D580>, 'static_function': <staticmethod object at 0x0000029BB631DA00>}
Meta init <class '__main__.Object2'> Object2 () {'param1': 100, 'param2': 200, '__init__': <function test.<locals>.<lambda> at 0x0000029BB63A33A0>, 'class_attr': 100, 'instance_function': <function Meta.__new__.<locals>.<lambda> at 0x0000029BB63A3430>, 'class_function': <classmethod object at 0x0000029BB631D580>, 'static_function': <staticmethod object at 0x0000029BB631DA00>}
Meta call (10, 20) {}
object1 init 10 20
Meta 动态绑定实例方法
Meta 动态绑定实例方法
Meta 动态绑定静态方法
Meta call (30, 40) {}
object2 init 30 40
Meta 动态绑定实例方法
Meta 动态绑定实例方法
Meta 动态绑定静态方法
- metaclass {#title-2} =======================
当定义类时,通过 metaclass 可以指定类对象的创建过程,这个过程定义可以是一个函数、或者带 call 方法的可调用对象。Python 在实例化对象之前,会通过这个函数或者可调用对象先把对应的类对象创建完成。
然后再调用类对象的 new、init 方法来完成最终的实例对象创建。这里需要注意的是,实例对象是由 metaclass 的 call 方法来创建的,但是 new 方法可以影响到最终创建的实例对象。在 new 方法中可以返回任意类型的实例对象,这个返回的对象会传入到 init 方法中进行初始化。
# 1. 对象创建过程
def create_class_object(cname, bases, attrs):
print('create_class_object')
return type(cname, bases, attrs)
class Meta(type):
def __new__(cls, cname, bases, attrs):
print('Meta new')
return super().__new__(cls, cname, bases, attrs)
def __init__(cls, cname, bases, attrs):
print('Meta init')
super().__init__(cname, bases, attrs)
def __call__(cls, *args, **kwargs):
print('Meta call')
return super().__call__(*args, **kwargs)
# 使用 create_class_object 函数构建 MyClass1 类对象
class MyClass1(object, metaclass=create_class_object):
class_param = 100
def __new__(cls):
print('MyClass1 new ')
return super().__new__(cls)
def __init__(self, *args, **kwargs):
print('MyClass1 init')
# 使用 Meta 类来构建 MyClass2 类对象
class MyClass2(object, metaclass=Meta):
class_param = 100
def __new__(cls):
print('MyClass2 new ')
return super().__new__(cls)
def __init__(self, *args, **kwargs):
print('MyClass2 init')
def test():
print('*' * 15)
# 调用 MyClass1 的 new、init 方法创建对象
object1 = MyClass1()
print('-' * 15)
# 调用 MyClass1 的 call、new、init 方法创建对象
object2 = MyClass2()
if __name__ == '__main__':
test()
create_class_object
Meta new
Meta init
***************
MyClass1 new
MyClass1 init
---------------
Meta call
MyClass2 new
MyClass2 init