Memanggil fungsi modul dengan menggunakan namanya (string)

1735

Apa cara terbaik untuk memanggil fungsi yang diberi string dengan nama fungsi dalam program Python. Sebagai contoh, katakanlah saya memiliki modul foo, dan saya memiliki string yang isinya "bar". Apa cara terbaik untuk menelepon foo.bar()?

Saya perlu mendapatkan nilai kembali fungsi, itulah sebabnya saya tidak hanya menggunakan eval. Saya menemukan cara melakukannya dengan menggunakan evaluntuk mendefinisikan fungsi temp yang mengembalikan hasil pemanggilan fungsi itu, tapi saya berharap ada cara yang lebih elegan untuk melakukan ini.

ricree
sumber

Jawaban:

2079

Dengan asumsi modul foodengan metode bar:

import foo
method_to_call = getattr(foo, 'bar')
result = method_to_call()

Anda dapat mempersingkat garis 2 dan 3 menjadi:

result = getattr(foo, 'bar')()

jika itu lebih masuk akal untuk use case Anda.

Anda dapat menggunakan getattrcara ini pada metode terikat instance kelas, metode tingkat modul, metode kelas ... daftarnya berlanjut.

Patrick Johnmeyer
sumber
9
hasattr atau getattr dapat digunakan untuk menentukan apakah suatu fungsi didefinisikan. Saya memiliki pemetaan basis data (eventType dan handling functionName) dan saya ingin memastikan saya tidak pernah "lupa" untuk mendefinisikan event handler dengan python saya
Shaun
9
Ini berfungsi jika Anda sudah tahu nama modulnya. Namun, jika Anda ingin pengguna memberikan nama modul sebagai string, ini tidak akan berfungsi.
Blairg23
7
Jika Anda perlu menghindari NoneType bukan pengecualian yang bisa dipanggil, Anda juga bisa menggunakan bentuk tiga argumen getattr: getattr (foo, 'bar', lambda: Tidak ada). Saya minta maaf untuk pemformatan; aplikasi android stackexchange tampaknya mengerikan.
geekofalltrades
3
Lihat juga jawaban yang diberikan oleh @sastanin jika Anda hanya peduli misalnya tentang fungsi modul lokal / Anda saat ini.
NuSkooler
6
@akki Ya, jika Anda di dalam foomodul yang dapat Anda gunakan globals()untuk melakukan hal ini:methodToCall = globals()['bar']
Ben Hoyt
543
locals()["myfunction"]()

atau

globals()["myfunction"]()

penduduk setempat mengembalikan kamus dengan tabel simbol lokal saat ini. global mengembalikan kamus dengan tabel simbol global.

sastanin
sumber
53
Metode dengan global / lokal ini bagus jika metode yang Anda perlu panggil didefinisikan dalam modul yang sama dengan yang Anda panggil.
Joelmob
@ Joelmob apakah ada cara lain untuk mendapatkan objek dengan string dari root namespace?
Nick T
@NickT Saya hanya menyadari metode ini, saya tidak berpikir ada orang lain yang mengisi fungsi yang sama seperti ini, setidaknya saya tidak bisa memikirkan alasan mengapa harus ada lebih banyak.
Joelmob
Saya punya alasan untuk Anda (sebenarnya yang menuntun saya ke sini): Modul A memiliki fungsi F yang perlu memanggil fungsi dengan nama. Modul B mengimpor Modul A, dan memanggil fungsi F dengan permintaan untuk memanggil Fungsi G, yang didefinisikan dalam Modul B. Panggilan ini gagal karena, tampaknya, fungsi F hanya berjalan dengan global yang didefinisikan dalam Modul F - jadi global () ['G'] = Tidak Ada.
David Stein
338

Solusi Patrick mungkin yang terbersih. Jika Anda juga perlu mengambil modul secara dinamis, Anda dapat mengimpornya seperti:

