función - ¿Qué es y cómo utilizar getattr() en Python?

CorePress2024-01-24  12

Leí un artículo sobre la función getattr, pero todavía no entiendo para qué sirve.

Lo único que entiendo sobre getattr() es que getattr(li, "pop") es lo mismo que llamar a 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?

¿Con qué parte tienes problemas? ¿Atributos como cadenas? ¿Funciones de primera clase?

- Ignacio Vázquez-Abrams

2 de noviembre de 2010 a las 5:50

2

Creo que mi problema es entender el concepto de getattr(). Todavía no entiendo su propósito.

- Terence Ponce

2 de noviembre de 2010 a las 5:55

@Terence, ¿mi respuesta no aclara las cosas?

-Alois Cochard

2 de noviembre de 2010 a las 5:57

@Alois, tu respuesta definitivamente aclaró algunas de mis dudas, pero todavía no puedo entender completamente para qué sirve getattr().

- Terence Ponce

2 de noviembre de 2010 a las 6:05

6

@S.Lott, lo hice. La documentación solo tenía la definición, así que estaba seguro.Y estoy confundido acerca de su uso. Sin embargo, ahora entiendo getattr después de leer más sobre ello.

- Terence Ponce

2 de noviembre de 2010 a las 12:57



------------------------------------

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), dir(True), dir(...), dir(dir) en la consola Python).

Por ejemplo tienes un objeto persona, que tiene varios atributos: nombre, género, etc.

Accedes a estos atributos (ya sean métodos u objetos de datos) normalmente escribiendo: persona.nombre, persona.género, persona.el_método(), etc.

Pero ¿y si no lo sabes?¿Cuál es 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)

Alguna práctica:

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 generará AttributeError si el atributo con el nombre dado 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

Puedes usar 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, existe setattr que le permite establecer un atributo de un objeto.inging su nombre:

>>> setattr(person, 'name', 'Andrew')
>>> person.name  # accessing instance attribute
'Andrew'
>>> Person.name  # accessing class attribute
'Victor'
>>>

3

14

Entonces me parece que getattr(..) debería usarse en 2 escenarios: 1. cuando el nombre del atributo es un valor dentro de una variable (por ejemplo, getattr(persona, some_attr)) y 2. cuando necesitamos utilice el tercer argumento posicional para el valor predeterminado (por ejemplo, getattr(persona, 'edad', 24)). Si veo un escenario como getattr(person, 'age') me parece que es idéntico a person.age lo que me lleva a pensar que person.age es más Pythonic. ¿Es correcto?

-wpcarro

24/10/2016 a las 22:01

1

@wpcarro tanto person.age como getattr(person, "age") son idiomáticos de Python, por lo que es difícil argumentar que uno es más pitónico que el otro.

-qneill

19/07/2020 a las 2:01

4

"La legibilidad cuenta". Ciertamente person.age es mejor que getattr(persona, "edad"). Tiene sentido usar getattr cuando tienes el nombre del atributo en una variable.

-warvariuc

10 de junio de 2021 a las 13:55



------------------------------------

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á un AttributeError si no hay y. Pero getattr(object, 'y', 5) devolverá 5.Respondido al

10 de abril de 2019 a las 9:42

nota_azul

nota_azul

28,1k

9

9 insignias de oro

76

76 insignias de plata

97

97 bronce insignias

12

1

¿Me equivoco al pensar que el segundo punto es inconsistente con la declaración inicial de la respuesta?

- skoh

2 de julio de 2020 a las 16:18

4

@skoh: bueno, en realidad, la declaración inicial menciona getattr con dos parámetros (lo cual es equivalente), y la segunda viñeta menciona getattr con 3 parámetros. Aunque fuera incoherente, probablemente lo dejaría, el énfasis es más importante.

- nota_azul

2 julio 2020 a las 17:12

2

@UlfGjerdingen: piensa en javascript. o.x es equivalente a o['x']. Pero la segunda expresión podría usarse con cualquier o[some_string] que pueda decidirse en tiempo de ejecución (por ejemplo, a partir de la entrada del usuario o la inspección de objetos), mientras que en la primera expresión, x es fijo.

- nota_azul

21/08/2020 a las 16:13

7

Para revivir un necro, otro caso de uso es cuando el identificador contiene un carácter ilegal como. o - (como estoy tratando ahora). getattr(obj, 'some.val') funcionará donde obj.some.val no.

