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

2065

¿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, digamos 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 usando evalpara definir una función temporal que devuelve el resultado de esa llamada a la función, pero espero que haya una forma más elegante de hacerlo.

2479

Suponiendo 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 a:

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.

dieciséis
  • dieciséis
    Se puede usar hasattr o getattr para determinar si una función está definida. Tenía un mapeo de base de datos (eventType y manejo de functionName) y quería asegurarme de que nunca "olvidé" definir un controlador de eventos en mi python
    Shaun
    3 jun 14 a las 13:20
  • 12
    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á. 21 de junio de 2014 a las 7:39
  • 9
    Si necesita evitar una excepción NoneType no invocable, también puede emplear la forma de tres argumentos de getattr: getattr (foo, 'bar', lambda: None). Pido disculpas por el formato; la aplicación de Android stackexchange es aparentemente terrible. 16 de agosto de 2014 a las 18:01
  • 4
    Consulte también la respuesta proporcionada por @sastanin si solo le interesan, por ejemplo, las funciones de su módulo local / actual. 19/06/2015 a las 22:19
  • 11
    @akki Sí, si estás en el foomódulo puedes usar globals()para hacer esto:methodToCall = globals()['bar'] 20/12/2016 a las 19:06
639
locals()["myfunction"]()

o

globals()["myfunction"]()

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

4
  • 74
    Este método con globals / locals es bueno si el método que necesita llamar está definido en el mismo módulo desde el que está llamando.
    Joelmob
    9/10/2014 a las 21:36
  • @Joelmob ¿hay alguna otra forma de obtener un objeto por cadena fuera del espacio de nombres raíz?
    Nick T
    26/01/15 a las 20:51
  • @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
    27/01/15 a las 12:34
  • 1
    Tengo una razón para ti (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 está definida en el Módulo B. Esta llamada falla porque, aparentemente, la función F solo se ejecuta con los globales que están definidos en el Módulo F, por lo que globals () ['G'] = Ninguno. 30 de ene. De 2017 a las 15:18
395

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()
6
  • 98
    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 los que desea importar un módulo cuyo nombre solo se conoce en tiempo de ejecución". Entonces: +1 para la respuesta dada. 5 de mayo de 12 a las 9:33
  • 55
    Utilice 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__ 5 de agosto de 2013 a las 22:07
  • 9
    @glarrain Siempre que esté de acuerdo con solo el soporte 2.7 y superior. 14 de septiembre de 2013 a las 16:54
  • 2
    @Xiong Chaimiov, importlib.import_modulees compatible con 3.6. Consulte docs.python.org/3.6/library/… 5/10/2017 a las 19:28
  • 8
    @cowlinator Sí, 3.6 es parte de "2.7 y versiones posteriores", tanto en la semántica de control de versiones estricta como en las fechas de lanzamiento (llegó unos seis años después). Tampoco existió durante tres años después de mi comentario. ;) En la rama 3.x, el módulo existe desde 3.1. 2.7 y 3.1 son ahora bastante antiguos; todavía encontrará servidores que solo admiten 2.6, pero probablemente valga la pena que importlib sea el consejo estándar hoy en día. 5 oct 2017 a las 23:55
137

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')
1
  • ¿Qué pasa si llamas a esta función dentro de una función de clase? el dia de ayer
131

Dada una cadena, con una ruta de Python completa 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()
2
  • 3
    Eso me ayudó. Es una versión ligera de la __import__función. 16/12/15 a las 13:19
  • 2
    Creo que esta fue la mejor respuesta.
    SdSaati
    22/01/20 a las 9:12
73

La mejor respuesta de acuerdo con las preguntas frecuentes de programación de Python sería:

functions = {'myfoo': foo.bar}

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

The primary advantage of this technique is that the strings do not need to match the names of the functions. This is also the primary technique used to emulate a case construct

60

La respuesta (espero) que nadie quiso nunca

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")()
2
  • 14
    esto podría mejorarse escaneando de forma recursiva el árbol de directorios y montando automáticamente las unidades USB
    wilks
    21 de mayo de 2020 a las 0:43
  • Esta es definitivamente la respuesta que quería. Perfecto. 27 feb a las 20:13
35

Por 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)
2
  • Un poco más genérico es handler = getattr(sys.modules[__name__], myFnName)
    lony
    6/10/2017 a las 12:33
  • ¿Cómo funciona si la función es una función de clase? el dia de ayer
31

Prueba esto. Si bien esto todavía usa eval, solo lo usa para convocar la función desde el contexto actual . Entonces, tienes la función real para usarla como desees.

El principal beneficio para mí de esto es que obtendrá cualquier error relacionado con la evaluación en el punto 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)
4
  • 2
    Esto sería arriesgado. string puede tener cualquier cosa y eval terminaría evaluándolo sin ninguna consideración.
    iankit
    30/12/2016 a las 18:13
  • 5
    Claro, debe tener en cuenta el contexto en el que lo está utilizando, ya sea que sea apropiado o no, dados esos riesgos.
    tvt173
    6 de enero de 2017 a las 18:48
  • 3
    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 arriesgado usar eval con una cadena es decir que el uso de cada función es arriesgado.
    red777
    14/08/18 a las 11:01
  • 2
    Nunca debe usar a evalmenos que sea estrictamente necesario. getattr(__module__, method_name)es una opción mucho mejor en este contexto.
    moi
    14/01/19 a las 12:05
