为什么在字典中传递参数的函数立即执行?
我对Python还比较陌生。
我正在写一个脚本,执行一系列的功能。
因为我不知道会有多少个功能,所以我选择了一个菜单,菜单的选项是基于一个全局字典的,字典的键代表用户的选择,而值则是调用相关函数。
在下面的代码片段中,你会看到字典里有几个func2
函数。
最开始我把这些函数当作独立的函数来处理(func2
、func3
、func4
等等)。但我发现它们之间有很多相似之处,遵循着一个共同的基本模式。所以,最近我学习了面向对象编程(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 个回答
choises = {
"1": func1,
"2": func2("CategoryA"),
"3": func2("CategoryB"),
...
"7": func3,
...
}
你有没有注意到字典在使用 func2
和 func1
、func3
时的关键区别?
对 func2
的引用后面有括号 ()
,这意味着这个函数会被 立即 调用,返回的结果会被存储到字典里。
而 func1
和 func3
后面没有括号,所以字典里存储的实际上是函数对象,这些函数会在 稍后 被调用。
如果你想让 func2
像 func1
和 func3
一样在稍后被调用,那就把字典定义中的括号去掉。
编辑:
如果你想让字典里也包含一些函数调用的参数,可以使用 functools.partial()
来创建一个可调用的“包”,里面包含一个函数对象和可选的一些参数:
from functools import partial
choises = {
"1": partial(func1),
"2": partial(func2, "CategoryA"),
"3": partial(func2, "CategoryB"),
...
"7": partial(func3),
...
}
然后你只需在字典中调用这个函数对象:
choises["2"]()
问题在于,你在定义字典的时候就立即调用了你的函数,然后把这些函数的返回值保存到了字典里。比如说,如果你的函数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)函数里其实不是必须的。