Classes as objects

首先,在认识metaclass之前,你需要认识下python中的class。python中class的奇怪特性借鉴了smalltalk语言。大多数语言中,classes仅仅是用于描述怎样创建一个对象的代码端。在某种程度上说,python中的class也是这样的。

  >>> class ObjectCreator(object):
... pass
... >>> my_object = ObjectCreator()
>>> print my_object
<__main__.ObjectCreator object at 0x8974f2c>

但是,python中的classes同时还是objects,是的,看的没错,是objects,一旦你使用关键字class,python将执行并且生成一个对象(object),命令

  >>> class ObjectCreator(object):
... pass
...

将在内存中创建一个名字为ObjectCreator的对象。

这个对象(类)自己具有创建对象(实例)的能力,这也是为什么被称之为类

因为它是一个对象,所以它应该具有对象的一些特性:

  • 你可以把它assign给一个变量
  • 你可以copy它
  • 你可以给它添加属性
  • 你可以把它作为一个函数的参数

例如:

  >>> 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>

Creating classes dynamically

因为classes是对象,所以你可以想对象一样动态的创建他们。

首先,你可以在一个函数中使用关键字创建它:

  >>> 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同样具有一种完全不同的能力,就是我们需要的动态创建函数。type可以将类的描述作为参数,然后生成一个类。type是这样工作的:

  type(name of the class,
tuple of the parent class (for inheritance, can be empty),
dictionary containing attributes names and values)

例如:

>>> class MyShinyClass(object):
... pass

可以利用以下方式手动创建:

  >>> 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>

可以看出,我们以MyShinyClass作为class的name, 函数很明显了,只不过是些不同的参数变化,没有理由去复杂化,用原文作者的话说:They can be different,but there is no reason to complicate things。

What are metaclasses (finally)

终于要开是解释metaclasses了,我也等了好久,Metaclasses are the 'stuff' that creates classes. 这个是文章的作者说的,就是说它是个可以创建类的东东,你定义类是为了创建对象是吗?但是在python中我们视类为对象,so,metaclasses就是来创建对象的。它们是类的类,你可以这样想象它们:

  MyClass = MetaClass()
MyObject = MyClass()

你可以看出type让你做了同样的事情:

  MyClass = type('MyClass', (), {})

这是因为函数type事实上是一个metaclass,在python中,type是一个metaclass用于在后台创建类。现在你知道为什么它为什么用的是小写,而不是大写的Type?
well, 你是不是想到了str/int等函数呢,str用于创建字符串对象,int创建整型对象,那type只不过是创建类对象的类而已。你可以通过查看__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的创建者是谁呢?

  >>> age.__class__.__class__
<type 'type'>
>>> name.__class__.__class__
<type 'type'>
>>> foo.__class__.__class__
<type 'type'>
>>> b.__class__.__class__
<type 'type'>

这下应该稍微明白了些吧,当然,我们也可以创建我们自己的metaclass。

The __metaclass__ attribute

你可以在创建类的时候添加__metaclass__属性:

class Foo(object):
__metaclass__ = something...
[...]

python将使用metaclass去创建类Foo,要小心哦,你写了class Foo(object),但是Foo还没有在内存中创建,python会在你的类中寻找__metaclass__如果找到了就用它创建,如果没有找到,它将使用type去创建类。

详细点说吧,当你定义类时:

class Foo(object):
pass

python将首先查看在类定义中有没有__metaclass__属性,没有将在parent中找,没有去module级别找,如果还没有找到,最后的杀招type。

好,最后的问题是,我们该在__metaclass__中放些什么东东呢?

Custom metaclasses

一个metaclass的主要目的是在类创建的时候自动的去改变它,读起来有点拗。一般来说,当你写些APIs,而且这些APIs要满足当前的上下文的时候,可以考虑使用metaclass。想象一下,当你的module中的类需要将它们的属性写为小写的时候,我们就可以试试在moudle级别metaclass,这时,module中的所有类都将由metaclass创建,我们所要做的是告诉metaclass将所有的属性转化为小写。幸运的是,我们不一定非要将__metaclass__定义为一个类:

# the metaclass will automatically get passed the same argument
# that you usually pass to `type`
def upper_attr(future_class_name, future_class_parents, future_class_attr):
"""
Return a class object, with the list of its attribute turned
into uppercase.
""" # pick up any attribute that doesn't start with '__' and uppercase it
uppercase_attr = {}
for name, val in future_class_attr.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val # let `type` do the class creation
return type(future_class_name, future_class_parents, uppercase_attr) __metaclass__ = upper_attr # this will affect all classes in the module class Foo(): # global __metaclass__ won't work with "object" though
# but we can define __metaclass__ here instead to affect only this class
# and this will work with "object" children
bar = 'bip' print hasattr(Foo, 'bar')
# Out: False
print hasattr(Foo, 'BAR')
# Out: True f = Foo()
print f.BAR
# Out: 'bip'

