用闭包和元编程技巧给 Python 对象加上索引

2015-04-07 15:47:44 +08:00
 wh0syourda66y

0x00.引子

在看《Expert Python Programming》看到Meta-programming这章,一下子玩high了,为了度过漫长的节后综合症恢复期,这里记录一下玩乐的过程。

0x01 初始化之前的代码执行

new方法是一个‘元构建器’(meta-constructor),每当一个对象被实例化时都会调用之。

class MyClass(object):
    def __new__(cls):
        print('{0}.__new__()'.format(cls.__name__))
        obj = object.__new__(cls)
        return obj

    def __init__(self):
        print('{0}.__init__()'.format(self.__class__))
In [7]: mclass = MyClass()
MyClass.__new__()
<class '__main__.MyClass'>.__init__()

继承自该类的子类自然也有相应的表现:

class SubClass(MyClass):
    pass
In [9]: sclass = SubClass()
SubClass.__new__()
<class '__main__.SubClass'>.__init__()

这里可以看到new是比init更早调用的,也就是说newinit更加底层,甚至在对象还没有创建的时候就可以工作,这对于需要隐式地初始化对象状态是很好的解决方案。

0x02 动态定义的方法

在Python中定义一个方法可以是这样:

def MyFunc():
    print('calling Myfunc')
In [12]: MyFunc()
calling Myfunc

也可以是这样

def gen_MyFunc():
    print('defining Myfunc')
    def inner_func():
        print('calling Myfunc')
    return inner_func
In [16]: MyFunc = gen_MyFunc()
defining Myfunc

In [17]: MyFunc()
calling Myfunc

0x03 动态定义的类

在Python中,类是一种type,type本身也是一个特殊的类,而类本身就是对象。

In [46]: MyClass.__class__
Out[46]: type

In [44]: "a".__class__
Out[44]: str

In [45]: "a".__class__.__class__
Out[45]: type

In [37]: type.__bases__
Out[37]: (object,)

In [42]: isinstance(type,object)
Out[42]: True

所以我们可以这么定义一个类:

MyClass = type('MyClass',(object,),{'__init__':lambda self:None'})

等价于

class MyClass(object):
    def __init__(self):
        pass

0x04 闭包

闭包是一组函数和存储上下文的组合。这个上下文(context)是这个函数被定义时的所处作用域中包含的引用集合。在Python中,这个环境被存储在一个cell的tuple中。你能够通过funcclosure或Python 3中的closure_属性访问它。这里就不详细展开了。

def func_closure():
    inner_var = 'inner var'
    def inner_func():
        print('accessing '+inner_var)
    return inner_func
In [71]: func_closure()()
accessing inner var

0x05 《let over lambda》

接下来我们要在上面这些功能的基础上,实现一个类似 ‘计划生育’ 的功能。借助这个功能,我们可以统计每个类实例化出的所有实例引用,并且可以通过这个引用操作所有类的实例。这样是不是很令人心动呢?

简单的思路如下:

不多说了,看代码:

# -*- coding=utf8 -*-
import weakref

def gen_class():
    # closure variables
    birth_hash = []

    def __new__(cls):
        #cls.saybefore_create()
        obj = object.__new__(cls)
        cls.save_birth_hash(obj)
        return obj

    def __init__(self):
        pass

    def method(self):
        print(self.__class__)

    @classmethod
    def saybefore_create(cls):
        print('hi,',cls)

    @classmethod
    def save_birth_hash(cls,obj):
        obj_ref = weakref.ref(obj)
        birth_hash.append(obj_ref)

    @classmethod
    def get_birth_hash(cls):
        return birth_hash

    return type('MyClass',(object,),{'__new__':__new__,'__init__':__init__,'method':method,'saybefore_create':saybefore_create,'save_birth_hash':save_birth_hash,'get_birth_hash':get_birth_hash})

And we play with it!

In [86]: MyClass = gen_class()

In [87]: a = MyClass()

In [88]: b = MyClass()

In [89]: c = MyClass()

In [90]: a.get_birth_hash()
Out[90]:
[<weakref at 0000000003579278; to 'MyClass' at 0000000003570CF8>,
 <weakref at 0000000003579368; to 'MyClass' at 00000000035707F0>,
 <weakref at 0000000003579458; to 'MyClass' at 0000000003570F28>]

In [91]: a.get_birth_hash()[1]() == b
Out[91]: True

到这里其实碰到了一个不大不小的问题,就是不能及时的清除失效的对象引用,如果直接重写 del()会破坏gc,如果有什么好的方法,请不吝告知。

EOF

2436 次点击
所在节点    Python
2 条回复
origingodoh
2015-04-08 09:37:31 +08:00
没弄清楚,难道不能直接用类变量来记录么?类变量也是所有实例共享的。
sing1ee
2015-04-13 09:06:03 +08:00
也一直是这样用的,不过感觉还是没有理清楚。这块儿值得深入讨论

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/182137

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX