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

1735

¿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 de Python? Por ejemplo, supongamos que tengo un módulo fooy tengo una cadena cuyo contenido es "bar". ¿Cuál es la mejor forma de llamar foo.bar()?

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

ricree
fuente

Respuestas:

2080

Asumiendo módulo foocon método bar:

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

Puede acortar las líneas 2 y 3 para:

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

si eso tiene más sentido para su caso de uso.

Puede usar getattrde esta manera en métodos vinculados a instancias de clase, métodos a nivel de módulo, métodos de clase ... la lista continúa.

Patrick Johnmeyer
fuente
99
Se puede usar hasattr o getattr para determinar si una función está definida. Tenía una asignación de base de datos (eventType y manejo de functionName) y quería asegurarme de que nunca me "olvidé" de definir un controlador de eventos en mi python
Shaun
99
Esto funciona si ya conoce el nombre del módulo. Sin embargo, si desea que el usuario proporcione el nombre del módulo como una cadena, esto no funcionará.
Blairg23
77
Si necesita evitar que NoneType no sea una excepción invocable, también podría emplear la forma de tres argumentos de getattr: getattr (foo, 'bar', lambda: None). Pido disculpas por el formato; la aplicación para Android stackexchange es aparentemente terrible.
geekofalltrades
3
Consulte también la respuesta proporcionada por @sastanin si solo le importa, por ejemplo, las funciones de su módulo local / actual.
NuSkooler
66
@akki Sí, si estás en el foomódulo, puedes usarlo globals()para hacer esto:methodToCall = globals()['bar']
Ben Hoyt
543
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.

sastanin
fuente
53
Este método con globales / locales es bueno si el método al que necesita llamar se define en el mismo módulo desde el que está llamando.
Joelmob
@Joelmob ¿hay alguna otra forma de obtener un objeto por cadena fuera del espacio de nombres raíz?
Nick T
@NickT Solo conozco estos métodos, no creo que haya otros que cumplan la misma función que estos, al menos no puedo pensar en una razón por la que debería haber más.
Joelmob
Tengo una razón para usted (en realidad lo que me llevó aquí): el módulo A tiene una función F que necesita llamar a una función por su nombre. El módulo B importa el módulo A e invoca la función F con una solicitud para llamar a la función G, que se define en el módulo B. Esta llamada falla porque, aparentemente, la función F solo se ejecuta con las variables globales que se definen en el módulo F, por lo que las variables globales () ['G'] = Ninguno.
David Stein
338

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

module = __import__('foo')
func = getattr(module, 'bar')
func()
HS.
fuente
93
No entiendo ese último comentario. __import__ tiene su propio derecho y la siguiente oración en los documentos mencionados dice: "El uso directo de __import __ () es raro, excepto en los casos en que desea importar un módulo cuyo nombre solo se conoce en tiempo de ejecución". Entonces: +1 para la respuesta dada.
hoffmaje
50
Uso importlib.import_module. Los documentos oficiales dicen sobre __import__: "Esta es una función avanzada que no es necesaria en la programación diaria de Python, a diferencia de importlib.import_module ()". docs.python.org/2/library/functions.html#__import__
glarrain
8
@glarrain Mientras estés bien con solo soporte 2.7 y superior.
Xiong Chiamiov
1
@ Xiong Chaimiov, importlib.import_modulees compatible con 3.6. Ver docs.python.org/3.6/library/…
cowlinator el
77
@cowlinator Sí, 3.6 es parte de "2.7 y versiones posteriores", tanto en la estricta semántica de versiones como en las fechas de lanzamiento (se produjo aproximadamente seis años después). Tampoco existió durante tres años después de mi comentario. ;) En la rama 3.x, el módulo ha existido desde 3.1. 2.7 y 3.1 ahora son bastante antiguos; todavía encontrará servidores que solo son compatibles con 2.6, pero probablemente valga la pena tener importlib como el consejo estándar hoy en día.
Xiong Chiamiov
114

Solo una simple contribución. Si la clase que necesitamos instanciar está en el mismo archivo, podemos usar algo como esto:

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

Por ejemplo:

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

Y, si no es una clase:

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

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

Dada una cadena, con una ruta completa de Python a una función, así es como obtuve 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()
rueda ferrosa
fuente
1
Esto me ayudo. Es una versión ligera de la __import__función.
Pankaj Bhambhani
Creo que esta fue la mejor respuesta.
Saeid
55

La mejor respuesta según las preguntas frecuentes de programación de Python sería:

functions = {'myfoo': foo.bar}

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

La principal ventaja de esta técnica es que las cadenas no necesitan coincidir con los nombres de las funciones. Esta es también la técnica principal utilizada para emular una construcción de caso


fuente
43

La respuesta (espero) que nadie haya querido

Evaluar como comportamiento

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

¿Por qué no agregar la importación automática?

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

En caso de que tengamos diccionarios adicionales, queremos verificar

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")()
00500005
fuente
esto se podría mejorar mediante el escaneo de forma recursiva el árbol de directorios y unidades USB de auto-montaje
Wilks
27

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

myFnName  = "MyFn"
myAppName = "MyApp"
app = sys.modules[myAppName]
fn  = getattr(app,myFnName)
trubliphone
fuente
Solo un poco más genérico eshandler = getattr(sys.modules[__name__], myFnName)
lony el
21

Prueba esto. Si bien esto todavía usa eval, solo lo usa para invocar la función desde el contexto actual . Entonces, tiene la función real de usar como desee.

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

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
fuente
1
Esto sería arriesgado. string puede tener cualquier cosa y eval terminaría evaluándolo sin ninguna consideración.
iankit
44
Claro, debe tener en cuenta el contexto en el que lo está utilizando, ya sea apropiado o no, dados esos riesgos.
tvt173
1
Una función no debería ser responsable de validar sus parámetros; ese es el trabajo de una función diferente. Decir que es riesgoso usar eval con una cadena es decir que el uso de cada función es arriesgado.
red777
Nunca debe usar a evalmenos que sea estrictamente necesario. getattr(__module__, method_name)Es una opción mucho mejor en este contexto.
moi
14

nada de lo sugerido me ayudó. Aunque descubrí esto.

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

Estoy usando python 2.66

Espero que esto ayude

Natdrip
fuente
13
¿En qué aspecto es esto mejor que getattr ()?
V13
1
Exactamente lo que quería. ¡Funciona de maravilla! ¡¡Perfecto!! self.__getattribute__('title')es igual aself.title
ioaniatr
self.__getattribute__('title')no funciona en todos los casos (no sé por qué) después de todo, pero lo func = getattr(self, 'title'); func();hace. Así, tal vez es mejor utilizar getattr()en su lugar
ioaniatr
10
¿Pueden las personas que no conocen Python dejar de votar esta basura? Usar en su getattrlugar.
Aran-Fey el
6

Como esta pregunta Cómo llamar dinámicamente 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 que 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

Serjik
fuente
-7

Esta es una respuesta simple, esto le permitirá borrar la pantalla, por ejemplo. Hay dos ejemplos a continuación, con eval y exec, que imprimirán 0 en la parte superior después de la limpieza (si está utilizando Windows, cambie cleara cls, los usuarios de Linux y Mac se van como están, por ejemplo) o simplemente ejecútelo, respectivamente.

eval("os.system(\"clear\")")
exec("os.system(\"clear\")")
Archivo de números
fuente
3
Esto no es lo que op pregunta.
Tuncay Göncüoğlu