Python骚操作之动态定义函数
在Python中,没有可以在运行时简化函数定义的语法糖。然而,这并不意味着它就不可能,或者是难以实现。
fromtypesimportFunctionType foo_code=compile('deffoo():return"bar"',"","exec") foo_func=FunctionType(foo_code.co_consts[0],globals(),"foo") print(foo_func())
输出:bar
剖析
逐行检视代码,你会发现语言/解释器的屏障是多么脆弱。
>>>fromtypesimportFunctionType
Python文档通常不会列出那些非用于手动创建的类的特征(这是完全合理的)。有三种方法可以解决这个问题:help()、inspect(无法查看内置方法)、以及最后的解决方案,即查看CPython源代码。
在本例中,help()与inspect都可以完成工作,但是查看实际的源代码,则会揭示出关于数据类型的更多细节。
>>>frominspectimportsignature >>>signature(FunctionType)
1.code
内部是一个PyCodeobject,作为types.CodeType对外开放。非内置方法拥有一个__code__属性,该属性保存了相应的代码对象。利用内置compile()方法,可以在运行期创建types.CodeType对象。
2.globals
如果一个函数引用的变量不是在局部定义的,而是作为参数转入、由默认参数值提供、或者通过闭包上下文提供,则它会在globals字典中查找。
内置的globals()方法会返回一个对当前模块的全局符号表(globalsymboltable)的引用,因此能被用来提供一个总是与当前表的状态相一致的字典。传入任意其它的字典也是可以的(FunctionType((lambda:bar).__code__,{"bar":"baz"},"foo")()=="baz")。
3.name(可选)
控制所返回的函数的__name__属性。只真正对lambdas有用(由于匿名性,它们通常没有名称),并且重命名函数。
4.argdefs(可选)
通过传入一个包含任意类型的对象的元组,提供一个方式来供应默认参数值(deffoo(bar="baz"))。(FunctionType((lambdabar:bar).__code__,{},"foo",(10,))()==10)。
5.closure(可选)
(如果需要在CPython(PyPy,Jython,…)以外的其它PythonVM中执行,可能不应该触及,因为它严重地依赖于实现细节)。
一个cell对象的元组。创建cell对象并非完全是直截了当的,因为需要调用CPython的内部组件,但有一个库可以令它更加方便:exalt(无耻的广告)。(译注:这个库是作者开发的。)
>>>foo_code=compile('deffoo():return"bar"',"","exec")
compile()是一个内置方法,因此同时也是文档丰富的。
exec模式被用到,因为定义函数需用多个语句。
>>>foo_func=FunctionType(foo_code.co_consts[0],globals(),"foo")
聚合全部内容,并将动态创建的函数指定给一个变量。
那个被前一句代码编译成的函数,成为了生成的代码对象的第一个常量,因此仅仅指向foo_code是不充分的。这是exec模式的直接后果,因为生成的代码对象可以包含多个常量。
>>>print(foo_func())
动态生成的函数可以像其它函数一样被调用。
结尾
除了做实验,需要用到动态创建函数的场景很少。
玩耍(Toyingaround)Python的内部构件是一种深入学习这门语言的好方法。
如果需要,可以毫不费力地越过解释器/语言的界线。
还是一如既往地:不要滥用语言(好吧,一点点也无妨,对吧?)
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。