example español python module python-3.x getattr attributeerror

python - español - __getattr__ en un módulo



getattr python español (9)

¿Cómo se puede implementar el equivalente de __getattr__ en una clase, en un módulo?

Ejemplo

Cuando llamo a una función que no existe en los atributos estáticamente definidos de un módulo, deseo crear una instancia de una clase en ese módulo e invocar el método con el mismo nombre que falló en la búsqueda de atributos en el módulo.

class A(object): def salutation(self, accusative): print "hello", accusative # note this function is intentionally on the module, and not the class above def __getattr__(mod, name): return getattr(A(), name) if __name__ == "__main__": # i hope here to have my __getattr__ function above invoked, since # salutation does not exist in the current namespace salutation("world")

Lo que da:

matt@stanley:~/Desktop$ python getattrmod.py Traceback (most recent call last): File "getattrmod.py", line 9, in <module> salutation("world") NameError: name ''salutation'' is not defined


Aquí está mi propia contribución humilde: un ligero embellecimiento de la respuesta altamente calificada de @Håvard S, pero un poco más explícita (por lo que podría ser aceptable para @ S.Lott, aunque probablemente no sea lo suficientemente bueno para el OP):

import sys class A(object): def salutation(self, accusative): print "hello", accusative class Wrapper(object): def __init__(self, wrapped): self.wrapped = wrapped def __getattr__(self, name): try: return getattr(self.wrapped, name) except AttributeError: return getattr(A(), name) _globals = sys.modules[__name__] = Wrapper(sys.modules[__name__]) if __name__ == "__main__": _globals.salutation("world")


Crea tu archivo de módulo que tiene tus clases. Importar el módulo. Ejecute getattr en el módulo que acaba de importar. Puede hacer una importación dinámica usando __import__ y extraer el módulo de sys.modules.

Aquí está su módulo some_module.py :

class Foo(object): pass class Bar(object): pass

Y en otro módulo:

import some_module Foo = getattr(some_module, ''Foo'')

Haciendo esto dinámicamente:

import sys __import__(''some_module'') mod = sys.modules[''some_module''] Foo = getattr(mod, ''Foo'')


En algunas circunstancias, el diccionario global globals() puede ser suficiente, por ejemplo, puede instanciar una clase por nombre del alcance global:

from somemodule import * # imports SomeClass someclass_instance = globals()[''SomeClass'']()


Esto es hackish, pero ...

import types class A(object): def salutation(self, accusative): print "hello", accusative def farewell(self, greeting, accusative): print greeting, accusative def AddGlobalAttribute(classname, methodname): print "Adding " + classname + "." + methodname + "()" def genericFunction(*args): return globals()[classname]().__getattribute__(methodname)(*args) globals()[methodname] = genericFunction # set up the global namespace x = 0 # X and Y are here to add them implicitly to globals, so y = 0 # globals does not change as we iterate over it. toAdd = [] def isCallableMethod(classname, methodname): someclass = globals()[classname]() something = someclass.__getattribute__(methodname) return callable(something) for x in globals(): print "Looking at", x if isinstance(globals()[x], (types.ClassType, type)): print "Found Class:", x for y in dir(globals()[x]): if y.find("__") == -1: # hack to ignore default methods if isCallableMethod(x,y): if y not in globals(): # don''t override existing global names toAdd.append((x,y)) for x in toAdd: AddGlobalAttribute(*x) if __name__ == "__main__": salutation("world") farewell("goodbye", "world")

Esto funciona iterando sobre todos los objetos en el espacio de nombre global. Si el elemento es una clase, itera sobre los atributos de la clase. Si el atributo es invocable, lo agrega al espacio de nombre global como una función.

Ignora todos los atributos que contienen "__".

No usaría esto en el código de producción, pero debería comenzar.


Esto es un truco, pero puedes envolver el módulo con una clase:

class Wrapper(object): def __init__(self, wrapped): self.wrapped = wrapped def __getattr__(self, name): # Perform custom logic here try: return getattr(self.wrapped, name) except AttributeError: return ''default'' # Some sensible default sys.modules[__name__] = Wrapper(sys.modules[__name__])


