¿Qué es y cómo utilizar getattr() en Python?
Leí un artículo sobre la getattr
función, pero todavía no entiendo para qué sirve.
Lo único que entiendo getattr()
es que getattr(li, "pop")
es lo mismo que llamar li.pop
.
¿Cuándo y cómo uso esto exactamente? El libro dice algo sobre su uso para obtener una referencia a una función cuyo nombre no se conoce hasta el tiempo de ejecución, pero ¿cuándo y por qué debería usarlo?
Los objetos en Python pueden tener atributos: atributos de datos y funciones para trabajar con esos (métodos). En realidad, cada objeto tiene atributos integrados (pruebe dir(None)
,,, en dir(True)
la consola de Python).dir(...)
dir(dir)
Por ejemplo, tienes un objeto person
que tiene varios atributos: name
, gender
, etc.
Accede a estos atributos (ya sean métodos u objetos de datos) generalmente escribiendo: person.name
, person.gender
, person.the_method()
, etc.
Pero ¿qué pasa si no sabes el nombre del atributo en el momento de escribir el programa? Por ejemplo, tiene el nombre del atributo almacenado en una variable llamada attr_name
.
si
attr_name = 'gender'
entonces, en lugar de escribir
gender = person.gender
puedes escribir
gender = getattr(person, attr_name)
Algunas prácticas:
Python 3.4.0 (default, Apr 11 2014, 13:05:11)
>>> class Person():
... name = 'Victor'
... def say(self, what):
... print(self.name, what)
...
>>> getattr(Person, 'name')
'Victor'
>>> attr_name = 'name'
>>> person = Person()
>>> getattr(person, attr_name)
'Victor'
>>> getattr(person, 'say')('Hello')
Victor Hello
getattr
se generará AttributeError
si el atributo con el nombre de pila no existe en el objeto:
>>> getattr(person, 'age')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Person' object has no attribute 'age'
Pero puedes pasar un valor predeterminado como tercer argumento, que será devuelto si dicho atributo no existe:
>>> getattr(person, 'age', 0)
0
Puede utilizar getattr
junto con dir
para iterar sobre todos los nombres de atributos y obtener sus valores:
>>> dir(1000)
['__abs__', '__add__', ..., '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']
>>> obj = 1000
>>> for attr_name in dir(obj):
... attr_value = getattr(obj, attr_name)
... print(attr_name, attr_value, callable(attr_value))
...
__abs__ <method-wrapper '__abs__' of int object at 0x7f4e927c2f90> True
...
bit_length <built-in method bit_length of int object at 0x7f4e927c2f90> True
...
>>> getattr(1000, 'bit_length')()
10
Un uso práctico para esto sería encontrar todos los métodos cuyos nombres comiencen con test
y llamarlos .
Similar a getattr
lo setattr
que le permite establecer un atributo de un objeto que tiene su nombre:
>>> setattr(person, 'name', 'Andrew')
>>> person.name # accessing instance attribute
'Andrew'
>>> Person.name # accessing class attribute
'Victor'
>>>
getattr(object, 'x')
es completamente equivalente a object.x
.
Sólo hay dos casos en los que getattr
puede resultar útil.
- no puedes escribir
object.x
porque no sabes de antemano qué atributo quieres (proviene de una cadena). Muy útil para metaprogramación. - desea proporcionar un valor predeterminado.
object.y
generará unAttributeError
si no hayy
. Perogetattr(object, 'y', 5)
regresará5
.
Para mí, getattr
es más fácil explicarlo de esta manera:
Le permite llamar a métodos según el contenido de una cadena en lugar de escribir el nombre del método.
Por ejemplo, no puedes hacer esto:
obj = MyObject()
for x in ['foo', 'bar']:
obj.x()
porque x no es del tipo builtin
, pero str
. Sin embargo, PUEDES hacer esto:
obj = MyObject()
for x in ['foo', 'bar']:
getattr(obj, x)()
Le permite conectarse dinámicamente con objetos según su entrada. Lo he encontrado útil cuando trato con objetos y módulos personalizados.
Un caso de uso bastante común getattr
es asignar datos a funciones.
Por ejemplo, en un marco web como Django o Pylons, getattr
resulta sencillo asignar la URL de una solicitud web a la función que la manejará. Si miras debajo del capó del enrutamiento de Pylons, por ejemplo, verás que (al menos de forma predeterminada) divide la URL de una solicitud, como:
http://www.example.com/customers/list
en "clientes" y "lista". Luego busca una clase de controlador llamada CustomerController
. Suponiendo que encuentra la clase, crea una instancia de la clase y luego la utiliza getattr
para obtener su list
método. Luego llama a ese método y le pasa la solicitud como argumento.
Una vez que comprenda esta idea, resultará realmente fácil ampliar la funcionalidad de una aplicación web: simplemente agregue nuevos métodos a las clases de controlador y luego cree enlaces en sus páginas que utilicen las URL apropiadas para esos métodos. Todo esto es posible gracias a getattr
.