在 Python 中,我们经常使用 type 来查看对象的类型,如下:
def test01(): number = 100 print(type(number)) if __name__ == '__main__': test03()
type 除此用法之外,还可以动态创建类。所谓动态创建类指的是指在运行时通过编程方式创建新的类,而不是在源代码中静态定义类。
1. type 类的动态构建
我们创建的静态类如下:
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 实例对象。
2. 自定义 type 类
我们先了解下 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 动态绑定静态方法
3. metaclass
当定义类时,通过 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