为什么在字典中传递参数的函数立即执行?

-1 投票
2 回答
46 浏览
提问于 2025-04-12 19:34

我对Python还比较陌生。

我正在写一个脚本,执行一系列的功能。

因为我不知道会有多少个功能,所以我选择了一个菜单,菜单的选项是基于一个全局字典的,字典的键代表用户的选择,而值则是调用相关函数。

在下面的代码片段中,你会看到字典里有几个func2函数。

最开始我把这些函数当作独立的函数来处理(func2func3func4等等)。但我发现它们之间有很多相似之处,遵循着一个共同的基本模式。所以,最近我学习了面向对象编程(OOP),我想把这些函数抽象成一个类。

我的想法是将每个choices的键与一个函数关联起来,对于func2,我会传递一个叫做'identifier'的参数。

这些参数已经在另一个嵌套字典dict)中定义了。所以,在调用类的构造函数并创建一个通用对象后,通过类别,我会把单个嵌套字典dict:{"CatA" : {sub_dict}, ... })传递给这个对象的方法,这个方法会计算出一个总数,并将其与所属类别一起打印出来。

#main.py
from menu import *

choices = {
 "1": func1,
 "2": func2("CategoryA"),
 "3": func2("CategoryB"),
 ...
 "7": func3,
 ...
}

def main():
 while True:
  printMenu()  # the print lines are set up like this: `num) description`
  choice = input("Enter choice: ")

  if choice == "0":
   break
  elif choice in choices:
   choices[choice]()
  else:
   print("Invalid Choice")

if __name__ == "__main__":
 main()
# menu.py
def func1():
 ...

def func2(category: str):
 from dict import extractDict
 from classfunc2 import ClassFunc2

 dict = extractDict()
 obj = ClassFunc2()
 total = obj.totNumber(**dict[category])
 obj.printResults(total,category)

def func3()
 ... 
 
def printMenu()
 ...
# classfunc2.py
class ClassFunc2:
 def __int__(self):
  return

 def totNumber(self, **dict):
  ...
  a = input(smth)
  ...
  return total

 def printResults(self, total, category):
  print()

但问题来了:当我运行脚本时,不知道什么原因,只有所有func2实例在我能从菜单中选择之前就被执行了。

我很确定我在抽象上做错了什么,但我完全不知道错在哪里。

有人能解释一下为什么会这样吗?我该如何修复这个脚本?

附注:我知道标题不太准确,但我不知道该怎么写。希望你们能给我一些建议,帮我改进标题。

2 个回答

0
choises = {
 "1": func1,
 "2": func2("CategoryA"),
 "3": func2("CategoryB"),
 ...
 "7": func3,
 ...
}

你有没有注意到字典在使用 func2func1func3 时的关键区别?

func2 的引用后面有括号 (),这意味着这个函数会被 立即 调用,返回的结果会被存储到字典里。

func1func3 后面没有括号,所以字典里存储的实际上是函数对象,这些函数会在 稍后 被调用。

如果你想让 func2func1func3 一样在稍后被调用,那就把字典定义中的括号去掉。

编辑:

如果你想让字典里也包含一些函数调用的参数,可以使用 functools.partial() 来创建一个可调用的“包”,里面包含一个函数对象和可选的一些参数:

from functools import partial

choises = {
 "1": partial(func1),
 "2": partial(func2, "CategoryA"),
 "3": partial(func2, "CategoryB"),
 ...
 "7": partial(func3),
 ...
}

然后你只需在字典中调用这个函数对象:

choises["2"]()
0

问题在于,你在定义字典的时候就立即调用了你的函数,然后把这些函数的返回值保存到了字典里。比如说,如果你的函数1返回的是None,那就意味着这个函数先被执行了,然后None这个值被保存到字典里,而不是函数本身,因为你在调用它的时候用了括号。

错误的写法是:func2("my argument")

你应该传递的是函数本身,而不是函数的输出。因此,你需要创建一个函数,用来调用你想要的函数,并传入所需的参数。

一个简单但比较啰嗦的解决方案是这样的:

def call_func_2():
    func_2("my argument")

menu_dictionary = {"2": call_func_2}

注意,之前的例子在初始化字典的时候并没有调用函数。

更好的方法是使用lambda函数。这些是小型的一行函数,可以让你在字典里包裹函数调用,而不需要在字典外定义额外的函数。在我看来,这是一种最简洁的方法,能以最有效的方式解决你的问题:

menu = {
    "1": func_1,
    "2": lambda: func_2("my param"),
    "3": lambda: func_3([1, 2, 3], "param")
}

如果你不想传递参数,包裹函数调用在(lambda)函数里其实不是必须的。

撰写回答