Python: 在 __init__ 中的字典中列出方法

Python: Listing methods in a dict in __init__

所以我想做的事情有点难以在标题中描述。

这是我想要做的: 在下面的代码中,我想要一些通用的方法,有人可以在 Room class(例如 Search、Loot)和 Player class(例如 quit、heal)上调用。我希望发生这种情况的方式是玩家在输入中输入他们想做的事情,然后 python 将在一个将选择与方法匹配的字典中查找该选择。

我已经通过房间的出口成功完成了这项工作。我可能可以通过制作 child class 并在其中列出方法来做到这一点,但我真的不想这样做,因为那样看起来很混乱。

当我运行下面的代码时,它会自动退出。如果我 运行 注释掉了第一个字典,我会收到一条错误消息,指出 __init__() 缺少必需的位置参数。

from textwrap import dedent
from sys import exit

class Player(object):


    actions = {
        'QUIT': quit
    }
    def __init__(self, actions):
        self.actions = actions
        # Want actions to be a list of actions like in the Room Class
        # 

    def quit(self):
        # Quits the game
        exit(0)

class Room(object):

    # Description is just a basic room description. No items needed to be added here.
    def __init__(self, desc, exits, exitdesc):
        self.desc = desc
        self.exits = exits
        self.exitdesc = exitdesc
        # Also want list of general actions for a room here.

    def enterroom(self):
        #First print the description of the room
        print(self.desc)
        #Then print the list of exits.
        if len(self.exits) > 1:
            print(f"You see the following exits:")
            for exd in self.exitdesc:
                print(self.exitdesc[exd])
        elif len(self.exits) == 1:
            print(f"There is one exit:")
            for exd in self.exitdesc:
                print(self.exitdesc[exd])
        else:
            print("There are no exits.")
        # Then allow the player to make a choice.
        self.roomactivity()

    # Here's what I mean about calling the methods via a dictionary
    def roomactivity(self):
        while True:
            print("What do you want to do?")
            choice = input("> ").upper()
            if choice in self.exits:
                self.exits[choice].enterroom()

    #And here's where I want to call actions other than directions.
            elif choice in player.actions:
                player.actions[choice]
            else:
                print("I don't understand.")

class VoidRoom(Room):
    def __init__(self):
        super().__init__(
            desc = "ONLY VOID.",
            exits = {},
            exitdesc = {})

class TestRoom(Room):
    def __init__(self):
        super().__init__(
            desc = dedent("""
                This room is only a test room.
                It has pure white walls and a pure white floor.
                Nothing is in it and you can hear faint echoes
                of some mad sounds."""),

            exitdesc = {
                'NORTH': 'To the NORTH is a black door.',
                'SOUTH': 'To the SOUTH is a high window.',
                'EAST': 'To the EAST is a red door.',
                'WEST': 'To the WEST is a blue door.'},
            exits = {
                'NORTH': void_room,
                'SOUTH': void_room,
                'EAST': void_room,
                'WEST': void_room})

void_room = VoidRoom()
test_room = TestRoom()
player = Player()

test_room.enterroom()

我希望我已经清楚地解释了这个问题。仍在学习这门语言,我现在可能已经咬得太多了。

编辑:下面的新代码:

我改变了一些东西,我将播放器命令和内容放在一个单独的 py 文件中,这样我就可以扩展播放器范围而不会弄乱 rooms.py 文件。

from textwrap import dedent
from sys import exit
from player import *
from enemies import *

