类也是对象
先明确一点,在Python中类(是类Class,不是对象或实例Object)同样也是一种对象。
1 2 3 4 5 6
| >>> class ObjectCreator(): pass >>> ObjectCreator <class __main__.ObjectCreatorat 0x02D878F0> >>> ObjectCreator() <__main__.ObjectCreator object at 0x02E87EE0>
|
只要你使用关键字class,Python解释器在执行的时候就在内存中创建一个对象,名字就是ObjectCreator。这个对象(类)自身拥有创建对象(类实例)的能力,而这就是为什么它是一个类的原因。
类的本质是一个对象,这就意味着你能对它作如下操作:
1) 你可以将它赋值给一个变量
2) 你可以拷贝它
3) 你可以为它增加属性
4) 你可以将它作为函数参数进行传递
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| >>> print ObjectCreator <class '__main__.ObjectCreator'> >>> def echo(o): … print o … >>> echo(ObjectCreator) <class '__main__.ObjectCreator'> >>> print hasattr(ObjectCreator, 'new_attribute') Fasle >>> ObjectCreator.new_attribute = 'foo' # 你可以为类增加属性 >>> print hasattr(ObjectCreator, 'new_attribute') True >>> print ObjectCreator.new_attribute foo >>> ObjectCreatorMirror = ObjectCreator >>> print ObjectCreatorMirror() <__main__.ObjectCreator object at
|
动态创建类
因为类也是对象,你可以在运行时动态的创建它们,就像其他任何对象一样。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| >>> def choose_class(name): … if name == 'foo': … class Foo(object): … pass … return Foo … else: … class Bar(object): … pass … return Bar … >>> MyClass = choose_class('foo') >>> print MyClass <class '__main__'.Foo> >>> print MyClass() # 你可以通过这个类创建类实例,也就是对象 <__main__.Foo object at at 0x89c6d4c>
|
class定义新类的执行过程
使用class语句定义新类时,将会发生很多事情。首先,类主体将作为其自己的私有字典内的一系列语句来执行。语句的执行与正常代码中的执行过程相同,只是增加了会在私有成员(名称以_开头)上发生的名称变形。最后,类的名称、基类列表和字典将传递给元类的解构函数,以创建相应的类对象。下面的例子演示了这一过程:
1 2 3 4 5 6 7 8 9
| class_name="Foo" class_parents=(object,) class_body=""" def __init__(self,x): self.x=x """ class_dict={} exec(class_body,globals(),class_dict) Foo=type(class_name,class_parents,class_dict)
|
强大的type函数
type函数能够让你知道一个对象的类型是什么。
但是type函数还有一种完全不同的能力,它也能动态的创建类。
type(类名, 父类的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| >>> class MyShinyClass(object): … pass >>> MyShinyClass_new = type('MyShinyClass', (), {}) >>> print MyShinyClass_new <class '__main__.MyShinyClass'> >>> print MyShinyClass_new() # 创建一个该类的实例 <__main__.MyShinyClass object at 0x8997cec> >>> class Foo(object): … bar = True >>> Foo_new = type('Foo', (), {'bar2':False}) >>> print Foo_new <class '__main__.Foo'> >>> print Foo_new.bar True >>> print Foo_new.bar2 False >>> f = Foo_new() >>> print f <__main__.Foo object at 0x8a9b84c> >>> print f.bar True >>> print f.bar2 False >>> FooChild = type('FooChild', (Foo,),{}) >>> print FooChild <class '__main__.FooChild'> >>> print FooChild.bar # bar属性是由Foo继承而来 True
|
回到主题,什么是元类
元类就是用来创建类的“东西”。
我们已经学习到了Python中的类也是对象。好吧,元类就是用来创建这些类(对象)的,元类就是类的类。
函数type实际上是一个元类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| MyClass = MetaClass() MyObject = MyClass() MyClass = type('MyClass', (), {}) 我们知道通过实例的__class__属性,可以知道这个实例是由哪一个类生成的 >>> 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__呢?这下就明白了,type就是Python在背后用来创建所有类的类(元类)。 >>> a.__class__.__class__ <type ''> >>> age.__class__.__class__ <type 'type'> >>> foo.__class__.__class__ <type 'type'> >>> b.__class__.__class__ <type 'type'>
|
那么,怎么样才能让python用元类来生成类呢。
你可以在写一个类的时候为其添加metaclass属性。
1 2 3
| class Foo(object): __metaclass__ = something… […]
|
在Python中,创建Foo类的时候。Python会找Foo中有metaclass这个属性吗?如果是,Python会在内存中通过metaclass创建一个名字为Foo的类对象。如果Python没有找到metaclass,它会继续在父类中寻找metaclass属性,并尝试做和前面同样的操作。如果Python在任何父类中都找不到metaclass,它就会在模块层次中去寻找metaclass,并尝试做同样的操作。如果还是找不到metaclass,Python就会用内置的type来创建这个类对象。
自定义元类
问题是,我们可以在metaclass中放置些什么代码呢。
答案就是:可以创建一个类的东西。那么什么可以用来创建一个类呢?type,或者任何使用到type或者子类化type的东东都可以。
假想一个很傻的例子,你决定在你的模块里所有的类的属性都应该是大写形式。
请记住,’type’实际上是一个类,就像’str’和’int’一样
所以,你可以从type继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| class UpperAttrMetaclass(type): def __new__(cls, name, bases, dict): attrs = ((name, value) for name, value in dct.items() if not name.startswith('__')) uppercase_attr = dict((name.upper(), value) for name, value in attrs) return super(UpperAttrMetaclass, cls).__new__(cls, name, bases, uppercase_attr) __metaclass__ = UpperAttrMetaclass class Foo(object): bar = 'bip' print hasattr(Foo, 'bar') print hasattr(Foo, 'BAR') 我们也可以 class Upper: __metaclass__ = UpperAttrMetaclass class Foo(Upper): bar = 'bip' print hasattr(Foo, 'bar') print hasattr(Foo, 'BAR')
|
究竟为什么要使用元类?
现在回到我们的大主题上来,究竟是为什么你会去使用这样一种容易出错且晦涩的特性?好吧,一般来说,你根本就用不上它:
元类的主要用途是创建API。一个典型的例子是Django ORM。它允许你像这样定义:
1 2 3 4 5
| class Person(models.Model): name = models.CharField(max_length=30) age = models.IntegerField() guy = Person(name='bob', age='35') print guy.age
|
这并不会返回一个IntegerField对象,而是会返回一个int,甚至可以直接从数据库中取出数据。这是有可能的,因为models.Model定义了metaclass, 并且使用了一些魔法能够将你刚刚定义的简单的Person类转变成对数据库的一个复杂hook。Django框架将这些看起来很复杂的东西通过暴露出一个简单的使用元类的API将其化简,通过这个API重新创建代码,在背后完成真正的工作。
阅读自爆栈网的一篇文章后有感做的笔记
原文:What is a metaclass in Python