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

4 投票
2 回答
3055 浏览
提问于 2025-04-15 21:02

我正在尝试设置一些装饰器,这样我就可以像下面这样使用:

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参数上。以下是我目前对commandwhenCommandSource的实现:

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

然后用你最开始的代码。

撰写回答