16

nada de lo que se sugirió me ayudó. Aunque descubrí esto.

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

Estoy usando python 2.66

Espero que esto ayude

4
  • 21
    ¿En qué aspecto es esto mejor que getattr ()?
    V13
    29/07/2016 a las 12:26
  • 1
    Exactamente lo que quería. ¡Funciona de maravilla! ¡¡Perfecto!! self.__getattribute__('title')es igual aself.title 6/08/18 a las 18:49
  • self.__getattribute__('title')no funciona en ningún caso (no sé por qué) después de todo, pero lo func = getattr(self, 'title'); func();hace. Entonces, tal vez sea mejor usar getattr()en su lugar 16/08/18 a las 16:52
  • dieciséis
    ¿Pueden las personas que no conocen Python dejar de votar esta basura? Úselo en su getattrlugar. 23 oct 2018 a las 5:19
12

Como esta pregunta Cómo llamar dinámicamente a métodos dentro de una clase usando la asignación de nombre de método a una variable [duplicada] marcada como un duplicado 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 más amplio y claridad:

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

9

Aunque getattr () es un método elegante (y aproximadamente 7 veces más rápido), puede obtener el valor de retorno de la función (local, método de clase, módulo) con eval tan elegante como x = eval('foo.bar')(). Y cuando implementas algún manejo de errores, entonces de manera bastante segura (el mismo principio se puede usar para getattr). Ejemplo con importación y clase de módulo:

# import module, call module function, pass parameters and print retured value with eval():
import random
bar = 'random.randint'
randint = eval(bar)(0,100)
print(randint) # will print random int from <0;100)

# also class method returning (or not) value(s) can be used with eval: 
class Say:
    def say(something='nothing'):
        return something

bar = 'Say.say'
print(eval(bar)('nice to meet you too')) # will print 'nice to meet you' 

Cuando el módulo o la clase no existe (error tipográfico o algo mejor), se genera NameError. Cuando la función no existe, se genera AttributeError. Esto se puede usar para manejar errores:

# try/except block can be used to catch both errors
try:
    eval('Say.talk')() # raises AttributeError because function does not exist
    eval('Says.say')() # raises NameError because the class does not exist
    # or the same with getattr:
    getattr(Say, 'talk')() # raises AttributeError
    getattr(Says, 'say')() # raises NameError
except AttributeError:
    # do domething or just...
    print('Function does not exist')
except NameError:
    # do domething or just...
    print('Module does not exist')
4

getattrllama al método por su nombre desde un objeto. Pero este objeto debería ser padre de la clase de llamada. La clase principal se puede obtener mediantesuper(self.__class__, self)

class Base:
    def call_base(func):
        """This does not work"""
        def new_func(self, *args, **kwargs):
            name = func.__name__
            getattr(super(self.__class__, self), name)(*args, **kwargs)
        return new_func

    def f(self, *args):
        print(f"BASE method invoked.")

    def g(self, *args):
        print(f"BASE method invoked.")

class Inherit(Base):
    @Base.call_base
    def f(self, *args):
        """function body will be ignored by the decorator."""
        pass

    @Base.call_base
    def g(self, *args):
        """function body will be ignored by the decorator."""
        pass

Inherit().f() # The goal is to print "BASE method invoked."
0

Me estoy enfrentando a un problema similar antes, que es convertir una cadena en una función. pero no puedo usar eval()o ast.literal_eval(), porque no quiero ejecutar este código inmediatamente.

por ejemplo, tengo una cadena "foo.bar"y quiero asignarla xcomo un nombre de función en lugar de una cadena, lo que significa que puedo llamar a la función por x() ON DEMAND .

aquí está mi código:

str_to_convert = "foo.bar"
exec(f"x = {str_to_convert}")
x()

en cuanto a su pregunta, solo necesita agregar el nombre de su módulo fooy .antes de la {}siguiente manera:

str_to_convert = "bar"
exec(f"x = foo.{str_to_convert}")
x()

¡¡¡ADVERTENCIA!!! o bien eval()o exec()es un método peligroso, usted debe confirmar la seguridad. ¡¡¡ADVERTENCIA!!! o bien eval()o exec()es un método peligroso, usted debe confirmar la seguridad. ¡¡¡ADVERTENCIA!!! o bien eval()o exec()es un método peligroso, usted debe confirmar la seguridad.

0

En python3, puedes usar el __getattribute__método. Vea el siguiente ejemplo con una cadena de nombre de método de lista:

func_name = 'reverse'

l = [1, 2, 3, 4]
print(l)
>> [1, 2, 3, 4]

l.__getattribute__(func_name)()
print(l)
>> [4, 3, 2, 1]
-14

Esta es una respuesta simple, esto le permitirá limpiar 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á usando Windows, cambie cleara cls, los usuarios de Linux y Mac lo dejan como está, por ejemplo) o simplemente lo ejecutan, respectivamente.

eval("os.system(\"clear\")")
exec("os.system(\"clear\")")
2
  • 5
    Esto no es lo que pide op. 14 nov 2019 a las 7:30
  • 9
    este fragmento de código contiene las 2 peores fallas de seguridad, anidadas. Algún tipo de récord. 17 jul.20 a las 5:00