用闭包和元编程技巧给 Python 对象加上索引 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
wh0syourda66y

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

  •  
  •   wh0syourda66y 2015 年 4 月 7 日 3033 次点击
    这是一个创建于 4036 天前的主题,其中的信息可能已经有所发展或是发生改变。

    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》

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

    简单的思路如下:

    • 闭包+动态类+动态方法+new操作符+weakref

    不多说了,看代码:

    # -*- coding=utf8 -*- import weakrefdef 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

    2 条回复    2015-04-13 09:06:03 +08:00
    origingodoh
        1
    origingodoh  
       2015 年 4 月 8 日
    没弄清楚,难道不能直接用类变量来记录么?类变量也是所有实例共享的。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1195 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 148ms UTC 17:44 PVG 01:44 LAX 10:44 JFK 13:44
    Do have faith in what you're doing.
    ubaomsnsnddmindexpchomeyahoorakutenmypapermeadowduckbidyahooyoubaozxmzxmasdabnvcgcvbfgdfscvmmhjkxxddcyybgbzznbnccubaouaituacvGXCVETGDGYHFGBCVBFJFHCBRECBCGDGET54WRWRRWERWREWWRWERRWERSDGEWSFDSFSFfbbsubaofhddfgewrdgdfewwrewwretruyutututdfgfgdgdfgtetgdfgtdfgdert4gdfggwr235wer3wevsdfsdfgdfertxcvsdfrwerhfddfgcvbrwfafbdfhjghbmnlghrtygfdscxvxcvxcsvdasfdffgdcvsdftertsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfshasha9178shasha9178shasha9178shasha9178shasha9178liflif2liflif2liflif2liflif2liflif2liblib3liblib3liblib3liblib3liblib3zhazha444zhazha444zhazha444zhazha444zhazha444dende5dendedendendenden2denden21fenfen9fenf619fen619fenfe9fe619sdfsdfsdfsdfsdfzhazh90zhazh0zhaa50zha90zh590zhozhozzhozhzhozhozhozho2lislislls95lili95lils5liss9sdf0ty987sdft876sdft9876sdf09876sd0t9876sdf0ty98sdf0976sdf0ty986sdf0ty96sdf0t76sdf0876df0ty98sf0t876sd0ty76sdy76sdf76sdf0t76sdf0ty9sdf0ty98sdf0ty987sdf0ty98sdf6676sdf876sd876sd876sdf6sdf6sdf9876sdf0tsdf06sdf0ty9776sdf0ty9776sdf0ty76sdf8876sdf0tsd6sdf06s688876sd688sdf86
    sing1ee
        2
    sing1ee  
       2015 年 4 月 13 日
    也一直是这样用的,不过感觉还是没有理清楚。这块儿值得深入讨论