如何使用字典来存储具有不同参数要求的多个函数

huangapple go评论62阅读模式
英文:

how to use dictionaries to store multiple functions with different argument requirements

问题

我正在尝试构建一个类似以下结构的字典(用于一个有趣的项目):

_cardLibrary = {
    1: {'name': 'Strike',
        'cost': 1,
        'type': 'attack',
        'rarity': 'common',
        'action': [deal_damage]
       },
    2: {'name': 'Defend',
        'cost': 1,
        'type': 'skill',
        'rarity': 'common',
        'action': [give_block]
       },
    3: {'name': 'Bash',
        'cost': 2,
        'type': 'attack',
        'rarity': 'common',
        'action': 
            [deal_damage,
             apply_status] 
       }
}

我想要创建一个名为 "use(i)" 的函数,它接受特定的卡片并执行存储在 'action' 中的代码。正如您在第三个元素中所看到的,'action' 应该能够包含多个函数,每个函数都需要特定的参数。

例如,deal_damage 需要3个参数,apply_status 需要4个参数。

deal_damage(caster, target, basedamage)
apply_status(caster, target, status, intensity)

是否有一种方法可以创建一个函数,调用存储在每个特定卡片内的所有函数?

我还需要一种方法来传递实际参数给每一个函数... 一些参数可以是常量值。比如,对于特定的卡片,status 始终为1,intensity 为1。然而,caster 和 target 需要在初始的 use(caster, target) 函数中定义...

到目前为止,我已经尝试遍历每个函数,但这让我无法传递具体的参数给每个函数...

我还尝试了这个链接:https://stackoverflow.com/questions/64043601/python-iterate-through-functions-with-different-arguments

但出于某种原因,我无法让代码运行起来。

如果这完全不起作用,我正在考虑将每张卡片都变成一个函数本身,但这对其他原因来说也是有问题的... 特别是关于可扩展性的问题。

有关如何处理这个问题的任何想法吗?

谢谢大家。

英文:

I'm trying to build a dictionary that looks like this (for a fun project):

_cardLibrary = {
    
    
    1: {'name': 'Strike',
        'cost': 1,
        'type': 'attack',
        'rarity': 'common',
        'action': [deal_damage]
       },
    
    2: {'name': 'Defend',
        'cost': 1,
        'type': 'skill',
        'rarity': 'common',
        'action': [give_block]
       },
    
    3: {'name': 'Bash',
        'cost': 2,
        'type': 'attack',
        'rarity': 'common',
        'action': 
            [deal_damage,
             apply_status] 

       }

}

I'd like to build a function " use(i) " which takes the specific card and executes the code stored inside of 'action'. As you can see in the third element, 'action' should be able to contain multiple functions, each of them requiring specific arguments.

For instance, deal_damage requires 3 arguments. Apply status requires 4.

deal_damage(caster, target, basedamage)
apply_status(caster, target, status, intensity)

Is there any way to create a function that calls all the functions stored inside every specific card?

I would also need some way to be able to pass the actual arguments to every one of the functions... Some of the arguments could be constant values. Like, for this specific card, status is always 1 and intensity is 1. However, target and caster really need to be defined in the initial use(caster, target) function...

So far, I've tried to iterate over every single function, however, this leaves me with the problem of not being able to pass the concrete arguments to every function...

I also tried this link: https://stackoverflow.com/questions/64043601/python-iterate-through-functions-with-different-arguments

but for some reason, iIcannot make the code work

If this doesn't work at all, I was considering the possibility of making every single card a function itself, however, this is problematic for other reasons... specifically regarding scalability.

Any ideas on how can I approach this?

thank you all

答案1

得分: 0

你可以使用functools.partial来预定义每个函数的一些参数。然后,在调用这些函数时,你只需要指定castertarget参数:

from functools import partial

def deal_damage(caster, target, basedamage):
    print(f"{caster} dealt {basedamage} damage to {target}")

def give_block(caster, target, defense):
    print(f"{caster} blocked {defense} damage from {target}")

def apply_status(caster, target, status, intensity):
    print(f"{caster} applied {intensity}x{status} to {target}")

_cardLibrary = {
    "strike": {'name': 'Strike',
        'cost': 1,
        'type': 'attack',
        'rarity': 'common',
        'actions': [partial(deal_damage, basedamage=10)]
       },
    
    "defend": {'name': 'Defend',
        'cost': 1,
        'type': 'skill',
        'rarity': 'common',
        'actions': [partial(give_block, defense=5)]
       },
    
    "bash": {'name': 'Bash',
        'cost': 2,
        'type': 'attack',
        'rarity': 'common',
        'actions': 
            [partial(deal_damage, basedamage=5),
             partial(apply_status, status='stun', intensity=5)] 
       }
}

注意,我将字典的键更改为字符串,并将每张卡片中的'action'键更改为'actions',因为每张卡片可以有多个动作。

然后,在你的游戏循环中,你可以像调用常规函数一样调用这些partial对象:

