Bir modülün işlevini adını (dize) kullanarak çağırma


1734

Bir Python programında işlevin adıyla bir dize verilen bir işlevi çağırmanın en iyi yolu nedir? Örneğin, foobir modülüm olduğunu ve içeriği olan bir dizem olduğunu varsayalım "bar". Aramanın en iyi yolu nedir foo.bar()?

Fonksiyonun dönüş değerini almam gerekiyor, bu yüzden sadece kullanmıyorum eval. evalBu işlev çağrısının sonucunu döndüren bir geçici işlev tanımlamak için kullanarak nasıl yapılacağını anladım , ancak bunu yapmanın daha zarif bir yolu olduğunu umuyorum.

Yanıtlar:


2078

Modül fooyöntemiyle varsayılırsa bar:

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

2. ve 3. satırları aşağıdakileri kısaltabilirsiniz:

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

kullanım durumunuz için daha mantıklıysa.

Sen kullanabilirsiniz getattrsınıf örneği bağımlı yöntemleri, modül düzeyinde yöntemden, sınıf yöntemleri bu şekilde ... liste uzayıp gidiyor.


9
bir işlevin tanımlanıp tanımlanmadığını belirlemek için hasattr veya getattr kullanılabilir. Ben bir veritabanı eşleme (eventType ve taşıma functionName) vardı ve ben benim python
Shaun

9
Modül adını zaten biliyorsanız bu işe yarar. Ancak, kullanıcının modül adını dize olarak vermesini istiyorsanız, bu çalışmaz.
Blairg23

7
NoneType çağrılabilir bir istisnadan kaçınmanız gerekiyorsa, getattr: getattr (foo, 'bar', lambda: None) şeklindeki üç bağımsız değişken biçimini de kullanabilirsiniz. Biçimlendirme için özür dilerim; stackexchange android uygulaması görünüşte korkunç.
geekofalltrades

3
Ayrıca yalnızca yerel / akım modülünüzün işlevlerini önemsiyorsanız @sastanin tarafından verilen yanıta bakın.
NuSkooler

6
sen eğer Evet @akki, içindefoo modül kullanabilirsiniz globals(): Bunu yapmak içinmethodToCall = globals()['bar']
Ben Hoyt

543
locals()["myfunction"]()

veya

globals()["myfunction"]()

locals , geçerli bir yerel sembol tablosuna sahip bir sözlük döndürür. globals global sembol tablosuyla bir sözlük döndürür.


53
Aramanız gereken yöntem aradığınız modülde tanımlanmışsa, globals / locals ile bu yöntem iyidir.
Joelmob

@Joelmob, bir nesneyi kök ad alanından dizeyle almanın başka bir yolu var mı?
Nick T

@NickT Sadece bu yöntemlerin farkındayım, bunlarla aynı işlevi dolduran başkaları olduğunu düşünmüyorum, en azından neden daha fazla olması gerektiğini düşünemiyorum.
Joelmob

Senin için bir nedenim var (aslında beni buraya yönlendiren şey): Modül A, bir işlevi isimle çağırması gereken F işlevine sahip. Modül B, Modül A'yı içe aktarır ve Modül B'de tanımlanan İşlev G'yi çağırmak için bir istekle F işlevini çağırır. Görünüşe göre, işlev F yalnızca Modül F'de tanımlanan genel öğelerle çalıştığından - yani globaller () ['G'] = Yok.
David Stein

337

Patrick'in çözümü muhtemelen en temiz çözümdür. Modülü de dinamik olarak almanız gerekiyorsa, aşağıdaki gibi içe aktarabilirsiniz:

module = __import__('foo')
func = getattr(module, 'bar')
func()

93
Bu son yorumu anlamıyorum. __import__'un kendi hakkı vardır ve adı geçen dokümanlardaki bir sonraki cümle der ki: "Adı sadece çalışma zamanında bilinen bir modülü almak istediğiniz durumlar dışında __import __ () 'in doğrudan kullanımı nadirdir. Yani: verilen cevap için +1.
hoffmaje

50
Kullanın importlib.import_module. Resmi dokümanlar şunları söylüyor __import__: "Bu, importlib.import_module () 'un aksine, günlük Python programlamasında gerekli olmayan gelişmiş bir işlevdir." docs.python.org/2/library/functions.html#__import__
glarrain

8
@glarrain Sadece 2.7 ve üstü desteği ile sorun değil.
Xiong Chiamiov

1
@Xiong Chaimiov, importlib.import_module3.6'da desteklenmektedir. Bkz. Docs.python.org/3.6/library/…
cowlinator

7
@cowlinator Evet, 3.6, hem katı sürüm semantiği hem de çıkış tarihlerinde (yaklaşık altı yıl sonra geldi) "2.7 ve üstü" nin bir parçasıdır. Yorumumdan sonra üç yıl boyunca da yoktu. ;) 3.x dalında, modül 3.1'den beri var. 2.7 ve 3.1 şimdi oldukça eski; hala 2.6'yı destekleyen sunucuları bulmaya devam edersiniz, ancak günümüzde standart öneri olarak importlib'e sahip olmaya değer.
Xiong Chiamiov

