在理解元类之前,我们需要再理解一下Python中的类。在Python中,一切皆为对象,我是指所有的东西——都是对象,它们要么是类的实例,要么是元类的实例。这包括整数、字符串、函数以及类。它们全部都是对象,而且它们都是从一个类或元类创建而来。类可以实例化即创建实例对象,我们可以对实例进行赋值。同样的,类本身也是对象,即类对象。元类即是批量创建类的工厂。
-
类也是对象
在Python中,类也是一种对象。只要使用关键字class,Python解释器在执行的时候就会创建一个类对象。我们可以对类赋值,可以增加属性,可以将它作为函数参数进行传递。例子如下:
class DemoClass(object):
pass
def foo(obj):
print(obj)
foo(DemoClass)
##输出:
<class '__main__.DemoClass'>
-
动态的创建类
因为类也是对象,我们可以使用关键字class动态的创建类
def named_class(obj_name):
if obj_name == "Demo":
class Demo(object):
def data(self):
print("demo")
return Demo
elif obj_name == "Test":
class Test(object):
def data(self):
print("Test")
return Test
cls_name = named_class("Demo")
print(cls_name) # 返回的是类,不是类的实例
print(cls_name()) ##可以通过这个类创建类实例
##输出:
<class '__main__.named_class.<locals>.Demo'>
<__main__.named_class.<locals>.Demo object at 0x1043102b0>
但这还不够动态,因为仍然需要自己编写整个类的代码。
通过一个参数调用 type
时,会生成现有类的 type
信息。通过三个参数调用 type
时,会创建一个新的类对象。调用 type
时,参数是类名、基类列表以及指定类的名称空间的字典(所有字段和方法)。
type能动态的创建类。type可以接受一个类的描述作为参数,然后返回一个类。使用type定义类格式:
type(类名, 父类的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))
举个例子:
class TypedClass(object):
test_attr = "test"
def test(self):
print("hello")
#也可以这样定义
def test():
print("hello")
TypedClass = type('TypedClass', (object,), {"test": test}) # 函数作为类的属性
print(TypedClass) ##类对象
print(TypedClass.__dict__)
print(TypedClass().test)
##输出
<class '__main__.TypedClass'>
{'test': <function test at 0x101c62e18>, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'TypedClass' objects>, '__weakref__': <attribute '__weakref__' of 'TypedClass' objects>, '__doc__': None}
<bound method test of <__main__.TypedClass object at 0x103c1c550>>
可以看到,在Python中,类也是对象,你可以动态的创建类。这就是当你使用关键字class时Python在幕后做的事情,而这就是通过元类来实现的。
-
元类
元类是什么
通过上文的描述,我们知道了Python中的类也是对象。元类就是用来创建这些类(对象)的,元类就是类的类,你可以这样理解为:
MyClass = MetaClass() #元类创建
MyObject = MyClass() #类创建实例
实际上MyClass就是通过type()来创创建出MyClass类,它是type()类的一个实例;同时MyClass本身也是类,也可以创建出自己的实例,这里就是MyObject
函数type实际上是一个元类。type就是Python在背后用来创建所有类的元类。
元类的用处:
为了当创建类时能够自动地改变类。Python中可以使用元类控制类的创建过程,类可以看作是元类创建出来的实例。我们可以通过定义元类,开控制子类的创建,例如验证子类的定义是否合法,检查子类是否实现了某些特定的方法等。
就元类本身而言,它们其实是很简单的:
1) 拦截类的创建
2) 修改类
3) 返回修改之后的类
元类主要适用于以下场合:
- 值的域限制
- 隐式转换自定义类的值(您可能希望向用户隐藏编写类的所有这些复杂方面)
- 执行不同的命名约定和样式准则(比如,“每种方法都应有一个文档字符串”)
- 向类添加新的属性
在类定义本身中定义所有这种逻辑时使用元类,主要原因就是为了避免在整个代码库中出现重复代码。
元类的作用域:
当前类中如果有__metaclass__这个属性吗,Python会在内存中通过__metaclass__创建一个类对象。如果Python没有找到__metaclass__,它会继续在(父类)中寻找__metaclass__属性,并尝试做和前面同样的操作。如果Python在任何父类中都找不到__metaclass__,它就会在模块层次中去寻找__metaclass__,并尝试做同样的操作。如果还是找不到__metaclass__,Python就会用内置的type来创建这个类对象。现在的问题就是,你可以在__metaclass__中放置些什么代码呢?
class Foo(Bar):
pass
在该类并定义的时候,它还没有在内存中生成,知道它被调用。Python做了如下的操作:
1)Foo中有__metaclass__这个属性吗?如果是,Python会在内存中通过__metaclass__创建一个名字为Foo的类对象(我说的是类对象,请紧跟我的思路)。
2)如果Python没有找到__metaclass__,它会继续在父类中寻找__metaclass__属性,并尝试做和前面同样的操作。
3)如果Python在任何父类中都找不到__metaclass__,它就会在模块层次中去寻找__metaclass__,并尝试做同样的操作。
4)如果还是找不到__metaclass__,Python就会用内置的type来创建这个类对象。
怎样创建元类:
现在的问题就是,你可以在__metaclass__中放置些什么代码呢?
答案就是:可以创建一个类的东西。那么什么可以用来创建一个类呢?type,或者任何使用到type或者子类化type的东西都可以。type是python中所有类的元类。我们定义元类时,都需要继承type(或者type的子类)。
编写自定义元类分为两个步骤:
1)编写元类类型的子类。
2)使用元类挂钩将新元类插入到类创建流程中。
我们使 type 类实现子类化,并修改魔术方法,比如 __init__
、__new__
、__prepare__
以及