如何动态检查和设置实例变量值?

时间:2018-08-15 19:36:05

标签: python dynamic

我希望能够动态检查和设置实例属性值。该用例用于类似点的类,在该类中,我想要一个自定义的“ set_value”方法,该方法检查客户端尝试设置的值,然后确定从那里开始做什么。

我的目标是拥有一种方法,该方法允许一次设置实例的值(x,y),然后禁止将来对这些变量进行赋值...有效地使它们“不变”。 我知道,从技术上讲,这并不是使它们一成不变,我依靠的是绅士的同意,不要做任何有趣的事情,也不要弄乱它。

我正在尝试弄乱 setattr 方法,但无济于事,因为那样一来,该init将无法首先运行。

我的课显示如下:

class Node:
    """Represents the nodes as points with a position x, y."""

    def __init__(self, x=0, y=0, x_state=0, y_state=0):
        """Create a new node at x, y"""
        self.x = x
        self.y = y

        if self.x != 0:
            self.x_state = 1
        else:
            self.x_state = x_state

        if self.y != 0:
            self.y_state = 1
        else:
            self.y_state = y_state

    # some other dunder methods here

    def set_value(self, attr, value):
        if self.[attr]_state == 0:
            self.[attr] = value
            self.[attr]_state = 1
        else:
            raise NotImplementedError

该方法检查适当的“状态”变量,然后如果“状态”为0,则将x或y变量分配给用户给定的值,然后将“状态”的值赋予1,从而呈现出未来值分配没用。

我对动态引用属性感到困惑。

这可能吗?

在此先感谢您的帮助!

1 个答案:

答案 0 :(得分:0)

选项1(旧的,向后兼容的样式)

听起来像您想要一个namedtuple(或将其子类化)。没有更具体的说明,尚不清楚不使用它的原因是什么。

In [7]: from collections import namedtuple
point = namedtuple("Point", ["x", "y"])

In [8]: point
Out[8]: __main__.Point

In [9]: point(1, 2)
Out[9]: Point(x=1, y=2)

In [10]: p = point(1, 2)

In [11]: p.x = 1
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-11-4e1ffb2a7978> in <module>()
----> 1 p.x = 1

AttributeError: can't set attribute

在这方面,Tuples非常棒,因为它们是高效的,并且是获得不变性的“ pythonic”方式。

选项2(如果您关心Python 3.7 +)

如果您使用的是最新版本的Python(3.7+),则可以执行以下操作:

In [1]: from dataclasses import dataclass
   ...: 

In [2]: @dataclass(frozen=True)
   ...: class Point():
   ...:     x: int
   ...:     y: int
   ...: 
   ...:     

In [3]: point = Point(x=1, y=2)

In [4]: point
Out[4]: Point(x=1, y=2)

In [5]: point.x = 1
---------------------------------------------------------------------------
FrozenInstanceError                       Traceback (most recent call last)
<ipython-input-5-7f9ef2714879> in <module>()
----> 1 point.x = 1

<string> in __setattr__(self, name, value)

FrozenInstanceError: cannot assign to field 'x'

这里最酷的部分是可以轻松添加一些功能来扩展Point

选项3(狗屎和傻笑)

setattr确实是用于您的案例的相当困难的方法。这是一个有趣的实现,但是不推荐使用此代码(hacky,可能还有一些边缘情况):

class Node():

    def __init__(self, x, y):        
        self.x = x
        self.y = y
        self.frozen = True

    def __setattr__(self, key, value):
        if getattr(self, "frozen", False):
            raise ValueError("Cannot change value")
        self.__dict__[key] = value