现在,我们用一个类来实现metaclass:

# remember that `type` is actually a class like `str` and `int`
# so you can inherit from it
class UpperAttrMetaclass(type):
# __new__ is the method called before __init__
# it's the method that creates the object and returns it
# while __init__ just initializes the object passed as parameter
# you rarely use __new__, except when you want to control how the object
# is created.
# here the created object is the class, and we want to customize it
# so we override __new__
# you can do some stuff in __init__ too if you wish
# some advanced use involves overriding __call__ as well, but we won't
# see this
def __new__(upperattr_metaclass, future_class_name,
future_class_parents, future_class_attr): uppercase_attr = {}
for name, val in future_class_attr.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val return type(future_class_name, future_class_parents, uppercase_attr)

但是这个不是真正的OOP。我们可以直接调用type的__new__:

class UpperAttrMetaclass(type): 

    def __new__(upperattr_metaclass, future_class_name,
future_class_parents, future_class_attr): uppercase_attr = {}
for name, val in future_class_attr.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val # reuse the type.__new__ method
# this is basic OOP, nothing magic in there
return type.__new__(upperattr_metaclass, future_class_name,
future_class_parents, uppercase_attr)

你可能已经注意到了额外的参数upperattr_metaclass,这个没有什么特别:一个方法常常将当前的实例作为首个参数,就像平常的方法中的self。当然,我这里用过长的名字视为了更清晰的解释,就像self一样,所有的参数有惯例性的名字,所以一下是个产品性的metaclass:

class UpperAttrMetaclass(type): 

    def __new__(cls, clsname, bases, dct):

        uppercase_attr = {}
for name, val in dct.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val return type.__new__(cls, clsname, bases, uppercase_attr)

我们可以使用super让代码更干净:

class UpperAttrMetaclass(type): 

    def __new__(cls, clsname, bases, dct):

        uppercase_attr = {}
for name, val in dct.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val return super(UpperAttrMetaclass, cls).__new__(cls, clsname, bases, uppercase_attr)

就这么多,metaclass就是这么简单。其实,使用metaclass的代码复杂的原因不是因为使用了metaclass,而是因为你常常使用metaclass去做些需要introspection,操作继承/变量比如__dict__等。事实上,metaclass也确实能做些复杂的事。但是,以下是不复杂的:

  • 理解一个类的创建
  • 改变类
  • 返回一个修改的类

Why would you use metaclasses classes instead of functions?

