استدعاء وظيفة وحدة نمطية باستخدام اسمها (سلسلة)

StackOverflow https://stackoverflow.com/questions/3061

  •  08-06-2019
  •  | 
  •  

سؤال

ما هي أفضل طريقة لاستدعاء دالة معطاة سلسلة باسم الدالة في برنامج بايثون.على سبيل المثال، لنفترض أن لدي وحدة نمطية foo, ، ولدي سلسلة محتواها "bar".ما هي أفضل طريقة للاتصال foo.bar()?

أحتاج إلى الحصول على القيمة المرجعة للدالة، ولهذا السبب لا أستخدمها فقط eval.لقد اكتشفت كيفية القيام بذلك باستخدام eval لتحديد دالة مؤقتة تُرجع نتيجة استدعاء هذه الوظيفة، ولكنني آمل أن تكون هناك طريقة أكثر أناقة للقيام بذلك.

هل كانت مفيدة؟

المحلول

بافتراض الوحدة النمطية foo مع الطريقة bar:

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

بقدر ما يذهب ذلك، يمكن ضغط السطرين 2 و 3 إلى:

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

إذا كان هذا منطقيًا أكثر لحالة الاستخدام الخاصة بك.يمكنك استخدام getattr بهذه الطريقة على الأساليب المرتبطة بمثيل الفصل، والأساليب على مستوى الوحدة، وأساليب الفصل...والقائمة تطول.

نصائح أخرى

locals()["myfunction"]()

أو

globals()["myfunction"]()

السكان المحليين تقوم بإرجاع قاموس يحتوي على جدول رموز محلي حالي. الكرة العالمية إرجاع قاموس مع جدول الرموز العالمي.

ربما يكون حل باتريك هو الأنظف.إذا كنت بحاجة إلى التقاط الوحدة ديناميكيًا أيضًا، فيمكنك استيرادها على النحو التالي:

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

مجرد مساهمة بسيطة.إذا كان الفصل الذي نحتاج إلى مثيله موجودًا في نفس الملف، فيمكننا استخدام شيء مثل هذا:

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

على سبيل المثال:

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

وإذا لم يكن فئة:

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

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

بالنظر إلى سلسلة، مع مسار بايثون كامل إلى وظيفة، هذه هي الطريقة التي حصلت بها على نتيجة الوظيفة المذكورة:

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

أفضل إجابة حسب الأسئلة الشائعة حول برمجة بايثون سيكون:

functions = {'myfoo': foo.bar}

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

الميزة الأساسية لهذه التقنية هي أن السلاسل لا تحتاج إلى مطابقة أسماء الوظائف.هذه أيضًا هي التقنية الأساسية المستخدمة لمحاكاة بناء الحالة

الجواب (آمل) لم يرغب به أحد على الإطلاق

تقييم مثل السلوك

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

لماذا لا تضيف الاستيراد التلقائي

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

في حال كان لدينا قواميس إضافية نريد التحقق منها

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

نحن بحاجة إلى التعمق

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

على أية حال، إذا كنت بحاجة إلى تمرير اسم الوظيفة (أو الفئة) واسم التطبيق كسلسلة، فيمكنك القيام بذلك:

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

جرب هذا.في حين أن هذا لا يزال يستخدم التقييم، فإنه يستخدمه فقط استدعاء الوظيفة من السياق الحالي.ثم، لديك الوظيفة الحقيقية لاستخدامها كما يحلو لك.

الفائدة الرئيسية بالنسبة لي من هذا هي أنك سوف تحصل على أي أخطاء متعلقة بالتقييم عند استدعاء الوظيفة.ثم سوف تحصل فقط الأخطاء المتعلقة بالوظيفة عند الاتصال.

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)

لم يساعدني أي مما تم اقتراحه.لقد اكتشفت هذا بالرغم من ذلك.

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

أنا أستخدم بيثون 2.66

أتمنى أن يساعدك هذا

مثل هذا السؤال كيفية استدعاء الأساليب ديناميكيًا داخل الفصل باستخدام تعيين اسم الطريقة لمتغير [مكرر] تم وضع علامة عليها كنسخة مكررة مثل هذه، أقوم بنشر إجابة ذات صلة هنا:

السيناريو هو أن هناك طريقة في الفصل تريد استدعاء طريقة أخرى في نفس الفصل ديناميكيًا، لقد أضفت بعض التفاصيل إلى المثال الأصلي الذي يقدم بعض السيناريوهات والوضوح الأوسع:

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

الإخراج (بايثون 3.7.x)

وظيفة 1:12

وظيفة 2:12

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top