在装饰方法中赋值之前引用的局部变量
Posted
技术标签:
【中文标题】在装饰方法中赋值之前引用的局部变量【英文标题】:Local variable referenced before assignment in decorated method 【发布时间】:2015-04-28 14:58:34 【问题描述】:我正在创建一个装饰器,它允许我执行以下操作:
@cooldownf(lambda self, **eargs: 30 - self.level)
def method(self, **eargs):
...
这将简单地装饰方法,使其具有冷却时间。
这一切都很好,现在可以每30 - self.level
秒执行一次。
但是,我想添加一条消息,如果该方法仍处于冷却状态,则会显示该消息。
我为cooldownf
装饰器添加了一个message
参数,但随后在我的装饰器代码中的ìf message:
行中出现UnboundLocalError: local variable 'message' referenced before assignment
错误:
def cooldownf(fn, message=None):
"""Decorates a method to have a dynamic cooldown.
Decorator function for easily adding cooldown as a dynamic time
(function) into skill's methods. The function gets called when the
cooldown is needed, and the skill is passed to the function.
Args:
fn: Function to determine the cooldown of the method
message: Optional message sent if there's still cooldown left
Returns:
Decorated method with a dynamic cooldown
"""
# Create a decorator using the function and message provided
def method_decorator(method):
# Create a wrapper method
@wraps(method, assigned=WRAPPER_ASSIGNMENTS+('__dict__',), updated=())
def method_wrapper(self, **eargs):
# If the method's cooldown is over
if method_wrapper.cooldown.remaining <= 0:
# Restart the cooldown
method_wrapper.cooldown.start(1, fn(self, **eargs))
# And call the function
return method(self, **eargs)
# If there was cooldown remaining and a message is provided
if message:
# Format the provided message
message = message.format(
name=self.name,
cd=method_wrapper.cooldown.remaining,
max_cd=method_wrapper.cooldown.limit
)
# Send it to the player
SayText2(message=message).send(eargs['player'].index)
# And exit with code 3
return 3
# Create the cooldown object for the wrapper
method_wrapper.cooldown = TickRepeat(lambda: None)
# And return the wrapper
return method_wrapper
# Return the decorator
return method_decorator
这是什么原因造成的?
我可以在cooldownf
或method_decorator
内打印message
,但在method_wrapper
中添加打印会导致错误出现。
那是确切的代码,我无法在 IDLE 中使用函数复制它,这与我使用 methods 有什么关系吗?
【问题讨论】:
【参考方案1】:您在最里面的函数中分配给message
:
message = message.format(
name=self.name,
cd=method_wrapper.cooldown.remaining,
max_cd=method_wrapper.cooldown.limit
)
这个赋值使它成为一个局部变量,但是你不能在没有首先访问message
的情况下进行这个赋值。你不能用本地人做到这一点。
由于您不想修改封闭参数,因此您想在此处使用 新 本地名称:
formatted_message = message.format(
name=self.name,
cd=method_wrapper.cooldown.remaining,
max_cd=method_wrapper.cooldown.limit
)
SayText2(message=formatted_message).send(eargs['player'].index)
【讨论】:
还有什么你不知道的吗?不,但说真的,谢谢 :) 我仍然不确定它是如何发生的(消息在cooldownf
的参数中定义?),但至少我知道为什么 :) 你的解决方案有效
@MarkusMeskanen:Python 通过 binding 定义名称所属的范围;如果您分配给一个名称,或导入一个名称或创建一个具有给定名称的函数或类,那么您将在该范围内绑定该名称。如果您不指定,则 Python 必须在父范围内查找名称;全局或内置,或来自封闭范围。
@MarkusMeskanen:如果你不分配给message
,它就不是本地的,所以Python会在封闭范围内寻找它并在那里找到它。如果您确实分配给它,它是一个本地和外部范围不被咨询。也就是说,直到你用global
或nonlocal
语句告诉Python 这样做。但是您也可以将message
变量设置为message.format()
操作的输出。
@MarkusMeskanen:那么解决方案是使用不同的本地名称来保存消息的格式化版本,并将 message
从父范围中拉入,保持不变。跨度>
非常感谢@MartijnPieters,你帮了大忙 :) !以上是关于在装饰方法中赋值之前引用的局部变量的主要内容,如果未能解决你的问题,请参考以下文章
Pycharm - 禁用'局部变量'xxx'可能在分配之前被引用'