我正在努力增强我的Python技能,我遇到了使用types.FunctionType的Open-Source code for Saltstack,我不明白发生了什么.
salt.cloud.clouds.cloudstack.py
kwargs = {
'name': vm_['name'],'image': get_image(conn,vm_),'size': get_size(conn,'location': get_location(conn,}
函数get_image和get_size传递给函数’namespaced_function’,如下所示:
get_size = namespaced_function(get_size,globals())
get_image = namespaced_function(get_image,globals())
salt.utils.functools.py
具有命名空间功能
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__,name=function.__name__,argdefs=defaults,closure=function.__closure__
)
new_namespaced_function.__dict__.update(function.__dict__)
return new_namespaced_function
所以问题就变成了,“namespaced_function()如何修改输入函数?”.如果您查看了什么类型.FunctionType()作为其参数,您会看到大多数值直接从原始函数复制.唯一没有直接复制的参数是函数的全局变量.换句话说,namespaced_function()正在创建一个新函数,它在输入函数的每个方面都是相同的,除了当函数引用全局变量时,它在不同的地方查找它们.
因此,他们正在创建一个新版本的get_image(),它也可以访问当前模块的全局变量.他们为什么这样做?好吧,要么覆盖一些全局变量,要么提供原始模块中根本不存在的变量(在这种情况下,原始函数在修改之前会被故意破坏).但我真的不能回答“为什么?”除了简单地说他们可能认为它比替代品更容易.
那有什么选择呢?嗯,全局变量往往不受欢迎 – 当它们不是常数时 – 因为,你可能想要改变它们.他们本可以使用额外的参数而不是全局变量,但是当他们的大多数函数使用它们时,他们可能不希望继续传递相同的参数.你也可以注入参数,就像它们注入全局变量一样 – 而且它也不那么hacky!那他们为什么不这样做呢?好吧,再次,我有点猜测,但他们可能有不止一个全局变量,他们正在改变/提供.
自动提供参数很容易:
def original(auto1,arg1,arg2,auto2):
print(auto1,auto2,arg2)
injected = functools.partial(original,'auto1',auto2='auto2')
injected(1,2) # is equal to original('auto1',1,2,'auto2')
自动提供大量参数会很快变得乏味.
当然,您可能只是让所有函数都有一个名为eg的参数. globals作为第一个参数,并使用inject = functools.partial(original,globals()).但是在函数内部,每当你需要引用这样一个变量时,你需要说全局变量[‘变量’]而不仅仅是变量.
所以,总的来说,它可能有点hacky,但作者可能认为“有点hacky”仍然比更冗长的替代方案好很多.