# This is the base class for a room.
class Room(object):

    # Description is just a basic room description. No items needed to be added here.
    def __init__(self, desc, exits, exitdesc, inventory):
        self.desc = desc
        self.exits = exits
        self.exitdesc = exitdesc
        self.inventory = inventory

    def enterroom(self):
        #First print the description of the room
        Player.currentroom = self
        print(self.desc)
        for item in self.inventory:
            print(self.inventory[item].lootdesc)
        #Then print the list of exits.
        if len(self.exits) > 1:
            print(f"You see the following exits:")
            for exd in self.exitdesc:
                print(exd)
        elif len(self.exits) == 1:
            print(f"There is one exit:")
            for exd in self.exitdesc:
                print(exd)
        else:
            print("There are no exits.")
        # Then allow the player to make a choice.
        self.roomactivity()

    def roomactivity(self):
        while True:
            print("What do you want to do?")
            choice = input("> ").upper()
            if choice in self.exits:
                self.exits[choice]().enterroom()
            elif choice in Player.actions:
                Player.actions[choice]()
            else:
                print("I don't understand.")
                #Player.actions[choice]()

class Room3(Room):

    def __init__(self):
        super().__init__(
            desc = dedent("""
                You are in a large, dimly lit room.
                Torches sit in empty alcoves, giving off an eerie red glow.
                You hear scratching and squeaking from behind the walls."""),
            exits = {
                'NORTHEAST': StartRoom
            },
            exitdesc = [
                'A sturdy looking door leads to the NORTHEAST'
            ],
            inventory = {})



class Room1(Room):

    def __init__(self):
        super().__init__(
            desc = dedent("""
                You are in a medium sized, dimly lit room.
                Busts of dead men you don't know sit atop web-strewn pedestals."""),
            exits = {
                'EAST': StartRoom
            },
            exitdesc = [
                'An arch leading into a dimly lit hall lies to the EAST.'
            ],
            inventory = {'IRON SWORD': iron_sword}
        )



class StartRoom(Room):

    def __init__(self):
        super().__init__(
            desc = dedent("""
                PLACEHOLDER LINE 49"""),
            exits = {
                'SOUTHWEST': Room3,
                'WEST': Room1
            },
            exitdesc = [
                'An arch leading into a dimly lit room lies to the WEST',
                'A sturdy looking door lies to the SOUTHWEST'],
            inventory = {}
        )



class HelpPage(Room):

    def __init__(self):
        super().__init__(
            desc = dedent("""
                All actions will be listed in all caps
                When asked for input you may:
                QUIT the game
                Check your INVENTORY
                Check your player STATUS
                SEARCH the room
                EXAMINE an object or point of interest
                USE an item from your inventory or the room
                ATTACK a creature
                GET an item from the room
                or pick a direction (listed in caps)"""),
            exits = {},
            exitdesc = [
                'Press ENTER to return to the Main Menu'],
            inventory = []
            )

    def enterroom(self):
        print(self.desc)
        for exd in self.exitdesc:
            print(exd)
        self.roomactivity()

    def roomactivity(self):
        input()
        MainMenu.enterroom()

help_page = HelpPage()

# Main menu, lil bit different from a regular room
class MainMenu(Room):

    def __init__(self):
        super().__init__(
            desc = dedent("""
                THE DARK DUNGEON OF THE VAMPIRE KNIGHT
                A game by crashonthebeat"""),
            exits = {
                'START': StartRoom,
                'HELP': HelpPage
            },
            exitdesc = [
                'Press START to Start the Game',
                'Or go to the HELP Menu'],
            inventory = []
            )

        def enterroom(self):
            print(self.desc)
            for exd in self.exitdesc:
                print(exd)
            self.roomactivity()

        def roomactivity(self):
            while True:
                choice = input("Choose an Option: ")
                if choice in self.exits:
                    self.exits[choice]().enterroom()
                else:
                    print("I don't understand")

以及来自player.py

的相关代码
from items import *
from rooms import *

class Player(object):

    @property
    def actions(self):
        actions_map = {
            'QUIT': 'quit_',
            'STATUS': 'status',
            'INVENTORY': 'printinventory',
            'EXAMINE': 'examine',
            'USE': 'useitem',
            'SEARCH': 'searchroom',
            'GET': 'getitem',
            'CURRENTROOM': 'getcurrentroom'
        }
        return actions_map

一种方法是

class Player(object):


    actions = {
        'QUIT': 'quit'
    }

然后