-Michael

26 de octubre de 2020 a las 5:00

1

@JürgenK.: por supuesto, self se comporta como cualquier otro objeto, la única diferencia es que se pasa automáticamente

- nota_azul

6 de septiembre de 2021 a las 9:22



------------------------------------

Para mí, getattr es más fácil de explicar de esta manera:

Le permite llamar a métodos basándose en 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 incorporado, perocalle. 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.

Respondido al

30 de agosto de 2013 a las 17:22

Peón Nuclear

Peón Nuclear

5.831

5

5 insignias de oro

46

46 insignias de plata

52

52 insignias de bronce

4

4

Esta es una respuesta bastante sencilla y precisa.

- usuario6037143

1 de enero de 2019 a las 17:27

¿Qué es el objeto.x?

- develarista

15/09/2020 a las 21:05

1

@develarist El autor de la pregunta no tenía un ejemplo en el que basar mi respuesta, así que MyObject, obj y x (definición de clase, instancia de clase y atributo respectivamente) son solo ejemplos/datos de maqueta donde debe completar sus propias clases y atributos a los que desea acceder. foo, bar y baz se utilizan a menudo como marcadores de posición en documentos de Linux/Unix/foss.

Peón Nuclear

15/09/2020 a las 21:50

operator.methodcaller() está diseñado para hacer lo mismo que en este ejemplo, llamando a un método definido con cadenas. Prefiero la implementación del ejemplo.

- Frote

30/11/2021 a las 23:09



------------------------------------

Un caso de uso bastante común para getattr es asignar datos a funciones.

Por ejemplo, en un marco web como Django o Pylons, getattr hace que sea 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 usa getattr para obtener su método de lista. Luego llama that método, pasándole 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.

0



------------------------------------

Aquí hay un ejemplo rápido y sencillo de cómo una clase podría activar diferentes versiones de un método de guardar dependiendo del sistema operativo en el que se esté ejecutando usando getattr().

import os

class Log(object):
    def __init__(self):
        self.os = os.name
    def __getattr__(self, name):
        """ look for a 'save' attribute, or just 
          return whatever attribute was specified """
        if name == 'save':
            try:
                # try to dynamically return a save 
                # method appropriate for the user's system
                return getattr(self, self.os)
            except:
                # bail and try to return 
                # a default save method
                return getattr(self, '_save')
        else:
            return getattr(self, name)

    # each of these methods could have save logic specific to 
    # the system on which the script is executed
    def posix(self): print 'saving on a posix machine'
    def nt(self): print 'saving on an nt machine'
    def os2(self): print 'saving on an os2 machine'
    def ce(self): print 'saving on a ce machine'
    def java(self): print 'saving on a java machine'
    def riscos(self): print 'saving on a riscos machine'
    def _save(self): print 'saving on an unknown operating system'

    def which_os(self): print os.name

Ahora usemos esta clase en un ejemplo:

logger = Log()

# Now you can do one of two things:
save_func = logger.save
# and execute it, or pass it along 
# somewhere else as 1st class:
save_func()

# or you can just call it directly:
logger.save()

# other attributes will hit the else 
# statement and still work as expected
logger.which_os()



------------------------------------

Aparte de todas las sorprendentes respuestas aquí, hay una manera de usar getattr para guardar grandes líneas de código y mantenerlo cómodo. Este pensamiento surgió a raíz de la terrible representación del código que a veces puede ser una necesidad.

Escenario

Supongamos que la estructura de su directorio es la siguiente:

- superheroes.py
- properties.py

Y tienes funciones para obtener información sobre Thor, Iron Man, Doctor Strange en superheroes.py. Escribes muy inteligentemente las propiedades de todos ellos en properties.py en un dictado compacto y luego accedes a ellos.

propiedades.py

thor = {
    'about': 'Asgardian god of thunder',
    'weapon': 'Mjolnir',
    'powers': ['invulnerability', 'keen senses', 'vortex breath'], # and many more
}
iron_man = {
    'about': 'A wealthy American business magnate, playboy, and ingenious scientist',
    'weapon': 'Armor',
    'powers': ['intellect', 'armor suit', 'interface with wireless connections', 'money'],
}
doctor_strange = {
    'about': ' primary protector of Earth against magical and mystical threats',
    'weapon': 'Magic',
    'powers': ['magic', 'intellect', 'martial arts'],
}

