1.什么是类
在理解元类之前,我们必须先掌握Python中的类(class)。
和大多数语言一样,Python中的类知识用来描述如何“生成一个对象”:
>>> class ObjectCreator(object):
... pass
...
>>> my_object = ObjectCreator()
>>> print(my_object)
<__main__.ObjectCreator object at 0x8974f2c>
但是,在Python中,类不仅能用来描述如何生成一个对象,类本身也是对象。
在你使用关键词 class 的时候,Python就会执行它,并创建一个对象。
>>> class ObjectCreator(object):
... pass
...
上述指令在内存中创建了一个“ObjectiveCreator”的对象。
这个对象(类)本身具有创建对象(实例)的能力,因此它也是一个类。你可以对它做以下操作:
1.将其分配给变量
2.复制它
3.为其添加属性
4.将其作为函数参数传递
例如:
>>> print(ObjectCreator) # you can print a class because it's an object
<class '__main__.ObjectCreator'>
>>> def echo(o):
... print(o)
...
>>> echo(ObjectCreator) # you can pass a class as a parameter
<class '__main__.ObjectCreator'>
>>> print(hasattr(ObjectCreator, 'new_attribute'))
False
>>> ObjectCreator.new_attribute = 'foo' # you can add attributes to a class
>>> print(hasattr(ObjectCreator, 'new_attribute'))
True
>>> print(ObjectCreator.new_attribute)
foo
>>> ObjectCreatorMirror = ObjectCreator # you can assign a class to a variable
>>> print(ObjectCreatorMirror.new_attribute)
foo
>>> print(ObjectCreatorMirror())
<__main__.ObjectCreator object at 0x8997b4c>
2.动态创建类
由于类是对象,因此你可以像创建任何对象(数组、字典等)一样,随时随地创建类。
你甚至可以在函数里创建类:
>>> def choose_class(name):
... if name == 'foo':
... class Foo(object):
... pass
... return Foo # return the class, not an instance
... else:
... class Bar(object):
... pass
... return Bar
...
>>> MyClass = choose_class('foo')
>>> print(MyClass) # the function returns a class, not an instance
<class '__main__.Foo'>
>>> print(MyClass()) # you can create an object from this class
<__main__.Foo object at 0x89c6d4c>
但是,这样的类并不是很动态,因为你必须自己编写整个类。
使用class关键字时,Python会帮你自动创建此对象,但是,Python同样也提供了一种手动创建的方法,那就是type函数。
>>> print(type(1))
<type 'int'>
>>> print(type("1"))
<type 'str'>
>>> print(type(ObjectCreator))
<type 'type'>
>>> print(type(ObjectCreator()))
<class '__main__.ObjectCreator'>
type函数最经典的用法是返回对象的类型。但是很少人知道,它还能接受参数并手动创建类。
其中
name
: 类名bases
: 元组,父类名attrs
: 字典,类属性值
因此你可以这样手动创建类:
>>> MyShinyClass = type('MyShinyClass', (), {}) # returns a class object
>>> print(MyShinyClass)
<class '__main__.MyShinyClass'>
>>> print(MyShinyClass()) # create an instance with the class
<__main__.MyShinyClass object at 0x8997cec>
如果你想给它赋予属性,可以这样玩:
>>> class Foo(object):
... bar = True
等同于
>>> Foo = type('Foo', (), {'bar':True})
用来继承也是可以的:
>>> FooChild = type('FooChild', (Foo,), {})
>>> print(FooChild)
<class '__main__.FooChild'>
>>> print(FooChild.bar) # bar is inherited from Foo
True
可见通过type()
函数创建的类和直接写class是完全一样的。
因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()
函数创建出class。
正常情况下,我们用class
来定义类,但是,type()
函数也允许我们动态创建类,也就是说,动态语言本身支持运行期动态创建类,这和静态语言有非常大的不同。
Python是通过什么做到这一切的?那就是元类。
3.什么是元类
元类就是用于创建类的“东西”。
你定义类是为了创建对象,Python中所有的类都是对象。元类是用于创建这些对象的。可以看这个例子:
MyClass = MetaClass()
my_object = MyClass()
这有点像套娃。这段代码转化为type就是这样的:
MyClass = type('MyClass', (), {})
因此,我们可以得到一个基本事实,type 本身就是一个元类。
其实,就是 type 在幕后创建了Python中所有的类。
通过检查__class__属性,你会看到Python中,一切对象都是基于 type 的:
>>> age = 35
>>> age.__class__
<type 'int'>
>>> name = 'bob'
>>> name.__class__
<type 'str'>
>>> def foo(): pass
>>> foo.__class__
<type 'function'>
>>> class Bar(object): pass
>>> b = Bar()
>>> b.__class__
<class '__main__.Bar'>
那么,有个有趣的问题,__class__的__class__是什么呢?
>>> age.__class__.__class__
<type 'type'>
>>> name.__class__.__class__
<type 'type'>
>>> foo.__class__.__class__
<type 'type'>
>>> b.__class__.__class__
<type 'type'>
因此,元类只是创建类对象的东西,如果愿意,可以将其称为“类的工厂”。
type
是Python使用的内置元类。不过,你可以创建自己的元类。
3.1 __metaclass__属性
在Python 2中,可以在编写类时添加属性__metaclass__,使用某个元类来创建该类:
class Foo(object):
__metaclass__ = something...
[...]
不过,要小心的是,你虽然先写了 class Foo(object),但Foo这个对象尚未被创建,Python将先寻找__metaclass__类,找到后用它来创建Foo类。
如果没有这个__metaclass__类,它将使用 type 来创建类。
因此,类创建的流程是这样的:
1.创建的类中有__metaclass__元类属性吗?
2.如果有,那就用__metaclass__给该类在内存中创建一个类对象。
3.如果Python找不到__metaclass__
,它将在MODULE级别查找__metaclass__属性 。
4.如果还是没有,那就使用父类的元类来创建类对象。
现在的问题就是,你可以在__metaclass__中放置些什么代码呢?
答案就是:可以创建一个类的东西。那么什么可以用来创建一个类呢?type,或者任何继承或使用它的东西。
3.2 Python 3中的元类
设置元类的语法在Python3已改为:
class Foo(object, metaclass=something):
...
即不再使用__metaclass__属性,而是在基类参数列表中引入关键字参数。
不过元类的基本工作方式不变。在Python3中,你可以将属性作为关键字参数传递给元类:
class Foo(object, metaclass=something, kwarg1=value1, kwarg2=value2):
...
4.为什么需要元类
元类最主要的一个应用方向是创建API,一个最著名的应用是Django ORM,比如:
class Person(models.Model):
name = models.CharField(max_length=30)
age = models.IntegerField()
当你这样访问属性的时候:
person = Person(name='bob', age='35')
print(person.age)
它并不会返回models.IntegerField,而是返回了一个整形的数字。
这是因为models.Model引用了一个ModelBase
类,该类随后进行了魔术般地操作,使其能够与数据库字段进行挂钩。
这就是元类的作用,Django通过它,完成了系列复杂的幕后工作,将原本非常复杂的事情变得非常简单。
我们的文章到此就结束啦,如果你喜欢今天的Python 实战教程,请持续关注Python实用宝典。
有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。
原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!
Python实用宝典 (pythondict.com)
不只是一个宝典
欢迎关注公众号:Python实用宝典