如何处理Python装饰器中的'self'参数
我正在尝试设置一些装饰器,这样我就可以像下面这样使用:
class Ball(object):
def __init__(self, owner):
self.owner = owner
class Example(CommandSource):
@command
@when(lambda self, ball: ball.owner == self)
def throwBall(self, ball):
# code to throw the ball
pass
e = Example()
ball = Ball(e)
commands = e.listCommands(ball) # commands = [ 'throwBall' ]
但现在这样做不行,因为当调用验证的函数时,没有传递self
这个参数。
不过,像下面这样就可以正常工作:
class Example(CommandSource):
@command
@when(lambda ball: ball.is_round)
def throwBall(self, ball):
pass
但这也不行:
class Example(CommandSource):
def verify_owner(self, ball):
return ball.owner == self
@command
@when(verify_owner)
def throwBall(self, ball):
pass
我的目的是能够在一个类的方法上标记为“命令”,然后获取一个可以执行的有效命令列表。实际上,执行这个方法的方式没有变化。我想在这里使用装饰器,因为这似乎是最简单的方法。实际上,我是在尝试用Python建立一个小型的领域特定语言(DSL)。
但是我遇到了困难,特别是在self
参数上。以下是我目前对command
、when
和CommandSource
的实现:
def command(cmd):
if getattr(cmd, 'command', False): return cmd
def wrapper(*args, **kwargs):
return cmd(*args, **kwargs)
wrapper.validators = []
wrapper.command = True
return wrapper
def when(predicate):
def createCommand(cmd):
newcmd = command(cmd)
newcmd.validators.append(predicate)
return newcmd
return createCommand
class CommandSource(object):
def listCommands(self, *args, **kwargs):
commands = []
for command in dir(self.__class__):
func = getattr(self, command, None)
if func == None or getattr(func, 'command', False) == False:
continue
valid = True
for validator in func.validators:
if not validator(*args, **kwargs):
valid = False
break
if valid:
commands.append(command)
return commands
2 个回答
1
你需要把一个实例的引用传递给你的 validator
函数:
for validator in func.validators:
if not validator(self, *args, **kwargs):
valid = False
break
2
把你的 CommandSource
改成下面这样:
class CommandSource(object):
def listCommands(self, *args, **kwargs):
commands = []
for command in dir(self.__class__):
func = getattr(self, command, None)
if func == None or getattr(func, 'command', False) == False:
continue
for validator in func.validators:
if not validator(self, *args, **kwargs):
break
else:
commands.append(command)
return commands
然后用你最开始的代码。