代码之家  ›  专栏  ›  技术社区  ›  r5ne

有没有一种方法可以在不调用描述符__set__方法的情况下设置实例变量?

  •  0
  • r5ne  · 技术社区  · 11 月前

    我创建了一个自定义描述符,并将其分配给类中的一个值。但是,我想在不使用描述符的情况下最初设置实例变量 __set__ 方法。

    class Descriptor:
        def __set_name__(self, owner, name):
            self.private_name = '_' + name
    
        def __get__(self, instance, owner=None):
            if instance is None:
                return self
            return getattr(instance, self.private_name)
    
        def __set__(self, instance, value):
            if value == 'bye':
                raise ValueError('blah')
            self.toggle(instance)
            setattr(instance, self.private_name, value)
       
        @staticmethod
        def toggle(instance):
            pass
    
    class Foo:
        bar = Descriptor()
    
        def __init__(self, bar):
            # no need to call descriptor initially for some reason ok? but is still called (unnecessary)
            self.bar = bar
    
    foo = Foo('hi')
    foo.bar = 'bye'  # descriptor needed here and with every subsequent change to bar.
    

    有没有办法解决这个问题并设置 self.bar 直接?
    我有这个问题,因为我最初使用 @property 描述符,使用时允许调用描述符方法和私有变量,这正是我所需要的。自从 @财产 是一个描述符,必须有一种合理的方法来做到这一点(尽管这与 @财产 私有变量是在类的范围内定义的,而不是像自定义描述符那样定义描述符类,自定义描述符允许在类内修改两者,但只有类范围外的公共版本(我知道私有变量不是真正的私有变量,但这无关紧要)。) 例如,使用@property的相同代码:

    class Foo:
        def __init__(self, bar):
            # no need to call setter initially
            self._bar = bar
    
        @property
        def bar(self):
            return self._bar
    
        @bar.setter
        def bar(self, value):
            self._bar = value
    
    foo = Foo('hi')
    foo.bar = 'bye'  # property setter called.
    

    然而,在这种情况下,当引入更多的实例变量时,getter和setter会占用更多的空间 toggle 对于添加的每个新的setter,必须重写原始装饰器的方法(这就是为什么我将@properties重写为自定义描述符类的原因,因为它似乎是一个使用oop的地方,不必重复自己)。

    1 回复  |  直到 11 月前
        1
  •  1
  •   user2357112    11 月前

    你想的那些范围的东西实际上都不重要。你的 property 套装 self._bar ,然后通过设置绕过它 自我_酒吧 直接。您的自定义描述符执行完全相同的操作,只是通过 setattr ,所以你 仍然 通过设置实例的 _bar 直接属性:

    class Foo
        bar = Descriptor()
        def __init__(self, bar):
            self._bar = bar