module = __import__('foo')
func = getattr(module, 'bar')
func()
HS.
sumber
93
Saya tidak mengerti komentar terakhir itu. __import__ memiliki haknya sendiri dan kalimat berikutnya dalam dokumen yang disebutkan mengatakan: "Penggunaan langsung __import __ () jarang terjadi, kecuali dalam kasus di mana Anda ingin mengimpor modul yang namanya hanya dikenal saat runtime". Jadi: +1 untuk jawaban yang diberikan.
hoffmaje
50
Gunakan importlib.import_module. Dokumen resmi mengatakan tentang __import__: "Ini adalah fungsi lanjutan yang tidak diperlukan dalam pemrograman Python sehari-hari, tidak seperti importlib.import_module ()." docs.python.org/2/library/functions.html#__import__
glarrain
8
@ glarrain Selama Anda baik-baik saja dengan hanya mendukung 2.7 dan lebih tinggi.
Xiong Chiamiov
1
@Xiong Chaimiov, importlib.import_moduledidukung di 3.6. Lihat docs.python.org/3.6/library/…
cowlinator
7
@cowlinator Ya, 3,6 adalah bagian dari "2.7 dan lebih tinggi", baik dalam semantik versi ketat dan tanggal rilis (muncul sekitar enam tahun kemudian). Itu juga tidak ada selama tiga tahun setelah komentar saya. ;) Di cabang 3.x, modul sudah ada sejak 3.1. 2.7 dan 3.1 sekarang cukup kuno; Anda masih akan menemukan server berkeliaran yang hanya mendukung 2.6, tetapi mungkin imporportlib layak menjadi saran standar saat ini.
Xiong Chiamiov
114

Kontribusi sederhana. Jika kelas yang perlu kita contoh berada di file yang sama, kita dapat menggunakan sesuatu seperti ini:

# Get class from globals and create an instance
m = globals()['our_class']()

# Get the function (from the instance) that we need to call
func = getattr(m, 'function_name')

# Call it
func()

Sebagai contoh:

class A:
    def __init__(self):
        pass

    def sampleFunc(self, arg):
        print('you called sampleFunc({})'.format(arg))

m = globals()['A']()
func = getattr(m, 'sampleFunc')
func('sample arg')

# Sample, all on one line
getattr(globals()['A'](), 'sampleFunc')('sample arg')

Dan, jika bukan kelas:

def sampleFunc(arg):
    print('you called sampleFunc({})'.format(arg))

globals()['sampleFunc']('sample arg')
Sourcegeek
sumber
101

Diberikan string, dengan jalur python lengkap ke suatu fungsi, ini adalah bagaimana saya mendapatkan hasil dari fungsi tersebut:

import importlib
function_string = 'mypackage.mymodule.myfunc'
mod_name, func_name = function_string.rsplit('.',1)
mod = importlib.import_module(mod_name)
func = getattr(mod, func_name)
result = func()
ferrouswheel
sumber
1
Ini membantu saya. Ini versi __import__fungsi yang ringan .
Pankaj Bhambhani
Saya pikir ini adalah jawaban terbaik.
Saeid
55

Jawaban terbaik menurut FAQ pemrograman Python adalah:

functions = {'myfoo': foo.bar}

mystring = 'myfoo'
if mystring in functions:
    functions[mystring]()

Keuntungan utama dari teknik ini adalah bahwa senar tidak perlu cocok dengan nama fungsi. Ini juga merupakan teknik utama yang digunakan untuk meniru konstruksi kasus


sumber
43

Jawabannya (saya harap) tidak ada yang mau

Perilaku seperti Eval

getattr(locals().get("foo") or globals().get("foo"), "bar")()

Mengapa tidak menambahkan impor otomatis

getattr(
    locals().get("foo") or 
    globals().get("foo") or
    __import__("foo"), 
"bar")()

Jika kami memiliki kamus tambahan yang ingin kami periksa

getattr(next((x for x in (f("foo") for f in 
                          [locals().get, globals().get, 
                           self.__dict__.get, __import__]) 
              if x)),
"bar")()

Kita harus masuk lebih dalam

getattr(next((x for x in (f("foo") for f in 
              ([locals().get, globals().get, self.__dict__.get] +
               [d.get for d in (list(dd.values()) for dd in 
                                [locals(),globals(),self.__dict__]
                                if isinstance(dd,dict))
                if isinstance(d,dict)] + 
               [__import__])) 
        if x)),
"bar")()
00500005
sumber
ini dapat ditingkatkan dengan memindai pohon direktori dan drive usb pemasangan otomatis secara rekursif
wilks
27

Untuk apa nilainya, jika Anda perlu meneruskan fungsi (atau kelas) nama dan nama aplikasi sebagai string, maka Anda bisa melakukan ini:

