web-dev-qa-db-ja.com

Pythonで文字列を関数に変換する方法は?

たとえば、次のようなaddという関数がある場合

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

そして、私は文字列または入力をそのような機能に向けて変換する機能が欲しい

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

または

w='add'

関数addを参照するためにwを使用する方法はありますか?

32
Bob

ユーザー入力を取得しているため、最も安全な方法は、有効な入力を正確に定義することです。

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

'add(3,4)'などの文字列を評価する場合は、 safe eval を使用できます。

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

evalは一般に、ユーザー入力に適用されると危険です。 ___builtins___は無効であり、localsdispatcherに制限されているため、上記の方が安全です。私よりも賢い人がまだトラブルを引き起こす可能性があるかもしれませんが、私はそれを行う方法を伝えることができませんでした。

警告:eval(..., {'__builtins__':None}, dispatcher) is isunsafeユーザー入力に適用されます。悪意のあるユーザー マシンで任意の機能を実行できますevalによって文字列を評価される機会が与えられた場合。

44
unutbu

安全な方法の1つは、名前から関数にマップすることです。 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.'
19
Chris Morgan

unutbuのソリューションは私が通常使用するものですが、完全を期すために:

関数の正確な名前を指定する場合は、evalを使用できますが、人々は悪意のあることを行うことができるため、お勧めできません。

eval("add")(x,y)
10
Foo Bah

組み込み関数eval はあなたが望むことをします。ユーザーが指定した任意のコードの実行に関する通常の警告がすべて適用されます。

定義済み関数の数が有限の場合、evalを避け、代わりにルックアップテーブルを使用する必要があります(つまり、Dict)。ユーザーを信頼しないでください。

8
Oscar Korz

ユーザーがコマンド(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-2つの数値を加算して合計を出力します
(Cmd)add 5 3
8
(Cmd)quit

プロンプト(Cmd)で、helpコマンドを発行して無料で入手できます。他のコマンドはaddquitで、これらはdo_add()do_quit()関数に対応しています。

Helpコマンドは、関数のdocstringを表示することに注意してください。 docstringは、関数宣言の直後の文字列です(たとえば、do_add()を参照)。

cmdモジュールは引数の分割、解析を行わないため、自分で行う必要があります。 do_add()関数はこれを示しています。

このサンプルプログラムは、開始するのに十分なはずです。詳細については、 cmd ヘルプページを参照してください。プロンプトやプログラムの他の側面をカスタマイズするのは簡単です。

5
Hai Vu

関数リファレンスを使用するだけです:

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')

シンプルで安全。

4
Jefferson Felix

同じ問題がありました。

私がそれを処理することをお勧めする方法は、一時的な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])
3
Zero Zero

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
2
sidarcy

[重複した質問でここに来ました。私が最初に考えたのは、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()
0
user8651755