Мне нужна функция класса, которая создает новый экземпляр из существующих атрибутов экземпляра.
Мне кажется, что это фабричная функция (отсюда и мысль, что это может быть метод класса), но для этого необходимо создать экземпляр класса.
Должен/можно ли мне использовать декоратор @classmethod
?
from dataclasses import dataclass
@dataclass
class Coin:
obverse: str
reverse: str
def flip(self):
return Coin(self.reverse, self.obverse)
cent = Coin('cent_ob', 'cent_rev')
flipped_cent = cent.flip()
cent
Out[20]: Coin(obverse='cent_ob', reverse='cent_rev')
flipped_cent
Out[21]: Coin(obverse='cent_rev', reverse='cent_ob')
Это работает. Но если я использую декоратор, этого не происходит.
from dataclasses import dataclass
@dataclass
class Coin:
obverse: str
reverse: str
@classmethod
def flip(cls, self):
return cls(self.reverse, self.obverse)
cent = Coin('cent_ob', 'cent_rev')
flipped_cent = cent.flip()
TypeError: Coin.flip() missing 1 required positional argument: 'self'
С каким случаем я здесь имею дело? Есть ли другой декоратор, более подходящий для этого случая?
Моя основная цель состоит в том, чтобы иметь декоратор, явно указывающий «эй, у вас здесь есть фабричная функция».
если вы хотите, чтобы это был метод класса, вам нужно явно передать экземпляр, что-то вроде cent.flip(cent)
, или, что более разумно, поскольку это метод класса, используйте Coin.flip(cent)
, но похоже, что вам просто нужен здесь метод экземпляра.
Методы класса не получают экземпляр, если вы не передадите его явно. Так же, как первый cent.flip()
эквивалентен Coin.flip(cent)
, второй эквивалентен Coin.flip(Coin)
. Методу передается только тип cent
.
Название flip
подразумевает мутацию: текущая монета подбрасывается. Имя типа flipped
лучше будет означать, что возвращается новая монета, но в другом состоянии.
Нет необходимости делать это classmethod
, поскольку состояние одного Coin
не обязательно должно совпадать с состоянием всех созданных вами монет. Вместо этого сделайте это:
from dataclasses import dataclass
@dataclass
class Coin:
obverse: str
reverse: str
def flip(self):
return Coin(self.reverse, self.obverse)
cent = Coin('cent_ob', 'cent_rev')
flipped_cent = cent.flip()
Теперь, если вы предполагаете иметь только один экземпляр этого класса в своем коде, просто измените состояние этого синглтона. Вы даже можете перестать беспокоиться о строках и просто сохранить одну переменную, которая указывает, подброшена ли монета в противоположном от ее первоначального состояния виде:
@dataclass
class Coin:
flipped = 0
def flip(self):
self.flipped = 1 & (self.flipped + 1)
Проверьте это:
coin = Coin()
print(coin.flipped)
coin.flip()
print(coin.flipped)
coin.flip()
print(coin.flipped)
coin.flip()
print(coin.flipped)
Наконец, дополнительный комментарий: иногда, особенно в настольных играх, «подбросить монету» означает подбросить ее в воздух и случайным образом придать ей новое состояние. Возможно, вы захотите назвать этот метод примерно так: turn_over
или что-то подобное. Вам решать.
Почему вы хотите, чтобы это был метод класса?