python3 metaclass学习

来源:互联网 发布:淘宝店铺会员卡图片 编辑:程序博客网 时间:2024/05/22 14:45

      在学习python3过程中发现,metaclass的用处比较广泛, 更多可以查看 PEP 3115. 在学习过程中需要做一些序列化操作(比如csv转换), 查阅资料发现, 利用元类可以很容易自动记录一个类中属性和方法定义的顺序,然后进行序列化的操作.

      首先,先介绍一下metaclass的新语法规则:

      1. 有一个prepare函数:

      def prepare_class(name, *bases, metaclass=None, **kwargs):          if metaclass is None:             metaclass = compute_default_metaclass(bases)          prepare = getattr(metaclass, '__prepare__', None)          if prepare is not None:             return prepare(name, bases, **kwargs)          else:             return dict()

       参数解析:

              'name' -- 将要创建的类的名称;

              'bases' -- 基类列表;

       由于它通常是在metaclass实例创建前被调用,所以__prepare__() 一般都被实现为一个类函数. 最后返回的dict()是一个定制的字典对象.http://write.blog.csdn.net/postedit/54095727


    2. __new__(cls, name, bases, clsdict)

      先了解一下在python里__new__(*args, **kwargs)的知识点. __new__至少有一个cls参数, 代表要实例化的类, 有python编译器自动提供. 此外, 必须要有返回值, 返回实例化出来的实例. 为了更好理解并防止以后学习的混淆,在此简单的分析一下__new__ 和 __init__的区别.

      __init__ 有一个self参数, 代表__new__返回的实例, 不需要返回值. 总之, __init__ 是在 __new__正确返回当前类实例的情况下, 才调用__init__() 的.下面用一个最简的例子来探索一下其中的原理.

In [1]: class A(object):
   ...:     def __init__(self):
   ...:         print('init...')
   ...:     def __new__(cls, *args, **kwargs):
   ...:         print(cls)
   ...:         return object.__new__(cls, *args, **kwargs)

In [2]: A()
<class '__main__.A'>
init...
Out[4]: <__main__.A at 0x7fd08daf6f28>

这是正确调用的;

In [3]: class C(object):
    ...:     pass
    ...:

In [4]: class A(C):
    ...:     def __init__(self):
    ...:         print('init...')
    ...:     def __new__(cls, *args, **kwargs):
    ...:         print(cls)
    ...:         return object.__new__(C, *args, **kwargs)
    ...:     

In [20]: A()
<class '__main__.A'>
Out[20]: <__main__.C at 0x7fd08d28f5f8>

发现__init__并没有被调用, 这是因为__new__()提供的是C而不是代表当前类实例的cls, 所以__new__没有返回当前类实例, 而是返回C的实例.


    说了这么多,接下来回到正题.继续讨论metaclass里__new__ 的用法.我援引PEP 3115中的例子来说明这个问题. 用metaclass创建包含所有类成员的列表, 按照声明的顺序排列.

# 定制字典

     # The custom dictionary
     class member_table(dict):
        def __init__(self):
           self.member_names = []

        def __setitem__(self, key, value):
           # if the key is not already defined, add to the
           # list of keys.
           if key not in self:
              self.member_names.append(key)

           # Call superclass
           dict.__setitem__(self, key, value)

     # The metaclass
     class OrderedClass(type):

         # The prepare function
         @classmethod
         def __prepare__(metacls, name, bases): # No keywords in this case
            return member_table()

         # The metaclass invocation
         def __new__(cls, name, bases, classdict):
            # Note that we replace the classdict with a regular
            # dict before passing it to the superclass, so that we
            # don't continue to record member names after the class
            # has been created.
            result = type.__new__(cls, name, bases, dict(classdict))
            result.member_names = classdict.member_names
            return result

     class MyClass(metaclass=OrderedClass):
        # method1 goes in array element 0
        def method1(self):
           pass

        # method2 goes in array element 1
        def method2(self):
           pass

          

按照这个模式, 我们很容易可以写出符合我们要求的python类, 参考python3-cookbook.

from collections import OrderedDict# A set of descriptors for various typesclass Typed:    _expected_type = type(None)    def __init__(self, name=None):        self._name = name    def __set__(self, instance, value):        if not isinstance(value, self._expected_type):            raise TypeError('Expected ' + str(self._expected_type))        instance.__dict__[self._name] = valueclass Integer(Typed):    _expected_type = intclass Float(Typed):    _expected_type = floatclass String(Typed):    _expected_type = str# Metaclass that uses an OrderedDict for class bodyclass OrderedMeta(type):    def __new__(cls, clsname, bases, clsdict):        d = dict(clsdict)        order = []        for name, value in clsdict.items():            if isinstance(value, Typed):                value._name = name                order.append(name)        d['_order'] = order        return type.__new__(cls, clsname, bases, d)    @classmethod    def __prepare__(cls, clsname, bases):        return OrderedDict()
使用:

class Structure(metaclass=OrderedMeta):    def as_csv(self):        return ','.join(str(getattr(self,name)) for name in self._order)# Example useclass Stock(Structure):    name = String()    shares = Integer()    price = Float()    def __init__(self, name, shares, price):        self.name = name        self.shares = shares        self.price = price
>>> s = Stock('xz', 100, 20.3)
>>> s.name
'xz'
>>> s.as_csv()
'xz,100,20.3'
>>>

这就得到一个能进行csv数据转换的代码了.

0 0
原创粉丝点击