Как я могу передать позиционные аргументы моему функциональному объекту, установленному как значение в словаре?

1

Я пытаюсь создать боевую последовательность для моей текстовой игры, где пользователю предлагается ввести вход, а их вход используется для захвата функционального объекта из словаря. Мне трудно передать какие-либо позиционные аргументы объекту функции.

Здесь мой код, с которым я работаю:

def combat(player, enemy):
    """Our mechanism for combat between the player and the computer."""
    while player.health >= 0 and enemy.health >= 0:

        player_turn = True

        while player_turn:
            player_choice = input("It your turn! ")
            player.moves.do(player_choice)(enemy)
            player_turn = False

        print("It the enemy turn.")
        enemy.do('basic attack')

    if player.health <= 0 and enemy.health > 0:
        print("You lose.")
    else:
        print("You win.")

Вход пользователя захватывается методом do().

def do(self, ability):
    """Grabs the value of the key, determined by player input."""
    self.moves.get(ability)

moves - это словарь, который мы получаем через инициализацию. Он содержит все движения игроков.

class Combatant(object):
    """Base class that subclasses Human and Enemy classes."""
    def __init__(self, health, stamina):
        self.health = health
        self.stamina = stamina
        self.moves = {}

Здесь один из этих шагов для контекста. Я держу их довольно просто сейчас, пока не узнаю больше о проблеме, с которой я столкнулся. В конце концов, я планирую передать больше аргументов в пользу методов.

def basic_attack(self, enemy):
    """Attack that does a set amount of damage and costs no stamina."""
    damage = 5
    enemy.health -= damage

Класс Combatant содержит только эти атрибуты и add_moves do и add_moves (которые я использую для обновления содержимого словаря). Я использовал это для подкласса пары других классов, которые имеют свои собственные способности. Я пытаюсь передать позиционные аргументы объекту функции, вложенному в moves, и я хочу избежать использования операторов if-else (поэтому я могу поддерживать совместимость между моим кодом здесь и формами, которые я использую в Flask). Я попытался установить кортеж рядом с вызовом функции для do, но он возвращает это:

    File ".\combat_sequence_2.py", line 72, in combat
    player.do(player_choice)(enemy)
TypeError: 'NoneType' object is not callable

Я пытаюсь основать свой подход на обратной связи, полученной OP в этой теме, и мне не повезло найти какие-либо возможные ответы в другом месте. Я также пытаюсь использовать более простой подход, если это возможно, прежде чем попытаться поиграть с модулем functools.

Редактировать:

Мои moves словарь будет выглядеть примерно так:

{'basic attack': basic_attack}
  • 0
    Можете ли вы привести пример вашего словаря ходов?
  • 0
    dict.get имеет подпись get(key, default=None) по key default None get(key, default=None) согласно которому , если key не в словаре, по default возвращается ( None по умолчанию). Таким образом, player_choice вы передаете, отсутствует в словаре. Вместо этого я бы посоветовал использовать self.moves[ability] поскольку в этом случае вы получите более четкую ошибку. docs.python.org/3.8/library/stdtypes.html#dict.get . Вам нужно будет справиться, когда игрок player_choice в player_choice который не входит в ваши moves .
Показать ещё 8 комментариев
Теги:
python-3.x

2 ответа

-1
Лучший ответ

Вам просто нужно заполнить ходы именами функций в качестве ключей, а функция - значением, например:

self.moves['basic attack'] = basic_attack

Однако каждый раз, когда вы вводите пользовательский ввод, вы должны убедиться, что он действителен. Во- первых, вам нужно добавить оператор возврата к do:

def do(self, ability):
    """Grabs the value of the key, determined by player input."""
    return self.moves.get(ability)

Затем, когда вы вызываете do, вам нужно убедиться, что тип возврата не является None прежде чем пытаться вызвать его:

    player_choice = input("It your turn! ")
    move = player.moves.do(player_choice.strip())
    if move is not None:
        move(enemy)
        player_turn = False

Также обратите внимание, что возвращаемое значение input имеет символ новой строки в конце, поэтому я добавил вызов .strip()

  • 0
    Я до сих пор не понимаю, как использовать get когда в этом нет необходимости. используйте [ability] и поймайте KeyError . Гораздо более питонический.
  • 0
    В вашем собственном программировании вы можете сделать это, но get() , однако, является частью языка и вполне допустима
Показать ещё 2 комментария
0

В функции do отсутствует оператор return. Кроме того, не имеет смысла для функции с именем do чтобы ничего не делать. Более того, это имя do является информативным, даже если оно что-то do.

Кроме того, я не думаю, что для каждого экземпляра класса Combatant имеет смысл содержать реестр допустимых ходов. Это похоже на то, что нужно обрабатывать на уровне класса.

Следующий код обращается к этим точкам с добавленным декоратором define_move и метаклассом для обработки перемещений с классом.

def define_move(function):
    function._move = True
    return function


class CombatantMeta(type):
    def __new__(metacls, name, bases, namespace):
        combatant_class = super().__new__(metacls, name, bases, namespace)
        combatant_class.moves = {
            name: function for name, function in namespace.items()
            if hasattr(function, '_move')
        }
        return combatant_class


class Combatant(metaclass=CombatantMeta):
    def __init__(self, health, stamina):
        self.health = health
        self.stamina = stamina

    @classmethod
    def is_valid_move(cls, name):
        return name in cls.moves

    def perform_move(self, name, enemy):
        self.moves[name](self, enemy)

    @define_move
    def basic_attack(self, enemy):
        enemy.health -= 5

Здесь имена ходов всегда будут идентичны имени функции - это должно помочь с удобочитаемостью. Использование метакласса может показаться чрезмерной инженерией, но это единственный способ динамически обновлять атрибут класса при создании.

Дополнительный метод is_valid_move поможет упростить вашу основную функцию. Вместе с функцией сложения input_move, combat может быть потоком подкладки, чтобы содержать только заметную логику.

def input_move(msg):
    move = ''
    while not Combatant.is_valid_move(move):
       move = input(msg)
    return move


def combat(player, enemy):
    while player.health > 0 and enemy.health > 0:
        player_move = input_move("It your turn! ")      
        player.perform_move(player_move, enemy)

        print("It the enemy turn.")
        enemy.perform_move('basic_attack', player)

    if player.health <= 0 and enemy.health > 0:
        print("You lose.")

    else:
        print("You win.")
  • 0
    Это на самом деле касается того, на чем я собирался сосредоточиться, как только преодолею эту проблему, хотя я еще недостаточно осведомлен, чтобы прийти к вашему выводу. Спасибо вам большое! Я буду изучать это.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню