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
chinesehuazhou
V2EX  ›  Python

Python 骚操作:动态定义函数(内有公众号宣传,不喜勿进)

  •  
  •   chinesehuazhou · 2019-03-18 21:24:19 +08:00 · 1286 次点击
    这是一个创建于 2081 天前的主题,其中的信息可能已经有所发展或是发生改变。

    标题:Python Tips: Dynamic function definition

    作者:Philip Trauner

    译者:豌豆花下猫

    链接: https://philip-trauner.me/blog/post/python-tips-dynamic-function-definition

    基于 MIT 许可协议

    在 Python 中,没有可以在运行时简化函数定义的语法糖。然而,这并不意味着它就不可能,或者是难以实现。

    from types import FunctionType
    
    foo_code = compile('def foo(): return "bar"', "<string>", "exec")
    foo_func = FunctionType(foo_code.co_consts[0], globals(), "foo")
    
    print(foo_func())
    

    输出:

    bar
    

    剖析

    逐行检视代码,你会发现语言 /解释器的屏障是多么脆弱。

    >>> from types import FunctionType
    

    Python 文档通常不会列出那些非用于手动创建的类的特征(这是完全合理的)。有三种方法可以解决这个问题:help()、inspect (无法查看内置方法)、以及最后的解决方案,即查看 CPython 源代码。

    在本例中,help() 与 inspect 都可以完成工作,但是查看实际的源代码,则会揭示出关于数据类型的更多细节。

    >>> from inspect import signature
    >>> signature(FunctionType)
    <Signature (code, globals, name=None, argdefs=None, closure=None)>
    

    1. code

    内部是一个PyCodeobject ,作为types.CodeType 对外开放。非内置方法拥有一个__code__ 属性,该属性保存了相应的代码对象。利用内置的 compile() 方法,可以在运行期创建types.CodeType 对象。

    2. globals

    如果一个函数引用的变量不是在局部定义的,而是作为参数转入、由默认参数值提供、或者通过闭包上下文提供,则它会在 globals 字典中查找。

    内置的 globals() 方法会返回一个对当前模块的全局符号表( global symbol table )的引用 ,因此能被用来提供一个总是与当前表的状态相一致的字典。传入任意其它的字典也是可以的( FunctionType((lambda: bar).__code__, {"bar" : "baz"}, "foo")() == "baz")。

    3. name (可选)

    控制所返回的函数的__name__ 属性。只真正对 lambdas 有用(由于匿名性,它们通常没有名称),并且重命名函数。

    4. argdefs (可选)

    通过传入一个包含任意类型的对象的元组,提供了一个方式来供应默认参数值( def foo(bar="baz"))。( FunctionType((lambda bar: bar).__code__, {}, "foo", (10,))() == 10 )。

    5. closure (可选)

    (如果需要在 CPython ( PyPy,Jython,...)以外的其它 Python VM 中执行,可能不应该触及,因为它严重地依赖于实现细节)。

    一个cell 对象的元组。创建 cell 对象并非完全是直截了当的,因为需要调用 CPython 的内部组件,但有一个库可以令它更加方便:exalt (无耻的广告)。(译注:这个库是作者开发的。)

    >>> foo_code = compile('def foo(): return "bar"', "<string>", "exec")
    

    compile() 是一个内置方法,因此同时也是文档丰富的。

    exec 模式被用到,因为定义函数需要用多个语句。

    >>> foo_func = FunctionType(foo_code.co_consts[0], globals(), "foo")
    

    聚合全部内容,并将动态创建的函数指定给一个变量。

    那个被前一句代码编译成的函数,成为了生成的代码对象的第一个常量,因此仅仅指向 foo_code 是不充分的。这是 exec 模式的直接后果,因为生成的代码对象可以包含多个常量。

    >>> print(foo_func())
    

    动态生成的函数可以像其它函数一样被调用。

    结尾

    • 除了做实验,需要用到动态创建函数的场景很少。
    • 玩耍( Toying around ) Python 的内部构件是一种深入学习这门语言的好方法。
    • 如果需要,可以毫不费力地越过解释器 /语言的界线。

    还是一如既往地:不要滥用语言 (好吧,一点点也无妨,对吧?)

    --------(译文完)--------

    花下猫语: 在上一篇《Python 进阶:如何将字符串常量转为变量?》中,我介绍了两种动态修改变量 的方法( globals() 与 exec())。写完之后,我偶然发现,在自己列的“计划转载清单”中,有这一篇相关的文章,它介绍了动态定义函数 的方法。因为它的相关度太大,而篇幅又是那么小(核心代码只有三行,文中其它内容都是在解释其背后的原理),我觉得如果翻译出来的话,效果会更好,所以就抓紧时间翻译出来了。建议与前一篇文章配合阅读。

    公众号 [Python 猫] , 专注 Python 技术、数据科学和深度学习,力图创造一个有趣又有用的学习分享平台。本号连载优质的系列文章,有喵星哲学猫系列、Python 进阶系列、好书推荐系列、优质英文推荐与翻译等等,欢迎关注哦。PS:后台回复“爱学习”,免费获得一份学习大礼包。

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1082 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 22:28 · PVG 06:28 · LAX 14:28 · JFK 17:28
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.