Ahora, digamosdesea devolver las capacidades de cada uno de ellos a pedido en superheroes.py. Entonces, hay funciones como

from .properties import thor, iron_man, doctor_strange


def get_thor_weapon():
    return thor['weapon']


def get_iron_man_bio():
    return iron_man['about']


def get_thor_powers():
    return thor['powers']

...y más funciones que devuelven diferentes valores según las claves y el superhéroe.

Con la ayuda de getattr, podrías hacer algo como:

from . import properties


def get_superhero_weapon(hero):
    superhero = getattr(properties, hero)
    return superhero['weapon']


def get_superhero_powers(hero):
    superhero = getattr(properties, hero)
    return superhero['powers']

¡Has reducido considerablemente el número de líneas de código, funciones y repeticiones!

Ah, y por supuesto, si tienes nombres incorrectos como properties_of_thor para las variables, puedes crearlos y acceder a ellos simplemente haciendo

def get_superhero_weapon(hero):
    superhero = 'properties_of_{}'.format(hero)
    all_properties = getattr(properties, superhero)
    return all_properties['weapon']

NOTA: Para este problema en particular, puede haber formas más inteligentes de abordar la situación, pero la idea es brindar información sobre cómo usar getattr en los lugares correctos para escribir código más limpio.



------------------------------------

# getattr

class hithere():

    def french(self):
        print 'bonjour'

    def english(self):
        print 'hello'

    def german(self):
        print 'hallo'

    def czech(self):
        print 'ahoj'

    def noidea(self):
        print 'unknown language'


def dispatch(language):
    try:
        getattr(hithere(),language)()
    except:
        getattr(hithere(),'noidea')()
        # note, do better error handling than this

dispatch('french')
dispatch('english')
dispatch('german')
dispatch('czech')
dispatch('spanish')



------------------------------------

Con bastante frecuencia, cuando creo un archivo XML a partir de datos almacenados en una clase, recibía errores si el atributo no existía o era de tipo Ninguno. En este caso, mi problema no era no saber cuál era el nombre del atributo, como se indica en su pregunta, sino que alguna vez se almacenaron datos en ese atributo.

class Pet:
    def __init__(self):
        self.hair = None
        self.color = None

Si usara hasattr para hacer esto, devolvería True incluso si el valor del atributo fuera del tipo Ninguno y esto provocaría que mi ElementTree establece el comando para que falle.

hasattr(temp, 'hair')
>>True

Si el valor del atributo fuera del tipo Ninguno, getattr también lo devolvería, lo que provocaría que mi comando set de ElementTree fallara.

c = getattr(temp, 'hair')
type(c)
>> NoneType

Utilizo el siguiente método para ocuparme de estos casos ahora:

def getRealAttr(class_obj, class_attr, default = ''):
    temp = getattr(class_obj, class_attr, default)
    if temp is None:
        temp = default
    elif type(temp) != str:
        temp = str(temp)
    return temp

Aquí es cuando y cómo uso getattr.



------------------------------------

Otro uso de getattr() para implementar una declaración de cambio en Python. Utiliza ambas reflexiones para obtener el tipo de caso.

import sys

class SwitchStatement(object):
    """ a class to implement switch statement and a way to show how to use gettattr in Pythion"""

    def case_1(self):
        return "value for case_1"

    def case_2(self):
        return "value for case_2"

    def case_3(self):
        return "value for case_3"

    def case_4(self):
        return "value for case_4"

    def case_value(self, case_type=1):
        """This is the main dispatchmethod, that uses gettattr"""
        case_method = 'case_' + str(case_type)
        # fetch the relevant method name
        # Get the method from 'self'. Default to a lambda.
        method = getattr(self, case_method, lambda: "Invalid case type")
        # Call the method as we return it
        return method()

def main(_):
    switch = SwitchStatement()
    print swtich.case_value(_)

if __name__ == '__main__':
    main(int(sys.argv[1]))
Respondido

28 de enero de 2018 a las 22:26

Jules Damji

Jules Damji

179

2

2 insignias de bronce

1

Me gusta esta respuesta, pero corrija los pequeños errores tipográficos

- mayo

11/01/2020 a las 20:54



------------------------------------

setattr()

Usamos setattr para agregar un atributo a nuestra instancia de clase. Pasamos la instancia de clase, el atributo na.Yo y el valor.