114

Sadece basit bir katkı. Örneklememiz gereken sınıf aynı dosyadaysa, şöyle bir şey kullanabiliriz:

# 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()

Örneğin:

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

Ve eğer bir sınıf değilse:

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

globals()['sampleFunc']('sample arg')

101

Bir işleve tam bir python yolu olan bir dize verildiğinde, ben bu işlevin sonucunu almak için gitti:

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

1
Bu bana yardımcı oldu. __import__Fonksiyonun hafif bir versiyonudur .
Pankaj Bhambhani

Bence bu en iyi cevaptı.
Saeid

55

Python programlama SSS'ye göre en iyi cevap :

functions = {'myfoo': foo.bar}

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

Bu tekniğin birincil avantajı, dizelerin işlevlerin adlarıyla eşleşmesi gerekmemesidir. Bu aynı zamanda bir vaka yapısını taklit etmek için kullanılan birincil tekniktir


43

Umarım kimse istemez.

Benzer davranışı değerlendirin

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

Otomatik içe aktarma neden eklenmiyor?

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

Ekstra sözlüklerimiz olması durumunda kontrol etmek istiyoruz

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

Daha derine inmemiz lazım

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")()

Bu, dizin ağacını tekrar tekrar tarayarak ve USB sürücülerini otomatik olarak
takarak geliştirilebilir

27

Değer için, işlev (veya sınıf) adını ve uygulama adını bir dize olarak iletmeniz gerekiyorsa, bunu yapabilirsiniz:

myFnName  = "MyFn"
myAppName = "MyApp"
app = sys.modules[myAppName]
fn  = getattr(app,myFnName)

Biraz daha genelhandler = getattr(sys.modules[__name__], myFnName)
lony

21

Bunu dene. Bu hala eval'i kullanırken, onu sadece mevcut bağlamdan işlevi çağırmak için kullanır . Ardından, istediğiniz gibi kullanmak için gerçek bir işleve sahipsiniz.

Bundan benim için en büyük yararı, fonksiyonu çağırma noktasında eval ile ilgili herhangi bir hata elde etmenizdir. Ardından, aradığınızda yalnızca işle ilgili hatalar alırsınız.

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)

1
Bu riskli olurdu. string herhangi bir şey olabilir ve eval herhangi bir dikkate almadan değerlendirmek sonunda.
iankit

4
Elbette, bu riskler göz önüne alındığında, uygun olup olmayacağı, kullandığınız içeriğe dikkat etmelisiniz.
tvt173

1
Bir işlev parametrelerini doğrulamaktan sorumlu olmamalıdır - bu farklı bir işlevin görevidir. Eval bir dize ile kullanmanın riskli olduğunu söylemek her fonksiyonun kullanımının riskli olduğunu söylüyor.
red777

evalKesinlikle gerekli olmadıkça asla kullanmamalısınız . getattr(__module__, method_name)bu bağlamda çok daha iyi bir seçimdir.
moi

14

önerilenlerin hiçbiri bana yardımcı olmadı. Bunu ben de keşfettim.

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

Python 2.66 kullanıyorum

Bu yardımcı olur umarım


13
Hangi açıdan bu getattr () 'den daha iyidir?
V13

1
Tam olarak ne istediğimi. Tıkır tıkır çalışıyor! Mükemmel!! self.__getattribute__('title')eşittirself.title
ioaniatr

self.__getattribute__('title')hiçbir durumda (neden bilmiyorum) func = getattr(self, 'title'); func();çalışmıyor , ama çalışıyor . Yani, belki kullanmak daha iyidir getattr()yerine
ioaniatr

10
Python bilmeyen insanlar lütfen bu çöpü oylamayı bırakabilir mi? getattrBunun yerine kullanın .
Aran-Fey

6

Bu soru olarak Bu bir kopya olarak işaretlenmiş bir değişken [yineleme] yöntem-adı ataması kullanarak bir sınıf içindeki yöntemleri dinamik olarak çağırmak için , ben burada ilgili bir yanıt gönderiyorum:

Senaryo, bir sınıftaki bir yöntem dinamik olarak aynı sınıfta başka bir yöntem çağırmak istiyorum, bazı daha geniş senaryo ve netlik sunan orijinal örneğe bazı ayrıntılar ekledim:

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

Çıktı (Python 3.7.x)

işlev1: 12

fonksiyon2: 12


-7

Bu basit bir cevap, örneğin ekranı temizlemenizi sağlayacaktır. Temizledikten sonra üstündeki 0 yazdırır eval ve exec ile aşağıda iki örnek, vardır (Windows, değiştirme kullanıyorsanız cleariçin cls, Linux ve Mac kullanıcıları olarak örneğin olduğunu bırakın) ya da sadece sırasıyla çalıştırmak.

eval("os.system(\"clear\")")
exec("os.system(\"clear\")")

3
Op'un sorduğu şey bu değil.
Tuncay Göncüoğlu
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.