使用字典选择要执行的函数

Posted

技术标签:

【中文标题】使用字典选择要执行的函数【英文标题】:Using a dictionary to select function to execute 【发布时间】:2012-02-28 09:55:42 【问题描述】:

我正在尝试使用函数式编程来创建一个包含键和要执行的函数的字典:

myDict=
myItems=("P1","P2","P3",...."Pn")
def myMain(key):
    def ExecP1():
        pass
    def ExecP2():
        pass
    def ExecP3():
        pass
        ...
    def ExecPn():
        pass  

现在,我看到了用于在模块中查找已定义函数的代码,我需要执行以下操作:

    for myitem in myItems:
        myDict[myitem] = ??? #to dynamically find the corresponding function

所以我的问题是,如何列出所有 Exec 函数,然后使用字典将它们分配给所需的项目?所以最后我会有myDict["P1"]() #this will call ExecP1()

我真正的问题是我有大量这些项目,我创建了一个库来处理它们,所以最终用户只需要调用 myMain("P1")

我想使用检查模块,但我不太确定该怎么做。

我避免的理由:

def ExecPn():
    pass
myDict["Pn"]=ExecPn

是我必须保护代码,因为我使用它在我的应用程序中提供脚本功能。

【问题讨论】:

为什么不使用类?如果我了解您要做什么,它可能会更具可扩展性,并且更易于实施。 @NiallByrne 因为每个 Exec 函数执行的代码非常不同。 “每个 Exec 函数执行非常不同的代码”?所以?类定义中的大多数方法都是如此。 是的,所以把它们放在一个类中是没有意义的,因为它们彼此没有关系。 您可以将函数命名为 P1 本身,而不是 ExecP1。 【参考方案1】:

简化,简化,简化:

def p1(args):
    whatever

def p2(more args):
    whatever

myDict = 
    "P1": p1,
    "P2": p2,
    ...
    "Pn": pn


def myMain(name):
    myDict[name]()

这就是你所需要的。


如果name 引用无效函数,您可以考虑使用dict.get 和可调用的默认值——

def myMain(name):
    myDict.get(name, lambda: 'Invalid')()

(从 Martijn Pieters 那里学到了这个巧妙的技巧)

【讨论】:

我知道,这是我的第一选择,但我希望最终用户具有有限的访问权限,因此用户无法在运行时更改字典中的内容。 用户可以在运行时随时更改他们想要的任何内容。是蟒蛇。他们有来源。 “这取决于用户”?那是什么意思?不是每个用户都可以调整代码?如果这就是您的意思,那么您应该花费 更少 时间来担心“访问受限”。因为——实际上——所有用户都可以调整代码,所以几乎不值得尝试添加代码来创建“受限访问”。在所有情况下都尽量简单。 @JohnnyDH 这是一个疯狂的论点。如果您确实需要使代码更“安全”,那么您必须使用编译语言——python 不是该工作的正确工具,您有责任向客户解释这一点。在代码中添加废话以尝试解决 python 固有的开放性并不是一种安全措施——它不能正确完成,尝试这样做只会让你的代码更丑陋和脆弱 @JohnnyDH:我提供软件才 30 年,所以我以前见过一些无用复杂性的例子。这是另一个。复杂性和安全性是有区别的。 “如果他们想看……”这一行是一个非常非常糟糕的例子,因为 Python 没有像 C 那样的“++”运算符。和。 “脚本功能”解释必须包含在您的问题中,这样您的问题才有意义。 cmets 中的重复要求不好【参考方案2】:

Simplify, simplify, simplify + DRY:

tasks = 
task = lambda f: tasks.setdefault(f.__name__, f)

@task
def p1():
    whatever

@task
def p2():
    whatever

def my_main(key):
    tasks[key]()

【讨论】:

