def ensure_type(parameter_name, the_type, type_converter_fn=None):
def decorator(fn):
def wrapped(self, *args, **kwargs):
if not type_converter_fn: # fails here
type_converter_fn = the_type
return fn(self, *args, **kwargs)
return wrapped
return decorator
当单步执行wrapped
函数时,parameter_name
和the_type
闭包变量被正确绑定,但type_converter_fn
不正确。无论是否使用该可选参数调用ensure_type
,都会发生这种情况,如果我强制使用该参数,也会发生这种情况。
为什么前两个参数有效,而第三个从未被分配?
作为参考,我在这一行上得到一个例外 - if not type_converter_fn
说它在分配之前已被引用。
答案 0 :(得分:3)
Python中的范围确定是静态的。函数内部的赋值使该函数的局部变量成为可能。在第一次分配之前,您无法访问本地变量。
中的第二行
if not type_converter_fn:
type_converter_fn = the_type
将type_converter_fn
设为wrapped()
本地,因此此代码段第一行的访问权限为UnboundLocalError
。 (顺便说一句,如果你告诉我们你得到了什么错误信息以及在哪一行,这将会变得如此简单。总是将错误信息的完整追溯复制到你的问题中 - 这会为回答者节省很多时间。)< / p>
答案 1 :(得分:2)
你的“行动水平”太复杂了。只要你有权访问它就可以处理它们。
作为
if not isinstance(parameter_name, str):
raise Exception("parameter_name must be a string")
if not type_converter_fn:
type_converter_fn = the_type
只处理“外部”参数,你应该在那里处理。
同样适用于
arg_position = list(fn.func_code.co_varnames).index(parameter_name) - 1
更深层次。
一般情况下,我会这样做(未经测试!):
def ensure_type(parameter_name, the_type, type_converter_fn=None):
if not isinstance(parameter_name, str):
raise Exception("parameter_name must be a string")
if not type_converter_fn:
type_converter_fn = the_type
def decorator(fn):
arg_position = list(fn.func_code.co_varnames).index(parameter_name) - 1 #minus once because of self
def wrapped(self, *args, **kwargs):
if arg_position > -1:
the_arg = args[arg_position]
if the_arg is not None and not isinstance(the_arg, the_type):
all_the_args = list(args)
all_the_args[arg_position] = type_converter_fn(the_arg)
args = tuple(all_the_args)
return fn(self, *args, **kwargs)
return wrapped
return decorator
编辑:我刚才看到:它仍然太复杂了。
只有在需要时才会进行换行。 if arg_position < 0
,您可以提出异常(因为整个内容毫无意义,因此请使用.find()
代替.index()
),或者您可以返回原始fn
。< / p>
def ensure_type(parameter_name, the_type, type_converter_fn=None):
if not isinstance(parameter_name, str):
raise Exception("parameter_name must be a string")
if not type_converter_fn:
type_converter_fn = the_type
def decorator(fn):
arg_position = list(fn.func_code.co_varnames).index(parameter_name) - 1 #minus once because of self
# Either use .find() here, or live with -1 and return the original function then:
if arg_position < 0: # only needed with .index()
return fn
def wrapped(self, *args, **kwargs):
the_arg = args[arg_position]
if the_arg is not None and not isinstance(the_arg, the_type):
all_the_args = list(args)
all_the_args[arg_position] = type_converter_fn(the_arg)
args = tuple(all_the_args)
return fn(self, *args, **kwargs)
return wrapped
return decorator