Проблема в том, что вы сделали equipped_weapon
атрибутом класса, а не атрибутом экземпляра. del instance.attribute
только удаляет атрибуты экземпляра, он даже не ищет атрибуты класса.
Если цель - просто изменить экземпляр, вы можете просто полностью удалить del
; следующая строка добавит (если атрибут экземпляра не существует, скрывая атрибут класса) или заменит (если атрибут экземпляра уже существует) конкретное оружие экземпляра.
Если цель состоит в том, чтобы изменить класс, вам нужно изменить назначение для работы с классом:
type(player).equipped_weapon = new_weapon_code
или явное имя класса:
Player.equipped_weapon = new_weapon_code
В любом случае, del
не является необходимым; присвоение нового значения заменяет старое, неявно отбрасывая ссылку на старое значение так же, как это делает явно del
. Было бы совершенно законно сделать del type(player).equipped_weapon
или del Player.equipped_weapon
для конкретного удаления атрибута класса, но это бессмысленно; независимо от желаемого поведения, простое присвоение атрибуту class или instance в любом случае отбросит старую ссылку.
ОБНОВЛЕНИЕ : Как вы заметили, удаление del
предотвращает исключение, но ваш вывод (с see_inventory
) никогда не меняется. Это потому, что see_inventory
смотрит на Player.inv
, который, в свою очередь, содержит ссылку на исходное значение Player.ui_equipped_weapon
. Проблема в том, что, хотя ui_equipped_weapon
и equipped_weapon
инициализируются на основе одного и того же оружия по умолчанию, они не синхронизируются друг с другом; изменение одного не влияет на другое (и, следовательно, изменение equipped_weapon
для класса или экземпляра не влияет на inv
, поэтому see_inventory
никогда не меняется).
Действительно, решение здесь состоит в том, чтобы сделать вменяемый класс. Ни один из ваших атрибутов класса не имеет смысла; все они логически атрибуты экземпляра и должны быть определены как таковые. В некоторых случаях это просто ориентированные на удобство преобразования другого атрибута; в этих случаях они вообще не должны быть атрибутами, а скорее property
s, которые динамически пересчитывают свое значение на основе другого атрибута, предотвращая их рассинхронизацию.
Вот действительно быстрое (не проверенное, может иметь небольшие опечатки) переписывание, которое перемещает все ваши оправданные атрибуты в экземпляр и преобразует остальные в свойства только для чтения, которые получают свое значение из другого атрибута экземпляра:
import copy
# Factored out to avoid repetition
def clean_armor(armor):
return {k.rstrip(':'): v
for k, v in armor.items() if k in {'Name:', 'Defense:'}}
class Person:
DEFAULT_WEAPON = weapon_rusty_sword
DEFAULT_ARMOR = {
"Head Protection:": clean_armor(armor_head_rusty_cap),
"Chest Protection:": clean_armor(armor_chest_rusty_mail),
"Legs Protection:": clean_armor(armor_legs_rash_leggings),
}
def __init__(self, name="Cavalex",
equipped_weapon=DEFAULT_WEAPON, equipped_armor=DEFAULT_ARMOR,
base_attack=1, base_defense=1,
hp=20, gold=10,
potions=(potion_hp["Name:"],)):
self.name = name
# deepcopy is defensive (so changing defaults doesn't retroactively
# alter existing instance); can be removed if you guarantee defaults
# won't be changed, or you want changes to defaults to affect
# Players still wielding default items
self.equipped_weapon = copy.deepcopy(equipped_weapon)
self.equipped_armor = copy.deepcopy(equipped_armor)
self.base_attack = int(base_attack)
self.base_defense = int(base_defense)
self.HP = int(hp)
self.gold = int(gold)
# potions only used as dictionary, but it's silly to store it as one
# Just store list of potion names in protected attribute,
# property can construct the expected dict on demand
self._potions = list(potions)
@property
def ATTACK(self):
return self.base_attack + self.equipped_weapon[add_attack]
@property
def DEFENSE(self):
return self.base_defense + sum(armor['Defense'] for armor in self.equipped_armor.values())
@property
def potions(self):
return {"Potions: ": self._potions}
@property
def ui_gold(self):
return {"Gold: ": self.gold}
@property
def ui_equipped_weapon(self):
return {"Equipped Weapon: {}".format(self.equipped_weapon): ""}
@property:
def inv(self):
return [
self.equipped_armor,
self.ui_equipped_weapon,
self.potions,
self.ui_gold,
]
def see_inventory(self):
for element in self.inv:
for k, v in element.items():
if isinstance(v, list):
print(k, ' '.join(v))
else:
print(k, v)
Это все еще имеет много сомнительных вариантов (непоследовательное именование атрибутов / свойств; странные имена клавиш в dict
s, которые, кажется, были выбраны для облегчения отображения, но делают все не отображаемыми использует больше раздражающих, используя функции верхнего уровня для вещей, которые имеют смысл только в качестве методов экземпляра и т. д.), но я сохранил эти причуды, чтобы сделать их заменой существующего класса (но со всеми атрибутами для каждого игрока, установленными на экземпляр, а не класс, и все производные атрибуты вычисляются с помощью property
s, а не устанавливаются статически, поскольку независимые статические атрибуты увеличивают шансы несоответствия базовых и производных атрибутов).
Я сохранил поведение, позволяющее вам создавать player
без предоставления каких-либо аргументов, хотя name
действительно должен быть необязательным аргументом (если только "Cavalex" не является таким распространенным именем, которое вы можете смело принимать большинством если не все игроки имеют такое имя).
Я понимаю, что довольно утомительно объяснять __init__
и определять property
s, когда вы можете просто скопировать данные из атрибута в производный атрибут, но с точки зрения кода, который вы действительно можете использовать осмысленно без чрезмерного повторения (и сопутствующего риска опечаток и логических ошибок), понять поведение и т. д., это единственный способ, который имеет смысл; class
, то есть атрибуты класса 99% (и все методы эффективно игнорируют состояние для каждого экземпляра), возможно, просто представляли собой набор глобальных функций и функций верхнего уровня, а не class
вообще.
Причина, по которой вы создаете классы, заключается в том, что вы можете создавать множество экземпляров класса с разными атрибутами, но с общим поведением; превращение класса в квази-синглтон с использованием чисто состояния уровня класса делает класс бессмысленным, по крайней мере, в Python, где конструкция языка с множеством парадигм означает, что шаблон синглтона редко требуется, если когда-либо (а использование состояния уровня класса, определенного во время определения, удаляет единственное преимущество шаблона - откладывать инициализацию синглтона до тех пор, пока он не понадобится).