【问题标题】:How to differentiate between a property being set with ___setattr__ from inside the class and outside the class?如何区分从类内部和类外部使用 __setattr__ 设置的属性?
【发布时间】:2019-06-11 19:04:22
【问题描述】:

__setattr__() 中是否有任何方法可以区分来自类内部或子/继承类的属性集,以及来自当前或子类外部的属性集?

我想从“外部”更改设置属性的工作方式,在我制作模块的情况下,我希望用户在设置属性时具有与从类内部设置属性时不同的逻辑。

例如:
i.x = 5 在从类中调用时通常应该赋值为 5,i 是它的一个实例,但是当从另一个类调用时,它应该减去 5 而不是设置为 5 .

【问题讨论】:

标签: python magic-methods setattr


【解决方案1】:

有点低级,但你可以使用inspect模块:

import inspect

class A:

    def __init__(self):
        self.__x = 0

    @property
    def x(self):
        return self.__x

    @x.setter
    def x(self, value):
        f = inspect.currentframe()
        if 'self' in f.f_back.f_locals and issubclass(type(f.f_back.f_locals['self']), A):
            print('Called from class!')
            self.__x = -value
        else:
            print('Called from outside!')
            self.__x = value

    def fn(self):
        print('Calling A.x from inside:')
        self.x = 10

class B(A):
    def __init__(self):
        super().__init__()

    def fn2(self):
        print('Calling B.x from inside:')
        self.x = 15

a = A()
print("A.x after init:", a.x)
print('Calling A.x from outside')
a.x = 10
print("A.x called from the outside:", a.x)
a.fn()
print("A.x called from the inside:", a.x)

b = B()
print("B.x after init:", b.x)
print('Calling B.x from outside')
b.x = 20
print("B.x called from the outside:", b.x)
b.fn2()
print("B.x called from the inside:", b.x)

打印:

A.x after init: 0
Calling A.x from outside
Called from outside!
A.x called from the outside: 10
Calling A.x from inside:
Called from class!
A.x called from the inside: -10
B.x after init: 0
Calling B.x from outside
Called from outside!
B.x called from the outside: 20
Calling B.x from inside:
Called from class!
B.x called from the inside: -15

【讨论】:

  • 完全符合我的要求,甚至可以完美地使用子类。
【解决方案2】:

使用属性。在类内部,您可以直接分配给基础属性。在外部,x 的分配反而减少了它。

class Foo:
    def __init__(self):
         self._x = 0

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

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

【讨论】:

  • 虽然这在技术上是一个可行的解决方案,但另一个解决方案也可以做到这一点,而无需在类中使用 _x。
  • 我认为将外部可见的名称与底层属性分开是一种特性,而不是缺点。
  • 这正是我想要完成的,而无需更改使用该属性的子类中的函数。接受的答案正是我所要求的,通过底层分离而不必实际使用不同的属性名称。
  • @laundmo:属性是继承的,所以子类可以使用普通的x_x 进行隐式赋值,就像在基类中一样。唯一需要用到_x的地方就是基类中的@x.setter函数。
  • @martineau 问题是我想直接分配 x ,而不像示例中使用的那样减去,当从类中完成时,就像从方法中一样,但是当类设置属性时不相关对于当前的,它会做减法。在提供的解决方案中,即使它是从同一类的方法中分配的,它也会进行减法,只要我使用 _x ,由于已经有数千行代码我需要更改,我不想这样做
【解决方案3】:

解决方案可能在于始终在类中使用self.__dict__,而不调用__setattr__ 方法。

例子:

class myClass:

    def __init__(self, value):
        self.__dict__['a'] = value

    def __setattr__(self, name, value):
        print("called from outside")
        if name == 'a':
            self.__dict__[name] = value - 5
        else:
            self.__dict__[name] = value


f = myClass(10)

print(f.a)
# 10

f.a = 20
print(f.a)
# called from outside
# 15

【讨论】:

  • 我已经尝试过你的解决方案,但是当在类中设置 self.a = 5 时,它仍然认为它是从外部调用的
  • 你必须在类中使用self.__dict__['a'] = 5来区分,如果你使用self.a = 5你会调用__setattr__方法
  • 好吧,另一个解决方案正是我想要的,谢谢你的尝试
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-07-13
  • 2015-06-09
  • 2014-08-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-27
相关资源
最近更新 更多