【问题标题】:How to properly set a property from an implementation of __setattr__ defined outside the class如何从类外部定义的 __setattr__ 的实现中正确设置属性
【发布时间】:2017-09-08 17:19:11
【问题描述】:

我想定义一个函数,它返回__setattr__ 的一般实现,它使属性不可设置,除非它们是属性(我通常应该测试数据描述符,但那是以后的时间)。函数如下所示:

def make_setattr(doc='Immutable attributes'):
    def __setattr__(self, name, value):
        if isinstance(getattr(type(self), name, None), property):
            super(type(self), self).__setattr__(name, value)
        raise AttributeError('Please stop trying!')
    __setattr__.__doc__ = doc
    return __setattr__

这是一个显示用法的示例:

class A:
    __setattr__ = make_setattr()

    @property
    def x(self):
        return 1

    @x.setter
    def x(self, name):
        pass


class B(A):
    pass

这似乎适用于非属性属性:

>>> a = A()
>>> a.y = 1
AttributeError: Please stop trying!
>>> a.y
AttributeError: 'A' object has no attribute 'y'

>>> b = B()
>>> b.y = 1
AttributeError: Please stop trying!
>>> b.y
AttributeError: 'B' object has no attribute 'y'

但是,对于属性属性,它非常失败:

>>> a.x = 1
AttributeError: Please stop trying!
>>> b.x = 1
RecursionError: maximum recursion depth exceeded while calling a Python object

a 上设置属性似乎完全忽略了x 是一个属性的事实。将其设置为b 会进入无限递归super(type(self), self).__setattr__(name, value)。这两个调用都好像super(type(self), self) 的结果返回了__setattr__ 的错误实现。

我试图通过删除super 的参数来缓解这种情况:super().__setattr__(name, value),但a.x = 1b.x = 1 都提高了

RuntimeError: super(): __class__ cell not found

如何在我的外部定义方法中找到并调用__setattr__ 的正确父实现?在这种情况下,如果可能的话,我想避免显式调用object.__setattr__

【问题讨论】:

  • 永远不要将type(self) 传递给super
  • 这听起来不错,但我很想知道为什么以及如何正确解决它。
  • 我不确定调用__setattr__ 是不是你想调用的——catch:是的,调用:我不太确定。您可以考虑改为调用描述符的 __set__ 方法。
  • 调用__set__ 也可以,并且无需了解课程。 code this was probably based on 在类中定义了__setattr__,所以它可以只使用super().__setattr__
  • 明确调用 __set__ 确实意味着您不会获得任何其他超类 __setattr__ 覆盖的效果。

标签: python python-3.x properties


【解决方案1】:

一个简单的解决方法是显式调用属性的 setter 函数,而不是使用 super(...).__setattr__

def make_setattr(doc='Immutable attributes'):
    def __setattr__(self, name, value):
        prop = getattr(type(self), name, None)
        if isinstance(prop, property):
            prop.__set__(self, value)
        else:
            raise AttributeError('Please stop trying!')
    __setattr__.__doc__ = doc
    return __setattr__

【讨论】:

  • 这与调用object.__setattr__ 具有相同的限制,因为它不会获取__setattr__ 的任何其他超类覆盖。
  • 这是一个很好的方法,我会考虑的。但是,正如@user2357112 指出的那样,您没有得到父__setattr__ 可能正在做的任何额外处理。
  • 如果无法通过其他方式确定 MRO 的正确起点,我认为我会将此作为我的后备。
【解决方案2】:

第一个错误:你raise AttributeError('Please stop trying!') 无论如何,即使你确实设置了属性。

第二个错误:将type(self) 传递给super 是错误的,因为当类被子类化并且type(self) 与您的预期不同时,它会中断。

您不能使用 0 参数 super,因为它所依赖的 __class__ 单元格组合仅针对按词法嵌套在类定义中的函数定义触发。您必须让您的 __setattr__ 明确知道该类,这在尚未创建该类时是很困难的。

一种选择是在类定义之后调用 make_setattr 并将类作为参数,或者使 make_setattr 成为类装饰器,但就个人而言,我可能只是在类定义中使用 def __setattr__并删除make_setattr

【讨论】:

  • 在测试代码中添加 return 肯定会有所作为。谢谢
  • 拥有make_setattr 的原因是它做了一大堆额外的配置,这些配置在我的项目中一遍又一遍地重复使用。那时复制和粘贴似乎不是一个好主意。
  • 我只会将该类作为强制参数添加到make_setattr。无论如何,将其传递给super 没有任何问题。
  • 你能想出一种从方法本身获取基类的方法吗?例如,我能破解getattr(self, '__setattr__').__class__ 之类的东西吗?
  • > 它做了一大堆额外的配置,这些配置在我的项目中一遍又一遍地重用 __setattr__ 它只是用共享代码调用你的函数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-18
  • 1970-01-01
  • 2014-11-05
  • 1970-01-01
  • 2015-06-09
  • 1970-01-01
相关资源
最近更新 更多