python - 字段的 setter

标签 python getter-setter

给定代码:

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

class Block:
    size = 40

    def __init__(self, x=1, y=1):
        self.pixel_position = Point(x * Block.size, y * Block.size)
        self.__block_position = Point(x, y)

    @property
    def block_position(self):
        return self.__block_position

    @block_position.setter
    def block_position(self, point):
        #I want for pixel_position to be updated whenever block position changes
        self.pixel_position.x += point.x * size
        self.pixel_position.y += point.y * size 
        self.__block_position = point

现在对于这样的代码,简单的赋值工作得很好
block = Block()
block.block_position = Point(2, 1)

但如果我想增加xblock position。。。好吧,代码不会进入setter。
block.block_position.x -= 1
# now block_position = (1, 1) but pixel_position = (80, 40) not (40, 40)

我该怎么改?
我知道我可以通过添加__pixel_position的属性来解决这个问题,该属性将在返回自身之前计算Block.size * __block_position,但这种方法不能满足我的要求-我想知道在python中如何为字段的字段设置属性。
我的问题不是要找到任何解决方案,而是要找到一个解决方案,其中更改fieldblock_position.x会将我重定向到setter/getter。

最佳答案

既然您要求它,我将提供一个示例实现,其中一个对象的属性在设置时通知其所有者,以便它与另一个对象同步。在本例中,我只考虑1D点(x),因为y的实现是完全相同的:

class NotificationProperty(property):
    def __init__(self, fget=None, fset=None, fdel=None, doc=None, notify=None):
        super().__init__(fget, fset, fdel, doc)
        self.notify = notify

    def __set_name__(self, owner, name):
        self.name = name

    def __set__(self, instance, val):
        super().__set__(instance, val)
        self.notify(instance, self.name, val)

    # Should define similar methods for other attributes
    # (see https://docs.python.org/3/howto/descriptor.html#properties).
    def setter(self, fset):
        return type(self)(self.fget, fset, self.fdel, self.__doc__, self.notify)


def notification_property(func):
    from functools import partial
    return partial(NotificationProperty, notify=func)


class SyncPoint:
    def __init__(self, x, sync_with=None):
        self.sync_with = sync_with
        self.x = x

    def sync(self, which, value):
        if self.sync_with is not None:
            obj, scale = self.sync_with
            value = int(scale * value)
            if getattr(obj, which) != value:  # Check if already synced -> avoid RecursionError.
                setattr(obj, which, value)

    @notification_property(sync)
    def x(self):
        return self._x

    @x.setter
    def x(self, val):
        self._x = val


class Block:
    size = 40

    def __init__(self, x=1):
        self.pixel_position = SyncPoint(self.size * x)
        self.block_position = SyncPoint(x, sync_with=(self.pixel_position, self.size))
        self.pixel_position.sync_with = (self.block_position, 1/self.size)


block = Block(3)
print('block_pos: ', block.block_position.x)  # block_pos:  3
print('pixel_pos: ', block.pixel_position.x)  # pixel_pos:  120

block.block_position.x -= 1
print('block_pos: ', block.block_position.x)  # block_pos:  2
print('pixel_pos: ', block.pixel_position.x)  # pixel_pos:  80

block.pixel_position.x -= Block.size
print('block_pos: ', block.block_position.x)  # block_pos:  1
print('pixel_pos: ', block.pixel_position.x)  # pixel_pos:  40

变化:通过x.setter(func)指定通知功能
下面是上述代码的一个变体,让我们指定在定义x.setter期间调用通知的函数。这可能感觉更直观,因为通知发生在__set__上,但归根结底这是一个口味问题:
from functools import partial


class notification_property(property):
    def __init__(self, fget=None, fset=None, fdel=None, doc=None, notify=None):
        super().__init__(fget, fset, fdel, doc)
        self.notify = notify

    def __set_name__(self, owner, name):
        self.name = name

    def __set__(self, instance, val):
        super().__set__(instance, val)
        self.notify(instance, self.name, val)

    # Should define similar methods for other attributes
    # (see https://docs.python.org/3/howto/descriptor.html#properties).
    def setter(self, func=None):
        return partial(type(self), self.fget, fdel=self.fdel, doc=self.__doc__, notify=(func or self.notify))


class SyncPoint:
    def __init__(self, x, sync_with=None):
        self.sync_with = sync_with
        self.x = x

    def sync(self, which, value):
        if self.sync_with is not None:
            obj, scale = self.sync_with
            value = int(scale * value)
            if getattr(obj, which) != value:  # Check if already synced -> avoid RecursionError.
                setattr(obj, which, value)

    @notification_property
    def x(self):
        return self._x

    @x.setter(sync)
    def x(self, val):
        self._x = val

关于python - 字段的 setter ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55504471/

相关文章:

Python3 - 从 json 解析 AWS 实例标签。经历古怪的行为

python - 在 Pythonscript 中获取 Netcat 的输出

python - 谷歌云功能 : Correct module needed for app_identity to get the APPLICATION_ID

python - 在 python 中按 ","拆分

java - 如何在两个不同的类中使用 getter 和 setter

Objective-C : Need advice on setting instance variables in init method

python - sklearn - 从文本文档预测多标签分类中的前 3-4 个标签

c++ - unique_ptr 对象的 getter 和 setter(依赖注入(inject))

python - 构造多方法 setter 的 Pythonic 方法

java - 如果返回值是可变的,为什么要使用 getter 和 setter?