深入理解python元类

类和对象

在理解什么是元类之前,有必要先理解下,什么是类。
什么是类?通俗的讲,类就是用来创建对象的代码片。在python中,类还有一个奇特的特性,就是类,本身也是一个对象。怎么理解?——在你定义一个类的时候,就会在内存中创建一个名字为类名的对象。
能够创建对象(实体)的对象(类),就称之为类。(说起来很别扭,但大致就是这么个意思)。
既然是一个对象,那就可以:
  • 将它分配给一个变量
  • 复制它
  • 给它添加属性
  • 传递给函数的参数

    我们知道,对象不可能凭空产生,它总是由某些”事物”生成的。既然类也是个对象,那也不例外。
    当你在使用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> 

type可以使用字典来定义该类的属性,如:

 >>> class Foo(object):
      ... bar = True

可以转换成:

 >>> Foo = type('Foo', (), {'bar':True})

你可以继承这个类:

 >>> class FooChild(Foo):
    ... pass

这种继承也能用type来实现:

>>> FooChild = type('FooChild', (Foo,), {})
     >>> print(FooChild)
    <class '_main_.FooChild'>
    >>> print(FooChild.bar) # bar is inherited from Foo
    True

最后,你可能还想要给类增加方法。这也很就简单。定义一个函数并将其分配给一个属性就行了:

>>> def echo_bar(self):
      ... print(self.bar)
      ... 
     >>> FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar}) 
     >>> hasattr(Foo, 'echo_bar')
     False
     >>> hasattr(FooChild, 'echo_bar')
     True
      >>> my_foo = FooChild()
      >>> my_foo.echo_bar()
     True 

是的,当你使用class关键字时,python做了所有该做的事。而这些事是通过元类(metaclass)来完成的。

什么是元类

  现在应该就知道元类是什么以及干什么的了吧?
  元类就是那个帮你创建类的"家伙"。
  为了创建对象(实体),你定义了类,是不是?
  而在python中,类也是对象,这样的对象就是通过元类来创建的。元类就是"类的类"。
 上文中提到的type事实上就是一个元类,在python中,所有的类都是使用type创建的。 
 到此,我们也能够更加理解为什么说在python中,一切皆对象了吧?int,string,function以及class,所有的多少对象。他们都能够从一个类中创建。通过_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'> 
 25这个整数对象是有类"int"创建的。那"int"这个对象是由谁创建的呢?继续查看_class_:
      >>> age._class.class_
      <type 'type'>
      >>> name._class.class_
      <type 'type'>
      >>> foo._class.class_
      <type 'type'>
      >>> b._class.class_
     <type 'type'>
是的,元类创建了这些"类"对象。你也可以称元类为创建类的工厂。 
type是python内置的元类,那能不能自定义元类呢?
能!
在学习如何定制元类前,先来了解一个重要的属性。

__metaclass属性

当你创建一个类的时候,你能够给它增加metaclass属性:
class Foo(object):
metaclass = something…
[…]
当你写下class Foo(object)的时候,类对象Foo并不会在内存中创建:Python首先会看类定义中的metaclass。如果找到,就会使用它创建类对象Foo。否则,就使用type来创建。
继续说明:
当你写下如下代码:
class Foo(Bar):
pass
Python:
Foo里面有没有metaclass属性?
如果有,通过使用metaclass里面的元类在内存中创建名为Foo的类对象(在本文中类对象就是指类这个对象,请与类创建的实体对象区别开)。
如果没有,则会在模块(MODULE)层寻找metaclass,并执行相同的创建类对象操作(这种情况只针对老类型定义的类,也就是没有继承任何类的类)。
最终如果找不到任何metaclass,Foo就会使用Bar(第一个父类)的元类来创建类对象。
现在的问题是,metaclass能赋什么值?
回答:能够创建一个类的”事物”,也就是元类type或type的子类(或组合了type的类)

定制元类

想象一个愚蠢的例子。当你想要你模块中的所有类的属性名都是大写字母。有很多种实现方法,其中一种是在模块层设置_metaclass_。这样,模块中的所有类都会使用这个原来来创建,我们只要告诉元类将所有属性转换成大写就行了。
 _metaclass_的值不一定要一个正式的类,可以为任何可调用的事物。
 比如,函数也可以(当然,记住上面说过的,要组合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 
              # 使用'type'来创建!
              return type(future_class_name, future_class_parents, uppercase_attr) 
         _metaclass_ = upper_attr #这会影响模块中的类 
        class Foo(): # 如果这里继承了object,那全局_metaclass_就不会对这个Foo类起作用了哦
       # 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' 
当然,也可以为元类定义一个类:
# 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,而没有调用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”参数。这没什么特别的。new方法总是将其所在的类作为第一个参数,这就是对象方法第一个参数是self,类方法第一个参数是cls一样。
上面的定义使用的参数都太长了,按约定俗成的名称会是这样的:

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) 
  元类在做很多"黑魔法"事情方面的确非常有用,这也是让他看起来复杂的原因。元类本身还是很简单的:
        1)拦截类的创建;2)修改这个类;3)返回修改后的类。
为什么要使用元类类(metaclasses classes)而不是函数呢?
     考虑到_metaclass_可以接受任何可调用事物,那为什么要用一个看起来明显复杂的类呢?
     原因主要有以下几个:

意图更明显。当读到UpperAttrMetaclass(type),你清楚知道要干什么
你能够使用OOP
能够更好的组织你的代码
能够利用new、init和call干点别的。
毕竟它们是元类啊,是不?(意思就是函数不能体现元类作为一个类,一个事物。)

为什么要使用元类?

python大师Tim Peters说过:元类是一种99%的人都不需要关心的深度魔法。当你在好奇你是否需要它,通常这就说明你并不需要(确切需要它的人不需要找任何原因解析为什么需要)。
元类的主要使用场景是创建一个API。典型的例子就是Django ORM。

总结

首先,除了type,一切皆对象,不是类的实体,就是元类的实体。
其次,元类恨复杂。在简单的改变类时你可能并不想要使用。你能通过使用以下两种技术来改变类:
monkey patching
class decorators
需要改变类的99%的情况,你使用这两种更好。而99%的场景,你其实并不需要元类。
【参考文献】
http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python

这篇里面也介绍元类也介绍的非常不错,虽然它标题是关于抽象类的:
http://blog.thedigitalcatonline.com/blog/2016/04/03/abstract-base-classes-in-python/

  • 5
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值