那不是 DRY,那是被混淆了。 我认为将 lambda 和自定义装饰器等高级语言功能称为混淆是一种误导,至少可以这么说。 当然。这里的 lambda 只是为了简洁。不管你坚持还是改写def task(f):\n tasks[f.__name__] = f,原理都是一样的。 DRY 消除了您保持字典键与函数名称同步的责任。它还可以更轻松地发现错误,例如缺少 @task(与缺少字典项相比)。 这是优雅和干燥的,但绝对不是简化的。【参考方案3】:

并不以此为荣,但是:

def myMain(key):
    def ExecP1():
        pass
    def ExecP2():
        pass
    def ExecP3():
        pass
    def ExecPn():
        pass 
    locals()['Exec' + key]()

不过,我确实建议您将它们放在一个模块/类中,这真是太可怕了。


如果您愿意为每个函数添加一个装饰器,您可以定义一个装饰器,将每个函数添加到字典中:

def myMain(key):
    tasks = 
    
    def task(task_fn):
        tasks[task_fn.__name__] = task_fn
    
    @task
    def ExecP1():
        print(1)
    @task
    def ExecP2():
        print(2)
    @task
    def ExecP3():
        print(3)
    @task
    def ExecPn():
        print(4)
    
    tasks['Exec' + key]()

另一种选择是将所有函数放在一个类下(或不同的模块中)并使用getattr

def myMain(key):
    class Tasks:
        def ExecP1():
            print(1)
        def ExecP2():
            print(2)
        def ExecP3():
            print(3)
        def ExecPn():
            print(4)
    
    task = getattr(Tasks, 'Exec' + key)
    task()

【讨论】:

有效,但我会创建本地人的过滤版本(例如,filter(lambda x: x.startswith('Exec'), locals()))。就安全而言,locals 将始终包含在本地范围内定义的函数。 请参阅***.com/a/9168387/4909087,了解如何真正正确地做到这一点【参考方案4】:
# index dictionary by list of key names

def fn1():
    print "One"

def fn2():
    print "Two"

def fn3():
    print "Three"

fndict = "A": fn1, "B": fn2, "C": fn3

keynames = ["A", "B", "C"]

fndict[keynames[1]]()

# keynames[1] = "B", so output of this code is

# Two

【讨论】:

【参考方案5】:

这将调用字典中的方法

这是带有函数调用的python switch 语句

根据您的要求创建几个模块。 如果要传递参数,则传递。

创建一个字典,它将根据需要调用这些模块。

    def function_1(arg):
        print("In function_1")

    def function_2(arg):
        print("In function_2")

    def function_3(fileName):
        print("In function_3")
        f_title,f_course1,f_course2 = fileName.split('_')
        return(f_title,f_course1,f_course2)


    def createDictionary():

        dict = 

            1 : function_1,
            2 : function_2,
            3 : function_3,

            
        return dict

    dictionary = createDictionary()
    dictionary[3](Argument)#pass any key value to call the method

【讨论】:

以整数作为键,列表也可以代替。【参考方案6】:
#!/usr/bin/python

def thing_a(arg=None):
    print 'thing_a', arg

def thing_b(arg=None):
    print 'thing_b', arg

ghetto_switch_statement = 
    'do_thing_a': thing_a,
    'do_thing_b': thing_b


ghetto_switch_statement['do_thing_a']("It's lovely being an A")
ghetto_switch_statement['do_thing_b']("Being a B isn't too shabby either")

print "Available methods are: ", ghetto_switch_statement.keys()

【讨论】:

我知道这样做很容易,我真的这样做了,但是我的字典没有保护,我真的需要防止最终用户修改那个字典 然后使它成为一个由(我松散地使用这个术语)只写强制装饰器保护的类,并在 a 中使用 __ 前缀变量(老实说,徒劳的尝试)来隐藏实现细节用户。看看fightingquaker.com/pyanno 以获得一些灵感..【参考方案7】:

你可以使用

myDict = 
    "P1": (lambda x: function1()),
    "P2": (lambda x: function2()),
    ...,
    "Pn": (lambda x: functionn())
myItems = ["P1", "P2", ..., "Pn"]