getattr()

Con getattr recuperamos estos valores

Por ejemplo

Employee = type("Employee", (object,), dict())

employee = Employee()

# Set salary to 1000
setattr(employee,"salary", 1000 )

# Get the Salary
value = getattr(employee, "salary")

print(value)



------------------------------------

Creo que este ejemplo se explica por sí mismo. Ejecuta el método del primer parámetro, cuyo nombre se proporciona en el segundo parámetro.

class MyClass:
   def __init__(self):
      pass
   def MyMethod(self):
      print("Method ran")

# Create an object
object = MyClass()
# Get all the methods of a class
method_list = [func for func in dir(MyClass) if callable(getattr(MyClass, func))]
# You can use any of the methods in method_list
# "MyMethod" is the one we want to use right now

# This is the same as running "object.MyMethod()"
getattr(object,'MyMethod')()



------------------------------------

También es aclaratorio en https://www.programiz.com/python-programming/methods/built-in/getattr

class Person:
    age = 23
    name = "Adam"

person = Person()
print('The age is:', getattr(person, "age"))
print('The age is:', person.age)

La edad es: 23

La edad es: 23

class Person:
    age = 23
    name = "Adam"

person = Person()

# when default value is provided
print('The sex is:', getattr(person, 'sex', 'Male'))

# when no default value is provided
print('The sex is:', getattr(person, 'sex'))

El sexo es: Masculino

AttributeError: el objeto 'Persona' no tiene el atributo 'sexo'



------------------------------------

Lo he probado en Python2.7.17

Algunos de mis compañeros ya respondieron. Sin embargo he intentado llamar getattr(obj, 'set_value') y esto no ejecutó el método set_value, así que cambié a getattr(obj, 'set_value')() --> Esto ayuda a invocar lo mismo.

Código de ejemplo:

Ejemplo 1:

    class GETATT_VERIFY():
       name = "siva"
       def __init__(self):
           print "Ok"
       def set_value(self):
           self.value = "myself"
           print "oooh"
    obj = GETATT_VERIFY()
    print getattr(GETATT_VERIFY, 'name')
    getattr(obj, 'set_value')()
    print obj.value

0



------------------------------------

getattr() puede acceder a los atributos de clase con clase y objeto.

Por ejemplo, hay una clase de prueba a continuación:

class Test:
    class_variable = "Class variable"
    
    def __init__(self):
        self.instance_variable = "Instance variable"
    
    def instance_method(self, var):
        print(var)
        
    @classmethod
    def class_method(cls, var):
        print(var)
        
    @staticmethod
    def static_method(var):
        print(var)

Luego, accediendo a los atributos de clase de prueba con class y getattr() los resultados se muestran a continuación:

print(getattr(Test, "class_variable"))                  # Class variable
print(getattr(Test, "instance_variable"))               # Error

getattr(Test, "instance_method")("Instance method")     # Error
getattr(Test, "instance_method")("", "Instance method") # Instance method
getattr(Test, "class_method")("Class method")           # Class method
getattr(Test, "static_method")("Static method")         # Static method

print(getattr(Test, "my_variable"))                     # Error
print(getattr(Test, "my_variable", "Doesn't exist"))    # Doesn't exist

print(getattr(Test, "my_method")())                     # Error
print(getattr(Test, "my_method", "Doesn't exist")())    # Error
print(getattr(Test, "my_method", "Doesn't exist"))      # Doesn't exist

Luego, accediendo a los atributos de la clase de prueba con object y getattr() se obtienen los resultados como se muestra a continuación:

obj = Test()
print(getattr(obj, "class_variable"))                  # Class variable
print(getattr(obj, "instance_variable"))               # Instance variable

getattr(obj, "instance_method")("Instance method")     # Instance method
getattr(obj, "instance_method")("", "Instance method") # Error
getattr(obj, "class_method")("Class method")           # Class method
getattr(obj, "static_method")("Static method")         # Static method

print(getattr(obj, "my_variable"))                     # Error
print(getattr(obj, "my_variable", "Doesn't exist"))    # Doesn't exist

print(getattr(obj, "my_method")())                     # Error
print(getattr(obj, "my_method", "Doesn't exist")())    # Error
print(getattr(obj, "my_method", "Doesn't exist"))      # Doesn't exist

Su guía para un futuro mejor - libreflare
Su guía para un futuro mejor - libreflare