61

例如,如果我有一个add名为

def add(x,y):
    return x+y

我希望能够将字符串或输入转换为直接指向该函数,例如

w=raw_input('Please input the function you want to use')

或者

w='add'

有什么方法可以使用 w 来引用函数add吗?

4

11 回答 11

67

由于您正在接受用户输入,因此最安全的方法是准确定义什么是有效输入:

dispatcher={'add':add}
w='add'
try:
    function=dispatcher[w]
except KeyError:
    raise ValueError('invalid input')

如果要评估类似 的字符串'add(3,4)',可以使用安全 eval

eval('add(3,4)',{'__builtins__':None},dispatcher)

eval一般来说,当应用于用户输入时可能会很危险。上述内容更安全,因为__builtins__已禁用并且locals仅限于dispatcher. 比我聪明的人也许还能制造麻烦,但我不能告诉你怎么做。

警告: Even应用于用户输入eval(..., {'__builtins__':None}, dispatcher)不安全的。如果有机会让他的字符串由eval.

于 2011-10-10T22:40:39.800 回答
21

一种安全的方法是从名称映射到函数。它比使用eval.

function_mappings = {
        'add': add,
}

def select_function():
    while True:
        try:
            return function_mappings[raw_input('Please input the function you want to use')]
        except KeyError:
            print 'Invalid function, try again.'
于 2011-10-10T22:43:11.890 回答
17

内置功能eval会做你想做的事。所有关于执行任意用户提供的代码的常见警告都适用。

如果有有限数量的预定义函数,您应该避免eval使用查找表,而是使用查找表(即Dict)。永远不要相信你的用户。

于 2011-10-10T22:43:02.370 回答
15

unutbu 的解决方案是我通常会使用的,但为了完整起见:

如果您要指定函数的确切名称,则可以使用eval,尽管非常不鼓励这样做,因为人们可能会做恶意的事情:

eval("add")(x,y)
于 2011-10-10T22:44:04.227 回答
14

只需使用函数参考:

def pwr(x, y):
    return x ** y

def add(x, y):
    return x + y

dispatcher = { 'pwr' : pwr, 'add' : add}

def call_func(x, y, func):
    try:
        return dispatcher[func](x, y)
    except:
        return "Invalid function"

call_func(2, 3, 'add')

简单而安全。

于 2018-07-21T12:40:24.647 回答
5

如果您正在实现一个类似 shell 的应用程序,其中用户输入一些命令(例如add),并且应用程序响应(返回总和),您可以使用该cmd模块,它为您处理所有命令交互和调度。这是一个例子:

#!/usr/bin/env python

import cmd
import shlex
import sys

class MyCmd(cmd.Cmd):
    def do_add(self, arguments):
        '''add - Adds two numbers the print the sum'''
        x, y = shlex.split(arguments)
        x, y = int(x), int(y)
        print x + y

    def do_quit(self, s):
        '''quit - quit the program'''
        sys.exit(0)

if __name__ == '__main__':
    cmd = MyCmd()
    cmd.cmdloop('type help for a list of valid commands')

这是一个示例运行会话:

$ python cmd_tryout.py
输入有效命令列表的帮助
(Cmd) help add
add - 添加两个数字打印总和
(Cmd) add 5 3
8
(Cmd) quit

在提示符(Cmd)下,您可以发出help免费获得的命令。其他命令是addquit对应于do_add()do_quit()功能。

请注意,帮助命令显示您的函数的文档字符串。文档字符串是紧跟在函数声明之后的字符串(参见do_add()示例)。

cmd模块不做任何参数拆分,解析,所以你必须自己做。该do_add()函数说明了这一点。

这个示例程序应该足以让您入门。有关更多信息,请查看cmd帮助页面。自定义程序的提示和其他方面是琐事。

于 2013-04-30T15:49:34.773 回答
3

我有同样的问题。

我建议您处理它的方式是创建一个临时 Python 文件来存储用户输入的函数。这是我在编写的用于绘制数学函数表示的程序中使用的示例:

with open("function.py",'w') as file:
    f=input('enter the function you want to draw example: 2*x+1 or e**x :\n')
    file.write("from math import *\ndef f(x):\n\treturn "+f)

这将创建一个包含我要调用的函数的文件。

接下来,您必须将您在文件中编写的函数调用到您的程序中:

from function import f

现在您可以将您的函数用作普通的 python 函数。

如果需要,您还可以使用 os.remove 删除存储函数的文件:

import os
os.remove("function.py")

为了帮助您理解,这是我绘制数学函数的程序:

import numpy
import cv2
import os
from math import *


