使用function()可以创建一个函数



我正在学习python中的元类,我很着迷地发现你可以创建这样的函数:Test = type("Test",(),{"x":5})

由于函数也是函数类的对象,因此可以创建类似格式的函数,如下所示:MyFunc = function("MyFunc", (), {})我不知道如何在函数中添加代码,也不知道这是否可能。感谢

事实上,函数不是直接可用的,所以您必须从真正函数的类型中获得它:

def foo():
pass
function = type(foo)
del foo        # remove it from the know symbols since it is no longer useful

此外,该函数类型的构造函数需要类型代码的第一个参数。对于琐碎的操作,可以使用lambda:

MyFunc = function((lambda x: 2*x).__code__, {}, 'MyFunc')

它现在看起来或多或少像一个功能:

print(MyFunc(2))
4
print(MyFunc.__name__)
MyFunc

但是,即使它表明Python可以是一个惊人的工具,我也无法想象上面的黑客会有一个严重的用例。。。

您的示例不创建函数,而是创建类型,并允许您创建Test对象。

Test = type("Test",(),{"x":5})
t = Test()
print(t)
print(type(t))

输出:

<__main__.Test object at 0x0000023B1CC0A100>
<class '__main__.Test'>

您可以使用lambda:创建一个函数

add_one = lambda x: x + 1
print(add_one(41))
print(type(add_one))

输出:

42
<class 'function'>

不过,一般来说,你不应该这样做,这样做被认为是不好的形式,除非你出于某种神秘的原因不得不这样做。

不应该这样做的主要原因是,通常不需要修改任何需要在运行时更改的函数。程序的用户不需要知道调用了什么函数和变量。如果你正在编写一个模块供人们导入,在大多数情况下,你可以使用组合和继承。

Edit:user@jsbueno正确地指出lambda函数不允许您正确地编写完整的函数。它们概述了如何开始实际操作,但如果您的意图是采用文本格式的代码并动态编译,您也可以考虑使用exec()

exec("""
def add_one(x):
return x + 1
""")
print(add_one(1))

然而,请注意,在程序中执行字符串是非常危险的,有些用户可能会对其进行某种程度的控制——他们可以在其中放入任何内容。还要注意,您的编辑器或IDE可能不喜欢您调用未定义为代码一部分的函数(毕竟这只是一个字符串,无论它是否是有效代码,IDE都无法处理)。

您应该告诉我们更多关于实际用例的信息,以便更好地反馈特定方法是否比另一种更好。

函数是一个更复杂的野兽,但它们可以用类似的方式创建。

首先:Test = type("Test",(),{"x":5})创建了一个类,而不是一个函数。

其次,还有函数作为表达式的语法,使用关键字lambda,其工作方式如下:

myfunction = lambda x: x + 5

相当于:

def myfunction(x):
return x + 5

但这与以编程方式创建函数不同。Type调用的等价物是对types.FunctionType的调用。

虽然该调用的返回将为您提供一个函数,但要让它工作的一些元素更难:最重要的部分是";代码对象":与一些元数据参数相关联的字节码的一部分编译,这些参数允许它作为函数执行-然后,对FunctionType的调用将聚合更多的元数据,您就有了一个工作函数。它远不如用Type创建一个新类有用。实际上,通常情况下,创建新函数是为了修改现有函数的某些行为,所以代码对象,即核心部分,只是从其他正常创建的现有函数中复制过来的。

内置的compile函数是创建代码对象的一种简单方法,它可以与FunctionType一起使用。但是,如果使用它编译的代码本身没有函数定义,那么它将被构建为顶级代码:它不能包含返回语句,也不能访问参数。

但是,尽管如此,compile的输出可以被馈送到FunctionType中,并且您可以有一个";基元";可以改变全局变量的函数:

In [12]: import types
In [13]: a = 0
In [15]: mycode = compile("global ana = 23", filename="", mode="exec")
In [16]: myfunc = types.FunctionType(mycode, globals(), "myfunc")
In [17]: myfunc()
In [18]: a
Out[18]: 23

然而,更常见的模式是创建一个包含完整函数体def name():和缩进的多行字符串,然后使用exec语句创建它。感觉远不如";魔术;毕竟,与上面的序列相比;eval";编写一段代码——但在实践中,它是在";真实世界";当需要此资源时执行代码。Python自己的stdlib使用类似的东西来创建collection.namedtuple对象:

In [19]: source = """
...: def myfunc(a, b):
...:     return a + b
...: """
In [20]: exec(source, globals())
In [21]: myfunc(3, 2)
Out[21]: 5

为此存在一些用例;源";一个";f字符串";以及在运行时以编程方式提交一些值。

为了完整起见,正如我上面提到的,对compile的调用不会给你足够的控制权来创建一个完全工作的函数,可以访问参数,可以返回值等等。然而,可以通过使用types.CodeType来构建-这是一个更复杂的调用,最多有18个参数,其中最重要的是不能容易地从Python源代码合成,除非使用上面的任何方法构建函数;借用";它需要一个";代码串";它是一个普通的bytes对象;在Python VM中运行的机器代码";。有一些方法可以使用dis模块,以及可能很好的第三方包,这些包将允许人们以所谓的";Python VM汇编";,显式地按名称编码PythonVM操作码,然后获得一个字节对象,这些字节对象被转换为二进制形式。但通常,即使在构建新的代码对象时,也会从现有的函数中借用它,获得属性existing_function.__code__.co_code

此外,尽管FunctionTypeCodeType都可以从types模块导入,但这两个可调用函数都可以通过使用type的1参数形式从正则函数获得。给定现有的";myfunction";可以做FunctionType = type(myfunction); CodeType= type(myfunction.__code__)

您可能想要检查compilefunctools.FunctionTypefunctools.CodeTypehelp输出或文档。

最新更新