如何在Python装饰器中处理'self'参数

4
我将尝试设置一些装饰器,以便我可以做如下操作:
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' ]

目前这个不起作用,因为当调用验证lambda时,没有传递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个回答

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

使用您的第一行代码。


1
太好了,这很有效,同时感谢您在内部的for循环中使用else - Joseph Kingry

1

你需要将实例引用传递给你的验证器函数:

for validator in func.validators:
    if not validator(self, *args, **kwargs):
        valid = False
        break

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接