Не могу изменить переменную класса с внешней функцией в Python - PullRequest
0 голосов
/ 14 сентября 2018

Итак, я создал игру, и я пытаюсь создать инвентарь для игрока, и я не могу изменить переменную с именем "connected_weapon".Я перепробовал все, и / или я получаю исключение или при печати инвентаря игрока показывает предыдущее оружие.Я тоже новичок, поэтому, если я что-то упустил, пожалуйста, скажите мне.

# Existing items
# Need a funtion to create a random item with random stats and a random name within a certain range.

yes_list = ["yes", "yeah", "sure", "why not"]

add_attack = "Attack:"
add_defense = "Defense:"
add_HP = "HP added:"
rarity = "Rarity"

weapon_rusty_sword = {
    "Name:": "Rusty Sword",
    add_attack: 1,
    add_defense: 0,
    rarity: 1
}

weapon_sword_wooden = {
    "Name:": "Wooden Sword",
    add_attack: 1.5,
    add_defense: 0,
    rarity: 1
}

weapon_sword_iron = {
    "Name:": "Iron Sword",
    add_attack: 2,
    add_defense: 0,
    rarity: 2
}

armor_chest_rusty_mail = {
    "Name:": "Rusty Mail",
    add_attack: 0,
    add_defense: 1,
    rarity: 1
}

armor_legs_adventurers = {
    "Name:": "Adventurer's Leggings",
    add_attack: 0,
    add_defense: 1,
    rarity: 1
}

armor_legs_rash_leggings = {
    "Name:": "Rash Leggings",
    add_attack: 0,
    add_defense: 0.5,
    rarity: 1
}

armor_head_rusty_cap = {
    "Name:": "Rusty Cap",
    add_attack: 0,
    add_defense: 0.5,
    rarity: 1
}
potion_hp = {
    "Name:": "HP Potion,",
    add_HP: 4
}

class Person:

    #global equipped_weapon
    equipped_weapon = weapon_rusty_sword
    ui_equipped_weapon = {"Equipped Weapon: {}".format(weapon_rusty_sword): ""}

    equipped_armor = {
    "Head Protection:": {"Name": armor_head_rusty_cap["Name:"], "Defense": armor_head_rusty_cap[add_defense]},
    "Chest Protection:": {"Name": armor_chest_rusty_mail["Name:"], "Defense": armor_chest_rusty_mail[add_defense]},
    "Legs Protection:": {"Name": armor_legs_rash_leggings["Name:"], "Defense": armor_legs_rash_leggings[add_defense]},
    }

    ATTACK = 1 + equipped_weapon[add_attack]
    DEFENSE = 1
    HP = 20

    gold = 10

    potions = {"Potions: ": [potion_hp["Name:"]]}
    ui_gold = {"Gold: ": gold}

    def __init__(self):
        self.name = "Cavalex"

    inv = [
        equipped_armor,
        ui_equipped_weapon,
        potions,
        ui_gold
    ]

    def see_inventory(self):
        for element in self.inv:
            for k, v in element.items():
                if type(v) == list:
                    print(k, ' '.join(v))
                else:
                    print(k, v)

#    def equip_weapon(self, new_weapon_code):
#        global equipped_weapon
#        eq_val = input(
#            "Do you want to equip this weapon? ->( {} )<-\nNote that your current weapon ( {} )will be discarded.".format(new_weapon_code["Name:"], self.equipped_weapon["Name:"]))
#        if eq_val.lower() in yes_list:
#            #del self.equipped_weapon
#            self.equipped_weapon = new_weapon_code
#            print("The weapon you had was discarded.")
#        else:
#            print("The new weapon was discarded.")





# See total amount of defense.
def defense_points():
    return sum(value["Defense"] for key, value in player.equipped_armor.items())


def add_gold(amount):
    player.gold += amount

# Adding to inventory
def new_potion_to_inv(potion):
    player.potions["Potions: "].append(potion["Name:"])


def equipp_weapon(new_weapon_code):
    # global player.equipped_weapon
    eq_val = input(
        "Do you want to equip this weapon? ->( {} )<-\nNote that your current weapon ( {} )will be discarded.".format(
            new_weapon_code["Name:"], player.equipped_weapon["Name:"]))
    if eq_val.lower() in yes_list:
        del player.equipped_weapon
        player.equipped_weapon = new_weapon_code
        print("The weapon you had was discarded.")
    else:
        print("The new weapon was discarded.")

player = Person()

# game loop
while True:
    print("Your name: ", player.name)
    player.see_inventory() # Can't put print this function, else will print "None" in the end.
    print("\nThis is your total armor defense: {}".format(defense_points()))
    print()
    new_potion_to_inv(potion_hp)
    player.see_inventory()
    print(player.ATTACK)
    print()
    print("Now we are changing your weapon.")
    equipp_weapon(weapon_sword_iron)
    print()
    print(player.ATTACK)
    player.see_inventory()



    break

, и это вывод:

C:\Users\mateu\AppData\Local\Programs\Python\Python37-32\python.exe C:/Users/mateu/PycharmProjects/Trabalhos_Python/TESTING_THINGS/testing_armor_without_weapons_and_armor.py
Your name:  Cavalex
Head Protection: {'Name': 'Rusty Cap', 'Defense': 0.5}
Chest Protection: {'Name': 'Rusty Mail', 'Defense': 1}
Legs Protection: {'Name': 'Rash Leggings', 'Defense': 0.5}
Equipped Weapon: {'Name:': 'Rusty Sword', 'Attack:': 1, 'Defense:': 0, 'Rarity': 1} 
Potions:  HP Potion,
Gold:  10

This is your total armor defense: 2.0

Head Protection: {'Name': 'Rusty Cap', 'Defense': 0.5}
Chest Protection: {'Name': 'Rusty Mail', 'Defense': 1}
Legs Protection: {'Name': 'Rash Leggings', 'Defense': 0.5}
Equipped Weapon: {'Name:': 'Rusty Sword', 'Attack:': 1, 'Defense:': 0, 'Rarity': 1} 
Potions:  HP Potion, HP Potion,
Gold:  10
2

Now we are changing your weapon.
Do you want to equip this weapon? ->( Iron Sword )<-
Note that your current weapon ( Rusty Sword )will be discarded.yes
Traceback (most recent call last):
  File "C:/Users/mateu/PycharmProjects/Trabalhos_Python/TESTING_THINGS/testing_armor_without_weapons_and_armor.py", line 158, in <module>
    equipp_weapon(weapon_sword_iron)
  File "C:/Users/mateu/PycharmProjects/Trabalhos_Python/TESTING_THINGS/testing_armor_without_weapons_and_armor.py", line 139, in equipp_weapon
    del player.equipped_weapon
AttributeError: equipped_weapon

Process finished with exit code 1

1 Ответ

0 голосов
/ 14 сентября 2018

Проблема в том, что вы сделали 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, где конструкция языка с множеством парадигм означает, что шаблон синглтона редко требуется, если когда-либо (а использование состояния уровня класса, определенного во время определения, удаляет единственное преимущество шаблона - откладывать инициализацию синглтона до тех пор, пока он не понадобится).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...