переменные Python с / без подчеркивания в наследовании - PullRequest
1 голос
/ 12 июня 2019

Я запутался в том, откуда берется self._value после наследования.Родительский класс имеет только 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)) 

Однако я запустил этот код и обнаружил, что могу создать экземпляр класса BlackJackCard с Foo, назначенным через конструктор.self._value == self.value == Foo.

Но метод родительского класса init не имеет self._value ...

Где магия

1 Ответ

1 голос
/ 14 июня 2019

Когда вы используете декоратор @value.setter в своем методе def value(self, new_value), вы говорите Python использовать этот метод в качестве метода установки, что означает его вызов при каждом вызове self.value = something.

Таким образом, конструктор BlackJackCard вызывает конструктор Card, который говорит self.value = x, который вызывает value(self, x), который, в свою очередь, выполняет self._value = x.Таким образом, ваша карта имеет атрибуты value и _value, установленные на x.


Пример с @ decorators:

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"

Контр-пример БЕЗ @ декораторов:

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'
...