script - modulos python 3




Llamar a una función de un módulo usando su nombre(una cadena) (8)

¿Cuál es la mejor manera de llamar a una función dada una cadena con el nombre de la función en un programa Python? Por ejemplo, digamos que tengo un módulo foo , y tengo una cadena cuyo contenido es "bar" . ¿Cuál es la mejor manera de llamar a foo.bar() ?

Necesito obtener el valor de retorno de la función, por lo que no solo uso eval . Descubrí cómo hacerlo utilizando eval para definir una función temporal que devuelva el resultado de esa llamada de función, pero espero que haya una forma más elegante de hacerlo.


Como esta pregunta Cómo llamar dinámicamente a los métodos dentro de una clase usando la asignación de nombre de método a una variable [duplicado] marcada como duplicada como esta, estoy publicando una respuesta relacionada aquí:

El escenario es, un método en una clase quiere llamar a otro método en la misma clase dinámicamente, he agregado algunos detalles al ejemplo original que ofrece un escenario y claridad más amplios:

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

Salida (Python 3.7.x)

función1: 12

función2: 12


Dada una cadena, con una ruta completa de python a una función, esta es la forma en que traté de obtener el resultado de dicha función:

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

La respuesta (espero) que nadie haya querido nunca.

Evaluar como comportamiento

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

¿Por qué no agregar auto-importación

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

En caso de que tengamos diccionarios extra queremos revisar.

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

Necesitamos ir más profundo

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

La solución de Patrick es probablemente la más limpia. Si también necesita recoger dinámicamente el módulo, puede importarlo como:

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

Para lo que vale la pena, si necesita pasar el nombre de la función (o clase) y el nombre de la aplicación como una cadena, entonces podría hacer esto:

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

Prueba esto. Si bien esto todavía usa eval, solo lo usa para invocar la función del contexto actual . Entonces, tienes la función real para usar como quieras.

El principal beneficio para mí de esto es que obtendrás cualquier error relacionado con la evaluación al momento de invocar la función. Entonces solo obtendrás los errores relacionados con la función cuando llames.

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)

Suponiendo módulo foo con bar método:

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

En lo que va, las líneas 2 y 3 se pueden comprimir para:

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

Si eso tiene más sentido para su caso de uso. Puede utilizar getattr de esta manera en los métodos enlazados de instancia de clase, los métodos de nivel de módulo, los métodos de clase ... y la lista continúa.


locals()["myfunction"]()

o

globals()["myfunction"]()

locals devuelve un diccionario con una tabla de símbolos local actual. globals devuelve un diccionario con tabla de símbolos global.





object