Python - 我可以在不编写getter / setter的情况下包装属性赋值

时间:2014-03-30 00:02:41

标签: python observer-pattern

这个问题的目标是确定我是否可以包装设置对象的属性,不用只是编写一个setter然后包装setter。

我正在尝试实现一个Observer模式,我不想编写比我需要的更多的代码(所以我当然会写一个很长的StackOverflow问题,哈哈 - 我觉得长期的回报是值得的它)。

我开始尝试用函数包装obj.__setattr__,但它没有按照我的预期做,所以现在我想知道我是否可以包装赋值或更改对象的属性如果我不要只写一个二传手。

这就是我的尝试:

class A(object):
    pass

def wrapper(obj, func):
    def inner(*args, **kwargs):
        print "called it"
        return func(*args, **kwargs)
    return inner

Stick = A()
Stick.__setattr__ = wrapper(Stick, Stick.__setattr__)
Stick.x = 14    #does not print "called it"

如果我只是写一个setter,它就会成为一个简单的钩子,比如:

class A(object):
    def __init__(self, x):
        self.x = x

    def set_x(self, new_x):
        self.x = x

但我希望能够以这样一种方式实现Observer模式:每当obj.x因任何原因发生更改时,侦听器都会更新。如果obj.xint,例如我可以直接设置它或使用obj.x += some_int来设置它,所以我想知道是否有一种包装任何/所有设置的方法obj.x的{​​{1}}没有,例如,写obj.set_x()obj.add_to_x()obj.subtract_from_x()obj.times_x()等等。

编辑:感谢您将我指向属性,但我不知道它是如何帮助我以我目前实施的方式包装它的。

例如,如果我有一个这样的对象:

class Foo(object):
    def __init__(self, ex):
        self._x = ex
    @property
    def x(self):
        return self._x
    @x.setter
    def x(self, value):
        self._x = value

...没关系,我看到我可以直接修改函数@x.setter包裹 ,但我希望创建一些我可以在下面使用的东西(虚构的伪代码) )方式:

A.x.setter = observing_function(A, A.x.setter)

...这样当A.x更改时,observing_function会被调用并执行它将会执行的操作。

如果它完全通知答案 - 我正在制作一个'记分牌'来显示视频游戏中的中心角色的得分和生命。而不是在每个循环期间不断检查或反复设置(我现在正在做的,这似乎是过度的),我只是希望它在角色的分数/生命发生变化时实际触发,而包装器似乎是最好的方法

我希望避免这个:

def add_to_score(self, pts):
    self.score += pts
    scoreboard_object.text = self.score

...因为那对我来说是非常糟糕的,即使它有效。此外,似乎很多代码只是为两个变量建立一个观察者模式:P

这个是我更喜欢在事后包装setter的原因。我有两个不同的对象,不一定需要有关于彼此的硬编码数据;只是一个具有self.score属性的玩家对象和一个具有self.text属性的记分板对象,并且在他们之间写“胶水”当然是必要的,我希望将方法写入{{1}并且set_score对于实现Observer模式并不重要。

如果最终我不能跳过写一个二传手(或者二传手),那么我想我会继续这样做;我只是希望避免它。

实际上,虽然现在这是一个非常具体的例子,但我也在一般意义上提出要求,因为能够仅仅为变化观看属性而不是围绕每个属性进行编码以便为其做好准备似乎非常方便也许看一些其他对象的一些变化。 :/

2 个答案:

答案 0 :(得分:1)

使用属性访问进行特殊处理的特殊方法,描述符和所有其他方法仅适用于类。你不能在对象上覆盖它们。

使用事件(或发布/订阅或我们现在正在调用的任何内容)系统。让角色负责发出事件,但不是故意记住谁想要他们。

class ScoreChanged(Event):
    ...

class Character(ListenerMixin):
    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, new):
        old = self._score
        self._score = new
        self.fire_event(ScoreChanged(self, old, new))

protag = Character()
scoreboard = Scoreboard()
scoreboard.listen_event(protag, ScoreChanged, scoreboard.on_score_change)

protag.score += 10

对象必然会被纠缠在某处,但它变成了一个实现细节,而不是代码的主要部分。特别是,记分牌不一定是全局的,即使记分牌不存在,角色仍然可以正常工作。

this question上有实施和现有库的示例,但如果您正在编写游戏,那么您可能已经在使用自己内置的库我知道pyglet会这么做。

答案 1 :(得分:0)

新型类实例的特殊功能是针对其类而非实例查找的,所以:

class A(object):
    pass

def wrapper(func):
    def inner(*args, **kwargs):
        print "called it"
        return func(*args, **kwargs)
    return inner

Stick = A()
A.__setattr__ = wrapper(A.__setattr__)
Stick.x = 14    # prints "called it"
Stick.x *= 2    # also prints "called it"