Python 的 types.FunctionType 如何创建动态函数? [英] How does Python's types.FunctionType create dynamic Functions?

查看:27
本文介绍了Python 的 types.FunctionType 如何创建动态函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在努力加强我的 Python 技能,我遇到了 Saltstack 的开源代码 正在使用 types.FunctionType,我不明白发生了什么.

I'm trying to strengthen my Python skills, and I came across Open-Source code for Saltstack that is using types.FunctionType, and I don't understand what's going on.

函数 create() 具有以下代码:

Function create() has the following bit of code:

kwargs = {
    'name': vm_['name'],
    'image': get_image(conn, vm_),
    'size': get_size(conn, vm_),
    'location': get_location(conn, vm_),
}

函数 get_image 和 get_size 被传递给函数namespaced_function",如下所示:

The function get_image, and get_size are passed to a function 'namespaced_function' as so:

get_size = namespaced_function(get_size, globals())
get_image = namespaced_function(get_image, globals())

salt.utils.functools.py

具有命名空间函数

salt.utils.functools.py

Has the namespaced function

def namespaced_function(function, global_dict, defaults=None, preserve_context=False):
    '''
    Redefine (clone) a function under a different globals() namespace scope
        preserve_context:
            Allow keeping the context taken from orignal namespace,
            and extend it with globals() taken from
            new targetted namespace.
    '''
    if defaults is None:
        defaults = function.__defaults__

    if preserve_context:
        _global_dict = function.__globals__.copy()
        _global_dict.update(global_dict)
        global_dict = _global_dict
    new_namespaced_function = types.FunctionType(
        function.__code__,
        global_dict,
        name=function.__name__,
        argdefs=defaults,
        closure=function.__closure__
    )
    new_namespaced_function.__dict__.update(function.__dict__)
    return new_namespaced_function

我可以看到他们正在动态创建一个函数 get_image,但我不明白这样做的好处.为什么不直接创建函数?

I can see that they are dynamically creating a function get_image, but I don't understand the benefit of doing it this way. Why not just create the function?

推荐答案

由于 namespaced_function() 将(旧)函数 get_image() 作为参数并返回(new) get_image() 函数,更倾向于说它正在修改函数,而不是创建它.当然,它正在创建一个函数并返回它,但它与输入函数非常相似.namespaced_function() 有点像装饰器,但装饰器通常只是将整个输入函数包装在另一个调用原始函数的函数中,而不是实际上创建原来的功能.原始 get_image() 定义在 libcloudfuncs.py.

Since namespaced_function() takes the (old) function get_image() as an argument and returns the (new) get_image() function, it's more apt to say that it's modifying the function, rather than creating it. Of course, it is creating a function and returning it, but one that very closely resembles the input function. namespaced_function() works a bit like a decorator, but decorators usually just wrap the whole input function inside another function that calls the original, rather than actually creating a modified version of the original function. The original get_image() is defined at libcloudfuncs.py.

所以问题变成了,namespaced_function() 如何修改输入函数?".如果您查看 types.FunctionType() 作为其参数获取的内容,您会发现大多数值都是直接从原始函数中复制过来的.唯一不直接复制的参数是函数的全局变量.换句话说,namespaced_function() 正在创建一个新函数,该函数在各方面都与输入函数相同,只是当该函数引用全局变量时,它会在不同的位置查找它们.

So the question becomes, "How does namespaced_function() modify the input function?". If you look at what types.FunctionType() is getting as its arguments, you see that most values get copied over directly from the original function. The only argument that isn't directly copied is the function's globals. In other words, namespaced_function() is creating a new function, that is identical in every way to the input function, except that when the function refers to global variables, it looks for them in a different place.

因此,他们正在创建一个新版本的 get_image(),它也可以访问当前模块的全局变量.他们为什么这样做?好吧,要么覆盖一些全局变量,要么提供原始模块中根本不存在的变量(在这种情况下,原始函数在修改之前会被故意破坏).但我不能真的回答为什么?"除了概括地说,他们可能认为它比替代方案更容易.

So, they're creating a new version of get_image() that also has access to the current module's global variables. Why are they doing that? Well, either to override some global variables, or to provide ones that weren't there at all in the original module (in which case the original function would have been deliberately broken before the modification). But I can't really answer the "Why?" except by summarily saying that they probably judged that it was easier than the alternatives.

那么有哪些替代方案?好吧,全局变量通常不受欢迎——当它们不是常量时——因为,你可能想要改变它们.他们可以使用额外的参数而不是全局变量,但可能不想一直传递相同的参数,因为他们的大多数函数都使用它们.不过,您也可以注入参数,就像它们注入全局变量一样 - 而且它也不那么笨拙!那么他们为什么不这样做呢?好吧,再次,我不得不猜测,但他们可能有不止一个他们正在更改/提供的全局变量.

So what are the alternatives? Well, global variables are often frowned upon - when they aren't constant - because, well, you might want to change them. They could have used extra arguments instead of global variables, but probably didn't want to have to keep passing the same arguments around, when most their functions use them. You can inject arguments too, though, like they injected the globals - and it's less hacky, too! So why didn't they do that? Well, again, I kind of have to guess, but they probably have more than one global variable they're changing/providing.

自动提供参数很容易:

def original(auto1, arg1, arg2, auto2):
    print(auto1, auto2, arg1, arg2)

injected = functools.partial(original, 'auto1', auto2='auto2')

injected(1, 2) # is equal to original('auto1', 1, 2, 'auto2')

自动提供大量参数很快就会变得乏味.

Automatically providing lots of arguments gets tedious quite quickly.

当然,您可能只是让所有函数都有一个名为 eg 的参数.globals 作为第一个参数,并使用 injected = functools.partial(original, globals()).但是在函数内部,每当您需要引用这样的变量时,您都需要说 globals['variable'] 而不仅仅是 variable.

Of course, you might just make all the functions have an argument named eg. globals as the first argument, and use injected = functools.partial(original, globals()). But then inside the function, whenever you needed to refer to such a variable, you'd need to say globals['variable'] instead of just variable.

所以,总而言之,它可能有点老套,但作者可能已经判断有点老套"仍然比更冗长的替代方案要好得多.

So, in conclusion, it's perhaps a bit hacky, but the authors have probably judged that "a bit hacky" is still a lot better than the more verbose alternatives.

这篇关于Python 的 types.FunctionType 如何创建动态函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