有没有办法将函数存储在列表或字典中,以便当调用索引(或键)时它会触发存储的函数?
- 2024-12-27 08:47:00
- admin 原创
- 123
问题描述:
例如,我尝试过这样的方法,但没有效果:
mydict = {
'funcList1': [foo(), bar(), goo()],
'funcList2': [foo(), goo(), bar()]}
是否存在具有这种功能的结构?
我意识到我显然可以用一堆def
语句轻松地做到这一点:
def func1():
foo()
bar()
goo()
但是我需要的语句数量变得非常繁琐,难以记住。如果能将它们很好地包装到字典中,这样我就可以时不时地检查一下其中的键,那就太好了。
解决方案 1:
函数是 Python 中的一等对象,因此您可以使用字典进行分派。例如,如果foo
和bar
是函数,并且dispatcher
是字典,就像这样。
dispatcher = {'foo': foo, 'bar': bar}
请注意,值是foo
和,bar
它们是函数对象,而不是foo()
和bar()
。
要拨打电话foo
,您只需执行dispatcher['foo']()
编辑:如果您想运行存储在列表中的多个函数,您可以做这样的事情。
dispatcher = {'foobar': [foo, bar], 'bazcat': [baz, cat]}
def fire_all(func_list):
for f in func_list:
f()
fire_all(dispatcher['foobar'])
解决方案 2:
操作函数
在 Python 中,函数是“一等对象” ——粗略地说,它们可以用于任何其他对象可以用于的所有非类型特定目的。具体来说,它们可以:
被分配给名称
=
(事实上,该def
语句是一种赋值形式)作为参数传递给(其他)函数
来自
return
函数具有可以用
.
语法检查的属性(事实上,Python 允许修改其中一些属性并分配新的属性;当然,并非所有对象都可以做到这一点)参与表达式(对函数的调用是一种表达式;函数调用语法在概念上是作用于函数的运算符)
对于当前目的来说最重要的是:存储在其他容器对象中,例如列表和字典
问题中描述的失败尝试的问题在于它们立即调用该函数。就像任何其他对象一样,Python 代码可以通过其名称引用函数(本身,作为对象)。函数的名称不包含括号;写作foo()
意味着立即调用该函数,并计算结果(无论是什么return
)。
例子
# Set up some demo functions
def foo():
print('foo function')
def bar():
print('bar function')
def goo():
print('goo function')
# Put them into containers
function_sequence = [foo, bar, goo]
function_mapping = {'foo': foo, 'bar': bar, 'goo': goo}
# Access them iteratively
for f in function_sequence:
# Each time through the loop, a different function is bound to `f`.
# `f` is thus a name for that function, which can be used to call it.
f()
# Access one by lookup, and call it.
# The lookup gives us an object which is a function;
# therefore it can be called with the function-call syntax
to_call = input('which function should i call?')
function_mapping[to_call]()
函数本身称为foo
、bar
和goo
;有了这些名称,它们就可以像其他具有名称的事物一样被操作。编写时无需使用语句中的名称,也无需使用任何名称 -就像乘法一样,foo()
它可以使用从容器中查找的值、文字值或从另一个表达式计算的值def
。
只要有一个表达式可以计算为函数对象,它就可以成为函数调用表达式的子表达式。
扩展
lambda
Python 的lambda
语法创建的对象与普通函数的类型相同。语法限制了结果函数可以执行的操作,并且它们具有特殊__name__
属性(因为在编译期间没有def
语句指定该属性);但除此之外,它们完全是可以以相同方式操作的普通函数。
def example_func():
pass
# A list containing two do-nothing functions, created with `def` and `lambda`.
dummies = [example_func, lambda: None]
# Either is usable by lookup:
dummies[0]()
dummies[1]()
# They have exactly the same type:
assert(type(dummies[0]) is type(dummies[1]))
实例方法
在 Python 3.x 中,直接在类中查找实例方法会得到一个完全普通的函数 - 没有单独的“未绑定方法”类型。调用该函数时,必须明确提供一个实例:
class Example:
def method(self):
pass
instance = Example()
# Explicit syntax for a method call via the class:
Example.method(instance)
# Following all the same patterns as before, that can be separated:
m = Example.method # the function itself
m(instance) # call it
# Again, `type(m)` is the same function type.
当然,在实例上查找实例方法会产生绑定方法。
# Calling a method via the instance:
instance.method()
# This, too, is separable:
bound = instance.method
bound()
绑定方法具有不同的类型,但提供与函数相同的可调用接口:它们使用语法调用()
。
@staticmethod
和@classmethod
这也没有什么太出人意料的事情:
class Fancy:
@staticmethod
def s(a, b, c):
pass
@classmethod
def c(cls):
pass
f = Fancy()
# For both of these, the result is the same whether lookup
# uses the class or the instance.
assert f.s is Fancy.s
# That assertion will NOT work for the classmethod, because method binding
# creates a new bound method object each time.
# But they can be verified to be functionally identical:
assert type(f.c) is type(Fancy.c)
assert f.c.__code__ is Fancy.c.__code__ # etc.
# As before, the results can be stored and used.
# As one would expect, the `cls` argument is bound for the classmethod,
# while the staticmethod expects all arguments that are listed.
fs = f.s
fs(1, 2, 3)
fc = f.c
fc()
处理争论
尝试从某个数据结构中查找可调用函数(无论是函数、lambda、绑定方法还是类……)然后调用它的代码需要能够为调用提供适当的参数。当然,如果每个可用的可调用函数都要求相同数量的参数(具有相同的类型和语义),那么这是最容易安排的。在许多情况下,需要通过预填充不会来自查找提供的公共参数的参数或通过包装它们以忽略参数来调整此类可调用函数。
def zero():
pass
def one(a):
print('a', a)
def two(a, b):
print('a', a, 'b', b)
funcs = # help!
def dispatch():
a = input('what should be the value of a?')
f = input('which func should be used?')
return funcs[f](a)
可以通过编写显式包装器或重新设计以便传递关键字参数来忽略参数(并且让被调用函数简单地忽略任何无关的**kwargs
内容)。
有关预填充参数的情况,请参阅如何在 Python 中将参数绑定到函数? 。
例如,我们可以使用 lambda 表达式进行调整(如果参数数量较多,这会变得很笨重):
funcs = {
'zero': (lambda a: zero()),
'one': one,
'two': (lambda a: two(a, 'bee'))
}
或者首先重新设计底层功能,使它们在此设置中更有用:
from functools import partial
def zero(**kwargs):
pass
def one(a):
print('a', a)
def two(b, a): # this order is more convenient for functools.partial
print('a', a, 'b', b)
funcs = {'zero': zero, 'one': one, 'two': partial(two, 'bee')}
def dispatch():
a = input('what should be the value of a?')
f = input('which func should be used?')
return funcs[f](a=a)
functools.partial
在这里特别有用,因为它避免了lambda 中的后期绑定导致的常见陷阱。例如,如果适配器使用变量而不是文字文本,并且该变量随后发生变化,则在使用该函数时会反映出这种变化(这通常是不希望的)。lambda a: two(a, 'bee')
`'bee'`dispatch
解决方案 3:
# Lets say you have 10 programs or functions:
func_list = [program_001, program_002, program_003, program_004, program_005,
program_006, program_007, program_008, program_009, program_010]
choose_program = int(input('Please Choose a program: ')) # input function number
func_list[choose_program - 1]()