元类和动态类

原文链接:http://www.jianshu.com/p/e899605ccf2f

本篇笔记学习自:Improve Your Python: Metaclasses and Dynamic Classes With Type

  • type()的使用
  1. >>> type(1)
  2. <class 'int'>
  3. >>> type('foo')
  4. <class 'str'>
  5. >>> type(3.0)
  6. <class 'float'>
  7. >>> type(float)
  8. <class 'type'>
  • type的类型?
    上面的 type(float) 为什么是<class 'type'>呢?我们再来看一个例子:
  1. >>> class Foo(object):
  2. ... pass
  3. ...
  4. >>> type(Foo)
  5. <class 'type'>

还有这个:

  1. >>> type(type)
  2. <class 'type'>

type是所有类型的类型,包括它自己。type是一个元类,或者是一个创建所有类的东西。

  • 创建自己的元类

Just like regular classes, metaclasses can be user-defined. To use it, you set a class's metaclass attribute to the metaclass you built. A metaclass can be any callable, as long as it returns a type. Usually, you'll assign a class's metaclass to a function that, at some point, uses a variant of type we've not yet discussed: the three parameter variety used to create classes.

就像普通的类,元类也可以是用户自定义的。使用自定义元类时,你需要设置一个类的metaclass属性为你创建的那个元类。元类可以是任何可调用的函数,只要它返回一种类型。

当我们写下下面的代码就是创建类的过程,首先解释器会检查是否有metaclass这个属性,没有就调用type来创建类(类也是对象)。

  1. class Foo(Bar):
  2. pass

现在的问题就是,你可以在metaclass中放置些什么代码呢?答案就是:可以创建一个类的东西。那么什么可以用来创建一个类呢?type,或者任何使用到type或者子类化type的东东都可以。

  • type的缺点
    当我们调用type并且传入三个参数的时候——type(name, bases, dict)创建一种新的类型。
    以下的两份代码作用是相同的,运行后,Foo都指向了一个继承自object的类:
  1. class Foo(object):
  2. pass
Foo = type('Foo', (), {})

添加方法:

  1. def always_false(self):
  2. return False
  3.  
  4. Foo.always_false = always_false

或者可以这样子做:

Foo = type('Foo', (), {'always_false': always_false})

类的继承:

FooBar = type('FooBar', (Foo), {})
  • 这有什么鬼用?
    虽然不经常用到,但是在一些场合,用type来创建类比较合适,比如在一些需要动态创建类的场合,只有程序允许起来,我们才有足够的信息知道要怎么去创建类,这些场合用type来动态创建类比较合适。

元类的主要目的就是为了当创建类时能够自动地改变类。通常,你会为API做这样的事情,你希望可以创建符合当前上下文的类。假想一个很傻的例子,你决定在你的模块里所有的类的属性都应该是大写形式。有好几种方法可以办到,但其中一种就是通过在模块级别设定metaclass。采用这种方法,这个模块中的所有类都会通过这个元类来创建,我们只需要告诉元类把所有的属性都改成大写形式就万事大吉了。

  • 自定义元类的例子——将类的属性都改为大写

元类的主要目的就是为了当创建类时能够自动地改变类。通常,你会为API做这样的事情,你希望可以创建符合当前上下文的
类。假想一个很傻的例子,你决定在你的模块里所有的类的属性都应该是大写形式。有好几种方法可以办到,但其中一种就是通
过在模块级别设定metaclass。采用这种方法,这个模块中的所有类都会通过这个元类来创建,我们只需要告诉元类把所有
的属性都改成大写形式就万事大吉了。

首先我们需要一个元类:

  1. # 元类会自动将你通常传给‘type’的参数作为自己的参数传入
  2. def upper_attr(future_class_name, future_class_parents, future_class_attr):
  3. '''返回一个类对象,将属性都转为大写形式'''
  4. # 选择所有不以'__'开头的属性
  5. attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__'))
  6. # 将它们转为大写形式
  7. uppercase_attr = dict((name.upper(), value) for name, value in attrs)
  8. # 通过 'type' 来做类对象的创建
  9. return type(future_class_name, future_class_parents, uppercase_attr)

模块级别地设置metaclass

__metaclass__ = upper_attr   #   这会作用到这个模块中的所有类
  1. class Foo(object):
  2. # 我们也可以只在这里定义 __metaclass__ ,这样就只会作用于这个类中
  3. bar = 'bip'
  4. print
  5. hasattr(Foo, 'bar')
  6. # 输出 : False
  7. print
  8. hasattr(Foo, 'BAR')
  9. # 输出 :True
  10. f = Foo()
  11. print
  12. f.BAR
  13. # 输出 :'bip'

拓展阅读:
1.stackoverflow上面的问答
2.上面链接回答的翻译

类也是对象,创建类的就是元类。

 

posted @ 2019-07-20 17:13  天涯海角路  阅读(105)  评论(0)    收藏  举报