myFnName  = "MyFn"
myAppName = "MyApp"
app = sys.modules[myAppName]
fn  = getattr(app,myFnName)
trubliphone
sumber
Sedikit lebih generik adalahhandler = getattr(sys.modules[__name__], myFnName)
lony
21

Coba ini. Meskipun ini masih menggunakan eval, ia hanya menggunakannya untuk memanggil fungsi dari konteks saat ini . Kemudian, Anda memiliki fungsi nyata untuk digunakan sesuai keinginan.

Manfaat utama bagi saya dari ini adalah bahwa Anda akan mendapatkan kesalahan terkait eval pada saat memanggil fungsi. Maka Anda hanya akan mendapatkan kesalahan terkait fungsi saat menelepon.

def say_hello(name):
    print 'Hello {}!'.format(name)

# get the function by name
method_name = 'say_hello'
method = eval(method_name)

# call it like a regular function later
args = ['friend']
kwargs = {}
method(*args, **kwargs)
tvt173
sumber
1
Ini akan berisiko. string dapat memiliki apa saja dan eval akan berakhir eval-ling tanpa pertimbangan.
iankit
4
Tentu, Anda harus memperhatikan konteks tempat Anda menggunakannya, apakah ini sesuai atau tidak, mengingat risiko-risiko itu.
tvt173
1
Suatu fungsi seharusnya tidak bertanggung jawab untuk memvalidasi parameternya - itulah tugas dari fungsi yang berbeda. Mengatakan bahwa berisiko menggunakan eval dengan string mengatakan bahwa penggunaan setiap fungsi berisiko.
red777
Anda tidak boleh menggunakan evalkecuali sangat diperlukan. getattr(__module__, method_name)adalah pilihan yang jauh lebih baik dalam konteks ini.
moi
14

tidak ada yang disarankan membantu saya. Saya memang menemukan ini.

<object>.__getattribute__(<string name>)(<params>)

Saya menggunakan python 2.66

Semoga ini membantu

Natdrip
sumber
13
Dalam aspek apa ini lebih baik daripada getattr ()?
V13
1
Apa yang saya inginkan. Bekerja seperti pesona! Sempurna!! self.__getattribute__('title')sama denganself.title
ioaniatr
self.__getattribute__('title')tidak bekerja dalam kasus apapun (tidak tahu mengapa) setelah semua, tetapi func = getattr(self, 'title'); func();tidak. Jadi, mungkin lebih baik menggunakan getattr()bukannya
ioaniatr
10
Bisakah orang yang tidak tahu python tolong berhenti mengangkat sampah ini? Gunakan getattrsebagai gantinya.
Aran-Fey
6

Saat pertanyaan ini Bagaimana memanggil metode secara dinamis di dalam kelas menggunakan penugasan metode-nama ke variabel [duplikat] yang ditandai sebagai duplikat seperti ini, saya memposting jawaban terkait di sini:

Skenarionya adalah, sebuah metode di kelas yang ingin memanggil metode lain di kelas yang sama secara dinamis, saya telah menambahkan beberapa detail ke contoh asli yang menawarkan beberapa skenario dan kejelasan yang lebih luas:

class MyClass:
    def __init__(self, i):
        self.i = i

    def get(self):
        func = getattr(MyClass, 'function{}'.format(self.i))
        func(self, 12)   # This one will work
        # self.func(12)    # But this does NOT work.


    def function1(self, p1):
        print('function1: {}'.format(p1))
        # do other stuff

    def function2(self, p1):
        print('function2: {}'.format(p1))
        # do other stuff


if __name__ == "__main__":
    class1 = MyClass(1)
    class1.get()
    class2 = MyClass(2)
    class2.get()

Output (Python 3.7.x)

function1: 12

function2: 12

Serjik
sumber
-7

Ini adalah jawaban yang sederhana, ini akan memungkinkan Anda untuk menghapus layar misalnya. Ada dua contoh di bawah ini, dengan eval dan exec, yang akan mencetak 0 di bagian atas setelah membersihkan (jika Anda menggunakan Windows, perubahan clearuntuk cls, Linux dan Mac pengguna meninggalkan seperti misalnya) atau hanya jalankan, masing-masing.

eval("os.system(\"clear\")")
exec("os.system(\"clear\")")
File Angka
sumber
3
Ini bukan yang diminta op.
Tuncay Göncüoğlu