virusdefender's blog ʕ•ᴥ•ʔ

python __new__和__metaclass__

先说__new__

__new__原型为object.__new__(cls,[...]),cls是一个类对象。当你调用C(*arg, **kargs)来创建一个类C的实例时。python内部调用是C.__new__(C, *arg, **kargs),然后返回值是类C的实例c。在确认c是C的实例后,python再调用C.__init__(c, *arg, **kargs)来实例化c

 1class Person(object):
 2    def __new__(cls, name, age):
 3        print '__new__ called.'
 4        return super(Person, cls).__new__(cls, name, age)
 5
 6    def __init__(self, name, age):
 7        print '__init__ called.'
 8        self.name = name
 9        self.age = age
10
11    def __str__(self):
12        return '<Person: %s(%s)>' % (self.name, self.age)
13
14if __name__ == '__main__':
15    piglei = Person('piglei', 24)
16    print piglei

__new__方法主要是当你继承一些不可变的class时(比如int, str, tuple), 提供给你一个自定义这些类的实例化过程的途径。还有就是实现自定义的metaclass

比如这样是没效果的

1class PositiveInteger(int):
2    def __init__(self, value):
3        super(PositiveInteger, self).__init__(self, abs(value))

这样才行

1class PositiveInteger(int):
2    def __new__(cls, value):
3        return super(PositiveInteger, cls).__new__(cls, abs(value))

使用__new__可以实现单例模式

1class Singleton(object):
2    def __new__(cls):
3        if not hasattr(cls, 'instance'):
4            cls.instance = super(Singleton, cls).__new__(cls)
5        return cls.instance

或者

1class Singleton(object):
2    _singleton = {}
3     
4    def __new__(cls):
5        if not cls._singleton.has_key(cls):
6            cls._singleton[cls] = object.__new__(cls)
7        return cls._singleton[cls]

下面是__metaclass__

“元类就是深度的魔法,99%的用户应该根本不必为此操心。如果你想搞清楚究竟是否需要用到元类,那么你就不需要它。那些实际用到元类的人都非常清楚地知道他们需要做什么,而且根本不需要解释为什么要用元类。” —— Python界的领袖 Tim Peters

比如我们想给python的list增加一个add方法,实现append方法的功能的时候,我们可以这样

1class MyList(list):
2    def add(self, value):
3        self.append(value)

还可以这样

1class ListMetaclass(type):
2    def __new__(cls, name, bases, attrs):
3        attrs['add'] = lambda self, value: self.append(value)
4        return type.__new__(cls, name, bases, attrs)
5
6class MyList(list):
7    __metaclass__ = ListMetaclass 

在这里面也能看出来__new__方法接受的参数是四个,而前面用到的一般只使用了cls一个参数

对于

1class Foo(Bar):
2    pass

Python做了如下的操作:

Foo中有__metaclass__这个属性吗?如果是,Python会在内存中通过__metaclass__创建一个名字为Foo的类对象(我说的是类对象,请紧跟我的思路)。如果Python没有找到__metaclass__,它会继续在Bar(父类)中寻找__metaclass__属性,并尝试做和前面同样的操作。如果Python在任何父类中都找不到__metaclass__,它就会在模块层次中去寻找__metaclass__,并尝试做同样的操作。如果还是找不到__metaclass__,Python就会用内置的type来创建这个类对象。

所以类的父类的__metaclass__是会被调用两次的,也就是说metaclass可以隐式地继承到子类,但子类自己却感觉不到。比如

 1class ListMetaclass(type):
 2    def __new__(cls, name, bases, attrs):
 3        print name
 4        attrs['add'] = lambda self, value: self.append(value)
 5        return type.__new__(cls, name, bases, attrs)
 6
 7class MyList(list):
 8    __metaclass__ = ListMetaclass 
 9    
10    
11class MyList1(MyList):
12    pass
13    
14l = MyList1()
15l.add(2)

可以发现输出为

1MyList
2MyList1

使用__metaclass__实现的一个简单的orm

 1class User(Model):
 2    # 定义类的属性到列的映射:
 3    id = IntegerField('id')
 4    name = StringField('username')
 5    email = StringField('email')
 6    password = StringField('password')
 7
 8
 9class Model(dict):
10    __metaclass__ = ModelMetaclass
11
12    def __init__(self, **kw):
13        super(Model, self).__init__(**kw)
14
15    def __getattr__(self, key):
16        try:
17            return self[key]
18        except KeyError:
19            raise AttributeError(r"'Model' object has no attribute '%s'" % key)
20
21    def __setattr__(self, key, value):
22        self[key] = value
23
24    def save(self):
25        fields = []
26        params = []
27        args = []
28        for k, v in self.__mappings__.iteritems():
29            fields.append(v.name)
30            params.append('?')
31            args.append(getattr(self, k, None))
32        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params))
33        print('SQL: %s' % sql)
34        print('ARGS: %s' % str(args))
35
36
37class ModelMetaclass(type):
38    def __new__(cls, name, bases, attrs):
39        # 还是上面的原因 Mode类不需要调用这个__new__
40        if name=='Model':
41            return type.__new__(cls, name, bases, attrs)
42        mappings = dict()
43        # attrs的内容是{'password': <__main__.StringField object at 0x7fb405714c10>...}等
44        for k, v in attrs.iteritems():
45            if isinstance(v, Field):
46                print('Found mapping: %s==>%s' % (k, v))
47                mappings[k] = v
48        # 删除这些类属性 防止访问实例属性的时候发生错误,因为实例属性优先级大于类属性
49        for k in mappings.iterkeys():
50            attrs.pop(k)
51        # 假设表名和类名一致
52        attrs['__table__'] = name 
53        # 保存属性和列的映射关系
54        attrs['__mappings__'] = mappings 
55        return type.__new__(cls, name, bases, attrs)

本文参考

廖雪峰的官方网站

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

提交评论 | 微信打赏 | 转载必须注明原文链接

#Python