【问题标题】:Set default (initial) value for descriptor设置描述符的默认(初始)值
【发布时间】:2020-05-11 00:06:16
【问题描述】:

假设我们得到以下代码:

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

    def __get__(self, instance, owner):
        if instance is None:
            return self
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        instance.__dict__[self.name] = value

class Person:
    fingerCount = NonNegative()
    eyesCount = NonNegative()
    def __init__(self, name):
        self.name = name

p = Person("name")
print(p.fingerCount)    <----- I want a default value printed here, instead of a KeyError
print(p.eyesCount)      <-----                 ------ | | ------

我将如何分别为 fingerCount 和 eyeCount 分配默认(初始)值?

这样我就不必这样做了:

p = Person("name")
p.fingerCount = 10
p.eyesCount = 2
print(p.fingerCount)  # output: 10   
print(p.eyesCount)    # output: 2

,但只是可以这样做:

p = Person("name")
print(p.fingerCount)  # output: 10   
print(p.eyesCount)    # output: 2

【问题讨论】:

    标签: python object properties instance descriptor


    【解决方案1】:

    将默认值传递给__init__ 方法,当instance.__dict__[self.name] 尚不存在时将其保存以供使用。

    class NonNegative():
        def __init__(self, v=0):  # You may or may not want a default default value
            self.dflt = v
    
        def __set_name__(self, owner, name):
            self.name = name
    
        def __get__(self, instance, owner):
            if instance is None:
                return self
            return instance.__dict__.get(self.name, self.dflt)
    
        def __set__(self, instance, value):
            instance.__dict__[self.name] = value
    class Person:
        fingerCount = NonNegative(10)
        eyesCount = NonNegative(2)
        def __init__(self, name):
            self.name = name
    

    如果您愿意,您可以继承 NonNegative 以提供特定于用例的默认值。

    class FingerCount(NonNegative):
        def __init__(self, v=10):
            super().__init__(v)
    
    
    class EyeCount(NonNegative):
        def __init__(self, v=2):
            super().__init__(v)
    
    
    class Person:
        fingerCount = FingerCount()
        eyecount = EyeCount()
    
    
    class Cyclops(Person):
        eyecount = EyeCount(1)
    

    如果您想要一个属性的每个实例的起始值,请通过 __init__ 中的设置器初始化该属性,就像任何其他属性一样。

    class Person:
        fingerCount = NonNegative()
        eyesCount = NonNegative()
    
        def __init__(self, fcount=10, ecount=2):
            self.fingerCount = fcount
            self.eyesCount = ecount
    

    【讨论】:

    • 你知道,我确实想过这个问题,但我认为它不会起作用,因为 Person 类的所有实例都共享同一个 NonNegative 描述符实例。 See descriptors2.py here (the typical gotcha)
    • 问题: 由于 Person 类的所有实例共享同一个 NonNegative 描述符实例,怎么可能有不同的“self.dflt”值?!
    • 你没有,只要你处理不可变的默认值,这不是问题。默认值仅在实例字典中尚不存在基础属性时使用,因此根据定义,它是属性的一部分,而不是访问属性的实例。
    【解决方案2】:

    对于更静态的硬编码解决方案

    class NonNegative:
        def __set_name__(self, owner, name):
            self.name = name
    
        def __get__(self, instance, owner):
            if instance is None:
                return self
            if isinstance(instance, Person) and instance.__dict__.get('fingerCount') is None:
                instance.fingerCount = 10
            if isinstance(instance, Person) and instance.__dict__.get('eyesCount') is None:
                instance.eyesCount = 2
            return instance.__dict__[self.name]
    
        def __set__(self, instance, value):
            instance.__dict__[self.name] = value
    
    class Person:
        fingerCount = NonNegative()
        eyesCount = NonNegative()
        def __init__(self, name):
            self.name = name
    
    p = Person("name")
    print(p.fingerCount)
    print(p.eyesCount)
    # 10
    # 2
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-03-03
      • 1970-01-01
      • 2021-10-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-02
      相关资源
      最近更新 更多