while True:
    print(f"You have {len(_cardLibrary)} cards: ")
    print(", ".join(_cardLibrary))
    u_in = input("What would you like to do? (or quit)").lower()
    if u_in == "quit":
        print("Goodbye")
        break
    
    try:
        card = _cardLibrary[u_in]
    except KeyError:
        print("Invalid choice. Please choose again")
        continue
    
    print(f"You played the {card['name']} card")
    for action in card['actions']:
        print("ACTION: ", end="")
        action(caster="You", target="Enemy")

这会产生以下输出:

You have 3 cards: 
strike, defend, bash

What would you like to do? (or quit) strike
You played the Strike card
ACTION: You dealt 10 damage to Enemy
You have 3 cards: 
strike, defend, bash

What would you like to do? (or quit) bash
You played the Bash card
ACTION: You dealt 5 damage to Enemy
ACTION: You applied 5xstun to Enemy
You have 3 cards: 
strike, defend, bash

What would you like to do? (or quit) defend
You played the Defend card
ACTION: You blocked 5 damage from Enemy
You have 3 cards: 
strike, defend, bash

What would you like to do? (or quit) quit
Goodbye
英文:

You could use functools.partial to pre-define some arguments to each function. Then, when you need to call the functions, you will only need to specify the caster and target arguments:

from functools import partial

def deal_damage(caster, target, basedamage):
    print(f"{caster} dealt {basedamage} damage to {target}")

def give_block(caster, target, defense):
    print(f"{caster} blocked {defense} damage from {target}")

def apply_status(caster, target, status, intensity):
    print(f"{caster} applied {intensity}x{status} to {target}")


_cardLibrary = {
    "strike": {'name': 'Strike',
        'cost': 1,
        'type': 'attack',
        'rarity': 'common',
        'actions': [partial(deal_damage, basedamage=10)]
       },
    
    "defend": {'name': 'Defend',
        'cost': 1,
        'type': 'skill',
        'rarity': 'common',
        'actions': [partial(give_block, defense=5)]
       },
    
    "bash": {'name': 'Bash',
        'cost': 2,
        'type': 'attack',
        'rarity': 'common',
        'actions': 
            [partial(deal_damage, basedamage=5),
             partial(apply_status, status='stun', intensity=5)] 

       }
}

Note that I changed the keys of the dictionary to strings, and the 'action' key in each card to 'actions' because there can be multiple actions per card.

Then, in your game loop, you can call these partial objects like a regular function:

while True:
    print(f"You have {len(_cardLibrary)} cards: ")
    print(", ".join(_cardLibrary))
    u_in = input("What would you like to do? (or quit) ").lower()
    if u_in == "quit":
        print("Goodbye")
        break
    
    try:
        card = _cardLibrary[u_in]
    except KeyError:
        print("Invalid choice. Please choose again")
        continue
    
    print(f"You played the {card['name']} card")
    for action in card['actions']:
        print("ACTION: ", end="")
        action(caster="You", target="Enemy")

This gives the following output:

You have 3 cards: 
strike, defend, bash

What would you like to do? (or quit) strike
You played the Strike card
ACTION: You dealt 10 damage to Enemy
You have 3 cards: 
strike, defend, bash

What would you like to do? (or quit) bash
You played the Bash card
ACTION: You dealt 5 damage to Enemy
ACTION: You applied 5xstun to Enemy
You have 3 cards: 
strike, defend, bash

What would you like to do? (or quit) defend
You played the Defend card
ACTION: You blocked 5 damage from Enemy
You have 3 cards: 
strike, defend, bash

What would you like to do? (or quit) quit
Goodbye

答案2

得分: 0

你可以让所有的函数参数列表以 ,**_ 结尾,这样它们可以接收(并忽略)它们实际上不处理的参数。 这将允许你直接将固定值参数添加到 _cardLibrary 字典中,并将其用作函数的参数。然后定义你的 use() 函数,将它自己的命名参数列表添加到字典的固定值作为覆盖:

def deal_damage(caster, target, basedamage, **_):
    # ...


def use(card, **kwargs):
    for function in _cardLibrary[card]['action']:
        function({**_cardLibrary[card], **kwargs})

用法:

use(1, caster="orc", target="human") 

# 假设 _cardLibrary 为卡片 #1 提供了 "baseDamage" 的默认值
英文:

You could make all your function parameter lists end with ,**_ which will let them receive (and ignore) parameters they're not actually processing. This will allow you to add fixed value parameters directly in the _cardLibrary dictionary and use it as parameters to the functions. Then define you use() function to add its own list of named arguments to the dictionary's fixed values as an override:

def deal_damage(caster,target,basedamage,**_):
    # ...


def use(card, **kwargs):
    for function in _cardLibrary[card]['action']:
        function(**{**_cardLibrary[card],**kwargs})

usage:

use(1,caster="orc",target="human") 

# assuming _cardLibrary supplies the default value for "baseDamage" on card #1

huangapple
  • 本文由 发表于 2023年2月18日 02:40:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/75488155.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定