def generate(f,a,b,min,max,functionname='noname'):
    ph=(b-a)/1920
    pv=(max-min)/1080
    picture=numpy.zeros((1080,1920))
    for i in range(0,1920):
        picture[1079-(int((f(a+(i+1)*ph)*1080/max))),i]=255
    for i in range(1920):
        picture[1079-(int((f(a+(i+1)*ph)*1080/max)))+1,i]=255
    cv2.imwrite(functionname+'.png',picture)


with open("function.py",'w') as file:
    f=input('enter the function you want to draw example: or e**x :\n')
    file.write("from math import *\ndef f(x):\n\treturn "+f)

from function import f
os.remove("function.py")
d=input('enter the interval ,min ,max and the image file name. Separate characters with spacebar. Example: 0 1 0 14 exponontielle :\n').split(" ")
generate(f,int(d[0]),int(d[1]),int(d[2]),int(d[3]),d[4])
于 2019-06-25T14:04:47.303 回答
2

我有很多情况需要在 Django 模板中将字符串与 int 进行比较,反之亦然。

我创建了一个过滤器,允许我传入函数名并使用 eval() 对其进行转换。

例子:

模板:

{% ifequal string int|convert:'str' %} do something {% endifequal %}

模板过滤器(我使用字符串来调用函数名):

@register.filter
def convert(value, funcname):
    try:
        converted = eval(funcname)(value)
        return converted
    except:
        return value
于 2013-04-30T15:04:42.430 回答
1
def add(x,y):
  print(x+y)

def subtract(x,y):
  print(x-y)

function_list = {'add', 'subtract'}

def caller(func, x, y):
  
  eval(func)(x,y) # more security exploits
  
  if func in function_list:
    eval(func)(x,y) # less security exploits

caller("add", 1, 2)
于 2021-05-17T17:18:12.773 回答
0

[我通过一个重复的问题来到这里。我的第一个想法是使用argparseshlex但我在这里没有看到,所以我将其添加为另一个选项。]

您可以argparse用来设置函数/命令的注册表并安全地解析它们的参数。这也将提供一定程度的用户友好性,例如,让您知道您何时输入了不存在的命令。

import argparse
import shlex

def hello(name):
    print('hello,', name)

def main():
    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers()

    hello_parser = subparsers.add_parser('hello')
    hello_parser.add_argument('name')
    hello_parser.set_defaults(func=hello)

    print('Enter q to quit')

    while True:
        command = input('command> ')
        command = command.strip()

        if not command:
            continue

        if command.lower() == 'q':
            break

        words = shlex.split(command)

        try:
            args = parser.parse_args(words)
        except SystemExit:
            # argparse will sys.exit() on -h and errors; prevent that
            continue

        func_args = {name: value for name, value in vars(args).items()}
        del func_args['func']
        args.func(**func_args)

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        print()
于 2017-10-29T00:46:30.947 回答
0

参考上面 John Curry 的问题……如果你想要一个处理多个参数的 Jefferson Felix 的代码版本,那么最简单的解决方案是提供一个列表中的参数,并安排每个调度的函数来检查参数计数在继续之前。

我刚刚在 Visual Studio 代码中测试的一个简单版本如下:

import math

def sin(args):
    argc = len(args)
    if (argc == 1):
        result = math.sin(args[0])
    else:
        result = None

    return(result)

def sum(args):
    argc = len(args)
    if (argc == 2):
        result = args[0] + args[1]
    else:
        result = None

    return(result)


def dot_product(args):
    argc = len(args)
    if (argc == 2):
        vector1 = args[0]
        vector2 = args[1]
        if (len(vector1) == 3 and len(vector2) == 3):
            result = (vector1[0] * vector2[0]) + (vector1[1] * vector2[1]) + (vector1[2] * vector2[2])
        else:
            result = None
    else:
        result = None

    return(result)

dispatcher = {"sin" : sin, "sum" : sum, "dot_product" : dot_product}

def call_func(dispatcher, func_name, args):
    func_list = list(dispatcher.keys())
    if (func_list.count(func_name) == 0):
        return(None)
    else:
        return(dispatcher[func_name](args))

val = call_func(dispatcher, "sin", [0.6])
print(f"Sine is : {val}")

val = call_func(dispatcher, "sum", [4, 6])
print(f"sum is : {val}")

val = call_func(dispatcher, "dot_product", [[3, 7, 2], [5, 9, 4]])
print(f"dot product is : {val}")

输出如下所示:

Sine is : 0.5646424733950354
sum is : 10
dot product is : 86

当然,更复杂的版本将包括更好的错误捕获,如果发现错误,只需返回“None”,但以上可以用作构建模板。同样,可以改进 dot_product 函数以处理任何维度的向量,但我将其作为练习留给读者...

于 2021-06-27T01:57:45.873 回答