for item in myItems:
    myDict[item]()

【讨论】:

【参考方案8】:

通常使用类来封装方法,以下是上述答案的扩展,使用默认方法,以防找不到方法。

class P:

     def p1(self):
         print('Start')

     def p2(self):
         print('Help')

     def ps(self):
         print('Settings')

     def d(self):
         print('Default function')

     myDict = 
         "start": p1,
         "help": p2,
         "settings": ps
     

     def call_it(self):
         name = 'start'
         f = lambda self, x : self.myDict.get(x, lambda x : self.d())(self)
         f(self, name)


 p = P()
 p.call_it()

【讨论】:

【参考方案9】:
class CallByName():
    
    def method1(self):
        pass

    def method2(self):
        pass

    def method3(self):
        pass

    def get_method(self, method_name):
        method = getattr(self, method_name)
        return method()


callbyname = CallByName()
method1 = callbyname.get_method(method_name)

```

【讨论】:

【参考方案10】:
def p1( ):
    print("in p1")

def p2():
    print("in p2")

myDict=
    "P1": p1,
    "P2": p2



name=input("enter P1 or P2")

我的字典名

【讨论】:

如果p1()p2()有参数呢?【参考方案11】:

你在浪费时间:

    您将要编写大量无用的代码并引入新的错误。 要执行该函数,您的用户无论如何都需要知道P1 名称。 等等等等等等

只需将所有函数放入.py 文件中即可:

# my_module.py

def f1():
    pass

def f2():
    pass

def f3():
    pass

并像这样使用它们:

import my_module

my_module.f1()
my_module.f2()
my_module.f3()

或:

from my_module import f1
from my_module import f2
from my_module import f3

f1()
f2()
f3()

这对于初学者来说应该足够了。

【讨论】:

你说得对,最终用户会知道这个名字,我非常感谢你的提示,我是一个初学者,但只是在 python 中,我非常了解一些编程原则,但我没有实现这个在个人项目中,与工作相关,客户要求一个强大的应用程序,而事实是该软件将被一群猴子使用,他们希望看到除了简单而简单的应用程序之外的所有内容...... @JohnnyDH,至少描述几个不同的场景,这些猴子接管你的应用程序。这可能会为您提供一些更适合您情况的答案。 应用程序是设备的 GUI 界面,我们在应用程序中提供“脚本”功能,但我们不提供对 python 的“完全”支持,因为这是我们的主要目标,限制访问。最终用户是卖家,只需要按照手册编写基本“脚本”的人,他们不接受专有的“语言”,他们想要基于 python 的,所以是的,用户可以一次修改字典马车线和产品将无法工作,所以是的,他们只需要重新启动,但客户会抱怨。 @JonnyDH 你的论点没有道理。 “只需一条错误线”,它们就可以像修改字典一样轻松地破坏您的主要功能。事实上,两者都不太可能发生,因为两者都不像合法的正常操作。但是这个答案表明您甚至不使用字典,您只需给他们一个公共模块,该模块包含最终用户应该调用的内容。如果您担心它们会导入其他模块并弄乱它们的内部结构,那么您无能为力。 @Ben 相信我,我非常清楚这一点,我有 8 年提供软件解决方案,这是我第一次使用 Python,我希望我可以使用其他语言的许多安全功能,但是我无法控制它,我需要使模块有点“只读”,客户要求我们给他们“证明”脚本功能尽可能安全,我们提供了一种专有语言,所以我们可以完全控制脚本编写过程,但他们拒绝了,我们无法在不影响功能灵活性的情况下控制所有内容。

以上是关于使用字典选择要执行的函数的主要内容,如果未能解决你的问题,请参考以下文章

以字典作为可选参数的函数 - Python

python基础:python循环三元运算字典文件操作

如何在 Flutter Web 中选择要上传的文件夹/文件夹?

函数与字典,可选参数和while语句结合的简单应用

在 Inno Setup 卸载程序上选择要卸载的自定义组件

易语言dll劫持注入写法