还是坚持写完吧!因为__metaclass__能接受任意的调用,那为什么要选用看起来比较复杂的类呢?有一下几个原因:

  • 兴建类的目的比较清晰,参考上面的UpperAttrMetaclass(type)
  • 你可以使用OOP。Metaclass能继承自metaclass,覆盖父类方法
  • 你可以更好的组织你的代码结构。make code easy to read
  • 你可以使用__new__, __init__, __call__
  • 都将做了metaclass,总要作点事吧!(这个比较牵强(:-)

Why the hell would you use metaclasses?

python guru写的解释道行太浅看不懂,不翻译了,不过他说了个例子,就是Django中的ORM模型使用了,例如我们可以定义数据模型如下:

  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转化为复杂的链接到数据库字段。

Django通过expose 一个简单的API,将许多复杂的事情让我们看起来很简单,而且通过metaclass,在后台利用API重新生成code。

The last word

最后,作者提到了type的exception,还有class alterations:意思就是说我们大部分时间是用不到class alteration的,用到的时候呢,可以使用monkey patching和class decorators来实现,所以留给metaclass的空间很小了。

全剧终!!!

文章的详情清查看:http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python

偶尔翻看stackoverflow的python帖子,受益匪浅,今天看到metaclass讲的太好了,就试着翻译出来试试,欢迎大家互相交流!

Python中metaclass解释的更多相关文章

  1. python中Metaclass的理解

    今天在学习<python3爬虫开发实战>中看到这样一段代码3 class ProxyMetaclass(type): def __new__(cls, name, bases, attrs ...

  2. python中MetaClass的一些用法

    元类在很多编程语言中都有这样的概念,我们都知道,类可以创建对象,类本身也是对象,既然是对象,那么它肯定也是被创造出来的,元类就专门用来创造类对象,于是,这就给我们提供了一种操纵或者监听类的能力. 平时 ...

  3. python中metaclass的工作原理

    class TMetaclass(type): def __new__(cls, name, bases, attrs): print(cls, name, bases, attrs) return ...

  4. pandas 中有关isin()函数的介绍,python中del解释

  5. [转] 深刻理解Python中的元类(metaclass)

    非常详细的一篇深入讲解Python中metaclass的文章,感谢伯乐在线-bigship翻译及作者,转载收藏. 本文由 伯乐在线 - bigship 翻译.未经许可,禁止转载!英文出处:stacko ...

  6. Python中dunder名称的来历

    版权声明:博客为作者原创,允许转载,但必须注明原文地址:https://www.cnblogs.com/byronxie/p/10741084.html 在 Python 中,我们经常会看到被双下划线 ...

  7. 深刻理解Python中的元类metaclass(转)

    本文由 伯乐在线 - bigship 翻译 英文出处:stackoverflow 译文:http://blog.jobbole.com/21351/ 译注:这是一篇在Stack overflow上很热 ...

  8. 深刻理解Python中的元类(metaclass)

    译注:这是一篇在Stack overflow上很热的帖子.提问者自称已经掌握了有关Python OOP编程中的各种概念,但始终觉得元类(metaclass)难以理解.他知道这肯定和自省有关,但仍然觉得 ...

  9. [转]深刻理解Python中的元类(metaclass)以及元类实现单例模式

    使用元类 深刻理解Python中的元类(metaclass)以及元类实现单例模式 在看一些框架源代码的过程中碰到很多元类的实例,看起来很吃力很晦涩:在看python cookbook中关于元类创建单例 ...

随机推荐

  1. JS设置获取cookies

    结合JavaScript权威指南,加上项目开发时在网上搜集的资料,整理了两种设置和获取cookie的方法. <script> //设置cookie  方法一function setCook ...

  2. TreeSet与HashSet的区别

    Set是java中一个不包含重复元素的collection.更正式地说,set 不包含满足 e1.equals(e2) 的元素对 e1 和 e2,并且最多包含一个 null 元素.正如其名称所暗示的, ...

  3. Bitmap 与ImageSource之间的转换

    public class ImageConverter { [DllImport("gdi32.dll", SetLastError = true)] private static ...

  4. [转载] 50个Android开发人员必备UI效果源码

    好东西,多学习! Android 仿微信之主页面实现篇Android 仿微信之界面导航篇Android 高仿QQ 好友分组列表Android 高仿QQ 界面滑动效果Android 高仿QQ 登陆界面A ...

  5. No enclosing instance of type Outer is accessible. Must qualify the allocation with an enclosing instance of type Outer (e.g. x.new A() where x is an instance of Outer)

    之前看内部类的时候没发现这个问题,今天写代码的时候遇到,写个最简单的例子: 下面这一段代码 红色的部分就是编译报错: No enclosing instance of type Outer is ac ...

  6. 也谈---基于 HTTP 长连接的“服务(转载)

    这里指讨论基于HTTP的推技术, 诸如flash,applet之类的东西不作分析, 他们就不能说是"纯粹"的浏览器应用了. 首先是一点背景知识, 大家都知道长连接避免了tcp连接的 ...

  7. oracle 库文件解决的方法 bad ELF interpreter: No such file or directory

    今天是2014-05-27,今天遇到一个lib问题,再次记录一下.这是一个案例,更是一种解决该问题的方法过程. 当我们在使用sqlplus 登陆unix数据库的时候,有可能出现类似:xxxxxx ba ...

  8. Struts2与ajax整合之缺点

    之前有篇博客介绍了Struts2与ajax的整合,链接Struts2之-集成Json插件实现Ajax 这里不再累述,看以上博客. 此篇博客想吐槽一下Struts2的缺点--错误处理做的不好,怎么做的不 ...

  9. android——字体颜色跟随状态改变

    TextView的字体颜色也可以和ImageView的background一样,跟随状态发生改变.只需要自定义一下字体颜色.在color文件夹下面,新建一个颜色文件的xml. OK ,这就完成 了. ...

  10. 《JavaScript 闯关记》之对象

    对象是 JavaScript 的数据类型.它将很多值(原始值或者其他对象)聚合在一起,可通过名字访问这些值,因此我们可以把它看成是从字符串到值的映射.对象是动态的,可以随时新增和删除自有属性.对象除了 ...