Hay dos problemas básicos con los que se está metiendo aquí:

  1. __xxx__ métodos __xxx__ solo se __xxx__ en la clase
  2. TypeError: can''t set attributes of built-in/extension type ''module''

(1) significa que cualquier solución debería también hacer un seguimiento de qué módulo se estaba examinando, de lo contrario, cada módulo tendría el comportamiento de sustitución de instancia; y (2) significa que (1) ni siquiera es posible ... al menos no directamente.

Afortunadamente, sys.modules no es quisquilloso con lo que va allí para que funcione un wrapper, pero solo para el acceso a módulos (es decir, import somemodule; somemodule.salutation(''world'') ; para el mismo módulo de acceso tienes que tirar de los métodos de la clase de sustitución y agréguelos a globals() con un método personalizado en la clase (me gusta usar .export() ) o con una función genérica (como las ya enumeradas como respuestas). Una cosa a tener en cuenta: si el contenedor está creando una nueva instancia cada vez, y la solución global no lo es, terminas con un comportamiento sutilmente diferente. Ah, y no puedes usar ambos al mismo tiempo, es uno o el otro.

Actualizar

De Guido van Rossum :

En realidad, hay un truco que se utiliza y recomienda ocasionalmente: un módulo puede definir una clase con la funcionalidad deseada, y luego al final, reemplazarse en sys.modules con una instancia de esa clase (o con la clase, si insistes) , pero eso generalmente es menos útil). P.ej:

# module foo.py import sys class Foo: def funct1(self, <args>): <code> def funct2(self, <args>): <code> sys.modules[__name__] = Foo()

Esto funciona porque la maquinaria de importación habilita activamente este truco, y como su paso final saca el módulo real de sys.modules, después de cargarlo. (Esto no es accidental. El truco se propuso hace mucho tiempo y decidimos que nos gustaba lo suficiente como para soportarlo en la maquinaria de importación).

Entonces, la forma establecida de lograr lo que quieres es crear una sola clase en tu módulo, y como último acto del módulo reemplazar sys.modules[__name__] con una instancia de tu clase, y ahora puedes jugar con __getattr__ / __setattr__ / __getattribute__ según sea necesario.


Similar a lo que @ Håvard S propuso, en un caso en el que necesitaba implementar algo de magia en un módulo (como __getattr__ ), definiría una nueva clase que hereda de types.ModuleType y lo pondría en sys.modules (probablemente reemplazando el módulo donde mi ModuleType personalizado fue definido).

Consulte el archivo __init__.py principal de Werkzeug para una implementación bastante robusta de esto.


Usualmente no lo hacemos de esa manera.

Lo que hacemos es esto

class A(object): .... # The implicit global instance a= A() def salutation( *arg, **kw ): a.salutation( *arg, **kw )

¿Por qué? Para que la instancia global implícita sea visible.

Por ejemplo, mire el módulo random , que crea una instancia global implícita para simplificar levemente los casos de uso en los que desea un generador de números aleatorios "simple".


Hace un tiempo, Guido declaró que todas las búsquedas de métodos especiales en las clases de estilo nuevo omiten __getattr__ y __getattribute__ . Dunder métodos utilizados para trabajar en los módulos: podría, por ejemplo, utilizar un módulo como administrador de contexto simplemente definiendo __enter__ y __exit__ , antes de que esos trucos se broke .

Recientemente, algunas características históricas han __getattr__ , el módulo __getattr__ entre ellas, por lo que el hack existente (un módulo que se reemplaza con una clase en sys.modules en el momento de la importación) ya no debería ser necesario.

2018 respuesta:

En Python 3.7+, simplemente hazlo de la manera más obvia.

# my_module.py def __getattr__(name: str) -> Any: ...

Para personalizar el acceso de atributos en un módulo, defina una función __getattr__ a nivel de módulo que debería aceptar un argumento (el nombre de un atributo) y devolver el valor calculado o generar un AttributeError . De forma similar, puede definir una función __dir__ a nivel de módulo para responder a dir(my_module) .

Ver PEP 562 para más detalles.