【问题标题】:Calling property setter using event handler使用事件处理程序调用属性设置器
【发布时间】:2018-03-12 04:02:42
【问题描述】:

我使用以下类来定义事件:

class Event(object):
    def __init__(self):
        self.handlers = set()

    def handle(self, handler):
        self.handlers.add(handler)
        return self

    def unhandle(self, handler):
        try:
            self.handlers.remove(handler)
        except:
            raise ValueError("Handler is not handling this event, so cannot unhandle it.")
        return self

    def fire(self, *args, **kwargs):
        for handler in self.handlers:
            print(handler)
            handler(*args, **kwargs)

    def getHandlerCount(self):
        return len(self.handlers)

    __iadd__ = handle
    __isub__ = unhandle
    __call__ = fire
    __len__  = getHandlerCount

我有一些这样定义的模型类:

class SomeModel(object):
    def __init__(self):
        self._foo = 0
        self.fooChanged = Event()

    @property
    def foo(self):
        return self._foo

    @foo.setter
    def foo(self, value):
        self._foo = value
        self.fooChanged(value)

现在,假设我想像这样更改foo

model = SomeModel()
other_model = SomeModel()

model.fooChanged += other_model.foo

model.foo = 1

model.foo = 1 之后,出现以下错误:

TypeError: 'int' 对象不可调用

现在,假设我使用这段代码来定义模型:

class SomeModel(object):
    def __init__(self):
        self._foo = 0
        self.fooChanged = Event()

    def get_foo(self):
        return self._foo

    def set_foo(self, value):
        self._foo = value
        self.fooChanged(value)

    foo = property(get_foo, set_foo)

这个代码改变foo的值:

model = SomeModel()
other_model = SomeModel()

model.fooChanged += other_model.set_foo

model.foo = 1

第二个版本运行良好,但是对我来说似乎有点不符合 Python 风格。我必须定义get_foo 方法,我想避免(因为属性可用)。这里是否有其他解决方法,所以可以运行第一个版本的代码?

注意:错误取决于self._foo 类型。如果是None,会返回错误提示NoneType不可调用,如果是字符串,错误会提示str对象不可调用。

【问题讨论】:

    标签: python event-handling


    【解决方案1】:

    经过大量挖掘,我发现这个answer 提供了非常丰富的信息,它把我推向了正确的方向。

    利用这些知识,我能够通过以下方法解决这个问题:

    model.fooChanged += lambda value: type(other_model).foo.__set__(other_model, value)
    

    model.fooChanged += lambda value: type(other_model).foo.fset(other_model, value)
    

    后一行在我看来更像 Pythonic,因为没有调用双下划线函数。

    【讨论】:

    • err 在开始写我的答案时没有看到这个,很高兴你找到了解决方案。
    • 你写的有点不同,但这也有效。谢谢。
    【解决方案2】:

    当你写model.fooChanged += other_model.foo时,我猜你真正想要的是它的setter方法,但由于other_model.foo是一个property object,你必须从它的类other_model.__class__.foo.fset中得到,写成:

    model.fooChanged += lambda value: other_model.__class__.foo.fset(other_model, value)
    

    OTOH,我认为您的第二个版本对我来说是 Pythonic,因为:

    显式优于隐式。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-03-29
      • 2012-10-12
      • 1970-01-01
      • 2016-05-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多