def roomactivity(self):
    while True:
        [...]
        elif choice in player.actions:
            getattr(player, player.actions[choice])()

我发现了一些潜在的问题:

  1. 玩家actions字典中的quit来自哪里?它显示为某种已知名称 (variable/method/object),但您唯一一次将 quit 定义为 Player 的方法,因此 class 属性 actions 无法访问它。

  2. quit 从未真正被调用过。例如,当 player.actions[choice] 在用户输入 "QUIT" 上执行时,即使 quit 确实存在,也只是 return 它指向的任何函数。它不会调用那个函数。这是不好的。 player.actions[choice]() 会让你到达那里。

  3. 在脚本中定义变量并在 class 中引用脚本变量是 no-no。让你的 class 方法调用 VoidRoom() 或 TestRoom() 是可以的,但是让它从完全不同的命名空间引用变量 test_roomvoid_room,就没那么多了。

请参阅以下示例:

actions = {
        'QUIT': quit
    }

这不会退出您的程序。 "quit" 也是 python IDLE 中的保留字,因此不是方法的最佳选择。 Python 惯例是在末尾放一个'_'以避免与保留字冲突:quit_。我会完全删除该属性并将其设为 属性,这样您就可以在其子项中覆盖它并添加额外的功能。您牺牲了使用自定义操作来初始化玩家的能力,但是如果 class 带有相关操作,这些不是更有意义吗?

class Player(object):
    @property
    def actions(self):
        actions_map = {
            'QUIT': self.quit_
        }
        return actions_map

    def quit_(self):
        print("Quitting the game.")
        exit(0)

class PlayerThatCanSing(Player):
    @property
    def actions(self):
        default_actions = super().actions # We still want Player actions
        new_actions = {
            'SING': self.sing
        }
        combined_actions = new_actions.update(default_actions) # Now player can quit AND sing
        return combined_actions

    def sing(self):
        print("Do Re Ma Fa So La Te Do")

现在引用 player.actions['QUIT']() 调用 player.quit_,这就是您想要的。

关于#3:

class TestRoom(Room):
    def __init__(self):
        super().__init__(
            desc = dedent("""
                This room is only a test room.
                It has pure white walls and a pure white floor.
                Nothing is in it and you can hear faint echoes
                of some mad sounds."""),

            exitdesc = {
                'NORTH': 'To the NORTH is a black door.',
                'SOUTH': 'To the SOUTH is a high window.',
                'EAST': 'To the EAST is a red door.',
                'WEST': 'To the WEST is a blue door.'},
            exits = {
                'NORTH': void_room,
                'SOUTH': void_room,
                'EAST': void_room,
                'WEST': void_room})

void_room = VoidRoom()
test_room = TestRoom()
player = Player()

您在脚本 运行 时声明了 void_roomtest_room,这很好。唯一的问题是你的 classes 对你的 运行 时间变量一无所知,所以如果你想要 North,South,East 和 West 映射到 VoidRoom 的实例(这是一个 class TestRoom 知道,因为它在模块中就位于它的上方),只需直接引用 VoidRoom(),而不是 void_room。永远不要假设您的 class 对 发生在 class 之外的任何事情 都一无所知,并且还没有传递到 class 的 __init__ =].

我希望带有动作 属性 的播放器示例(在这种情况下,只需将 属性 视为一种将函数作为变量引用的方式 - 因为动作 return 是 dict,我们可以像 dict 一样对待它,而无需使用 actions() 调用方法。player.actions 将 return 字典,漂亮且可读)是有道理的,因为如果你以这种方式实现它,您可以拥有特定类型的吟游诗人,它们向下继承许多层,并且通过调用 super().actions(它们的父级 class)覆盖操作意味着即使是最具体的 DwarfBlacksmithWhoSingsInHisSpareTime class一直获取所有父动作(因为每个动作方法都调用其父动作,一直调用直到它击中 Player),所以你得到一个可以戒烟、唱歌和打铁的矮人。非常优雅,我希望它不会太混乱,因为这是一个非常酷的概念。祝你好运!