共计 1774 个字符,预计需要花费 5 分钟才能阅读完成。
在 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)>
- code
外部是一个 PyCodeobject,作为 types.CodeType 对外开放。非内置办法领有一个__code__属性,该属性保留了相应的代码对象。利用内置 compile() 办法,能够在运行期创立 types.CodeType 对象。 - globals
如果一个函数援用的变量不是在部分定义的,而是作为参数转入、由默认参数值提供、或者通过闭包上下文提供,则它会在 globals 字典中查找。
内置的 globals() 办法会返回一个对以后模块的全局符号表(global symbol table)的援用,因而能被用来提供一个总是与以后表的状态相一致的字典。传入任意其它的字典也是能够的(FunctionType((lambda: bar).__code__, {“bar” : “baz”}, “foo”)() == “baz”)。
- name(可选)
管制所返回的函数的__name__ 属性。只真正对 lambdas 有用(因为匿名性,它们通常没有名称),并且重命名函数。 - argdefs(可选)
通过传入一个蕴含任意类型的对象的元组,提供一个形式来供给默认参数值(def
foo(bar="baz"))。(FunctionType((lambda bar: bar).__code__, {}, "foo", (10,))() == 10)。
- 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 常识欢送返回公众号:Python 编程学习圈,发送“J”即可收费获取,每日干货分享