python 变量 with/without 继承中的下划线

python variables with/without underscore in inheritance

我对继承后 self._value 的来源感到困惑。父class只有self.value但没有self._value

from abc import ABCMeta, abstractmethod

class Card(metaclass=ABCMeta):

    def __init__(self, value):
        self.value = value

    @property
    @abstractmethod
    def value(self):
        pass

    @value.setter
    @abstractmethod
    def value(self, other):
        pass


class BlackJackCard(Card):

    def __init__(self, value):
        super().__init__(value)

    def is_ace(self):
        return self._value == 1

    def is_face_card(self):
        """Jack = 11, Queen = 12, King = 13"""
        return 10 < self._value <= 13

    @property
    def value(self):
        if self.is_ace() == 1:
            return 1
        elif self.is_face_card():
            return 10
        else:
            return self._value

    @value.setter
    def value(self, new_value):
        if 1 <= new_value <= 13:
            self._value = new_value
        else:
            raise ValueError('Invalid card value: {}'.format(new_value)) 

但是,我 运行 这段代码发现我可以用构造函数分配的 Foo 实例化 BlackJackCard class。 self._value == self.value == Foo.

但是父 class init 方法没有 self._value...

神奇在哪里?

当您在 def value(self, new_value) 方法上使用 @value.setter 装饰器时,您是在告诉 Python 使用该方法作为setter,这意味着每当调用 self.value = something 时调用它。

因此 BlackJackCard 构造函数调用 Card 构造函数,后者表示 self.value = x 调用 value(self, x),进而执行 self._value = x。所以你的卡片最终将 value_value 属性设置为 x.


带有@装饰器的示例:

class A(metaclass=ABCMeta):
    def __init__(self, value):
        print('constructing A')
        self.x = value

class B(A):
    def __init__(self, value):
        print('constructing B')
        super().__init__(value)

    @property
    def x(self):
        print('getting x')
        return self._internalX

    @x.setter
    def x(self, new_x):
        print('setting x')
        self._internalX = new_x

# test B
b = B('X')
print('b.x = "{}"'.format(b.x))
print('b._internalX = "{}"'.format(b._internalX))

输出:

constructing B
constructing A
setting x
getting x
b.x = "X"
b._internalX = "X"

Counter-example 没有 @ 装饰器:

class A(metaclass=ABCMeta):
    def __init__(self, value):
        print('constructing A')
        self.x = value

class C(A):
    def __init__(self, value):
        print('constructing C')
        super().__init__(value)

    def x(self):
        print('not getting x')
        return self._internalX

    def x(self, new_x):
        print('not setting x')
        self._internalX = new_x

# test C
c = C('X')
print('c.x = "{}"'.format(c.x))
try:
    print('c._internalX = "{}"'.format(c._internalX))
except AttributeError as e:
    print('oops:',e)

输出:

constructing C
constructing A
c.x = "X"
oops: 'C' object has no attribute '_internalX'