Qu'est-ce que getattr () exactement et comment l'utiliser?

295

J'ai récemment lu sur la getattr()fonction . Le problème est que je n'arrive toujours pas à saisir l'idée de son utilisation. La seule chose que je comprends, getattr()c'est getattr(li, "pop")la même chose que d'appeler li.pop.

Je ne comprenais pas quand le livre mentionnait comment vous l'utilisez pour obtenir une référence à une fonction sans connaître son nom jusqu'à l'exécution. C'est peut-être moi d'être un noob en programmation, en général. Quelqu'un pourrait-il faire la lumière sur le sujet? Quand et comment dois-je l'utiliser exactement?

Terence Ponce
la source
Avec quelle partie avez-vous des problèmes? Attributs sous forme de chaînes? Fonctions de première classe?
Ignacio Vazquez-Abrams
1
Je pense que mon problème est de comprendre le concept de getattr (). Je ne comprends toujours pas son but.
Terence Ponce
@Terence ma réponse ne clarifie-t-elle pas les choses?
Alois Cochard
@Alois, votre réponse a définitivement dissipé certains de mes doutes, mais je ne comprends toujours pas à quoi sert getattr ().
Terence Ponce
6
@ S.Lott, je l'ai fait. La documentation n'avait que la définition, donc j'étais un peu confus quant à son utilisation. Je comprends maintenant getattr après avoir lu plus à ce sujet.
Terence Ponce

Réponses:

88

getattr(object, 'x') est complètement équivalent à object.x.

Il n'y a que deux cas où cela getattrpeut être utile.

  • vous ne pouvez pas écrire object.x, car vous ne savez pas à l'avance quel attribut vous voulez (il provient d'une chaîne). Très utile pour la méta-programmation.
  • vous souhaitez fournir une valeur par défaut. object.ysoulèvera un AttributeErrors'il n'y a pas y. Mais getattr(object, 'y', 5)reviendra 5.
note bleue
la source
2
Je pense que cela devrait être la réponse acceptée. Très clair et précis.
yuqli
290

Les objets en Python peuvent avoir des attributs - des attributs de données et des fonctions pour fonctionner avec ces (méthodes). En fait, chaque objet a des attributs intégrés.

Par exemple , vous avez un objet person, qui a plusieurs attributs: name, gender, etc.

Vous accédez à ces attributs (que ce soit des méthodes ou des objets de données) écrit habituellement: person.name, person.gender,person.the_method() , etc.

Mais que faire si vous ne connaissez pas le nom de l'attribut au moment où vous écrivez le programme? Par exemple, le nom de l'attribut est stocké dans une variable appelée attr_name.

si

attr_name = 'gender'

alors, au lieu d'écrire

gender = person.gender

tu peux écrire

gender = getattr(person, attr_name)

Un peu de pratique:

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

getattraugmentera AttributeErrorsi l'attribut avec le nom donné n'existe pas dans l'objet:

>>> getattr(person, 'age')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Person' object has no attribute 'age'

Mais vous pouvez passer une valeur par défaut comme troisième argument, qui sera retourné si cet attribut n'existe pas:

>>> getattr(person, 'age', 0)
0

Vous pouvez utiliser getattravecdir pour parcourir tous les noms d'attributs et obtenir leurs valeurs:

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

Une utilisation pratique pour cela serait de trouver toutes les méthodes dont les noms commencent par test et de les appeler .

Similaire à getattril y a setattrqui vous permet de définir un attribut d'un objet ayant son nom:

>>> setattr(person, 'name', 'Andrew')
>>> person.name  # accessing instance attribute
'Andrew'
>>> Person.name  # accessing class attribute
'Victor'
>>>
warvariuc
la source
9
Il me semble donc que cela getattr(..)devrait être utilisé dans 2 scénarios: 1. lorsque le nom d'attribut est une valeur à l'intérieur d'une variable (par exemple getattr(person, some_attr)) et 2. lorsque nous devons utiliser le troisième argument positionnel pour la valeur par défaut (par exemple getattr(person, 'age', 24)). Si je vois un scénario comme getattr(person, 'age')celui-ci, il me semble qu'il est identique à person.agece qui m'amène à penser que person.agec'est plus Pythonic. Est-ce exact?
wpcarro
102

Pour moi, getattrc'est plus facile à expliquer de cette façon:

Il vous permet d'appeler des méthodes basées sur le contenu d'une chaîne au lieu de taper le nom de la méthode.

Par exemple, vous ne pouvez pas faire ceci:

obj = MyObject()
for x in ['foo', 'bar']:
    obj.x()

parce que x n'est pas du type builtin, mais str. Cependant, vous POUVEZ faire ceci:

obj = MyObject()
for x in ['foo', 'bar']:
    getattr(obj, x)()

Il vous permet de vous connecter dynamiquement avec des objets en fonction de votre entrée. Je l'ai trouvé utile lorsqu'il s'agit d'objets et de modules personnalisés.

NuclearPeon
la source
2
C'est une réponse assez simple et précise.
user6037143
43

Un cas d'utilisation assez courant pour getattr mappage des données aux fonctions.

Par exemple, dans un cadre Web comme Django ou Pylons, getattril est facile de mapper l'URL d'une demande Web à la fonction qui va la gérer. Si vous regardez sous le capot du routage de Pylons, par exemple, vous verrez que (par défaut, au moins), il coupe l'URL d'une demande, comme:

http://www.example.com/customers/list

en "clients" et "liste". Il recherche ensuite une classe de contrôleur nommée CustomerController. En supposant qu'il trouve la classe, il crée une instance de la classe et l'utilise ensuite getattrpour obtenir sa listméthode. Il appelle ensuite cette méthode, en lui passant la demande comme argument.

Une fois que vous avez compris cette idée, il devient très facile d'étendre les fonctionnalités d'une application Web: ajoutez simplement de nouvelles méthodes aux classes de contrôleur, puis créez des liens dans vos pages qui utilisent les URL appropriées pour ces méthodes. Tout cela est rendu possible par getattr.

Robert Rossney
la source
13

Voici un exemple rapide et sale de la façon dont une classe peut déclencher différentes versions d'une méthode de sauvegarde en fonction du système d'exploitation sur lequel elle est exécutée 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

Utilisons maintenant cette classe dans un exemple:

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()
Josh
la source
7

Outre toutes les réponses étonnantes ici, il existe un moyen d'utiliser getattrpour enregistrer de nombreuses lignes de code et le garder bien ajusté. Cette pensée est venue suite à la représentation épouvantable du code qui peut parfois être une nécessité.

Scénario

Supposons que votre structure de répertoire soit la suivante:

- superheroes.py
- properties.py

Et, vous avez des fonctions pour obtenir des informations sur Thor, Iron Man, Doctor Strangeen superheroes.py. Vous écrivez très intelligemment les propriétés de chacun d'eux properties.pydans un compact dict, puis y accédez.

properties.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'],
}

Supposons maintenant que vous souhaitiez restituer les capacités de chacun d'eux à la demande superheroes.py. Donc, il y a des fonctions comme

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

... et plus de fonctions renvoyant des valeurs différentes en fonction des touches et du super-héros.

Avec l'aide de getattr, vous pourriez faire quelque chose comme:

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

Vous avez considérablement réduit le nombre de lignes de code, de fonctions et de répétition!

Oh et bien sûr, si vous avez des mauvais noms comme properties_of_thorpour les variables, ils peuvent être créés et accessibles en faisant simplement

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

REMARQUE: pour ce problème particulier, il peut y avoir des moyens plus intelligents de gérer la situation, mais l'idée est de donner un aperçu de l'utilisation getattraux bons endroits pour écrire du code plus propre.

unixia
la source
3
# 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')
Kduyehj
la source
2
Pourriez-vous élaborer davantage votre réponse en ajoutant un peu plus de description sur la solution que vous proposez?
abarisone
3

J'utilise parfois getattr(..)pour paresseusement initialiser des attributs d'importance secondaire juste avant qu'ils ne soient utilisés dans le code.

Comparez les éléments suivants:

class Graph(object):
    def __init__(self):
        self.n_calls_to_plot = 0

    #...
    #A lot of code here
    #...

    def plot(self):
        self.n_calls_to_plot += 1

Pour ça:

class Graph(object):
    def plot(self):
        self.n_calls_to_plot = 1 + getattr(self, "n_calls_to_plot", 0)

L'avantage de la deuxième méthode est qu'elle n_calls_to_plotn'apparaît qu'à l'endroit du code où elle est utilisée. Ceci est bon pour la lisibilité, car (1) vous pouvez immédiatement voir quelle valeur il commence en lisant comment il est utilisé, (2) il n'introduit pas de distraction dans la __init__(..)méthode, qui devrait idéalement concerner l'état conceptuel de la classe , plutôt qu'un certain compteur d'utilités qui n'est utilisé que par l'une des méthodes de la fonction pour des raisons techniques, telles que l'optimisation, et n'a rien à voir avec la signification de l'objet.

Evgeni Sergeev
la source
3

Très souvent, lorsque je crée un fichier XML à partir de données stockées dans une classe, je recevrais fréquemment des erreurs si l'attribut n'existait pas ou était de type None. Dans ce cas, mon problème n'était pas de savoir quel était le nom de l'attribut, comme indiqué dans votre question, mais plutôt des données ont-elles été stockées dans cet attribut.

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

Si je faisais hasattrcela, cela reviendrait Truemême si la valeur d'attribut était de type Noneet cela entraînerait l'échec de ma setcommande ElementTree .

hasattr(temp, 'hair')
>>True

Si la valeur d'attribut était de type None, la getattrretournerait également, ce qui entraînerait l'échec de ma setcommande ElementTree .

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

J'utilise la méthode suivante pour m'occuper de ces cas maintenant:

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

C'est quand et comment j'utilise getattr.

btathalon
la source
3

Une autre utilisation de getattr () dans l'implémentation d'une instruction switch en Python. Il utilise à la fois la réflexion pour obtenir le type de cas.

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]))
Jules Damji
la source
Je aime cette réponse , mais s'il vous plaît corriger les petits fautes de frappe
peut
2

setattr ()

Nous utilisons setattr pour ajouter un attribut à notre instance de classe. Nous transmettons l'instance de classe, le nom d'attribut et la valeur.

getattr ()

Avec getattr nous ces valeurs

Par exemple

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

employee = Employee()

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

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

print(value)
kuldeep Mishra
la source
1

Je pense que cet exemple est explicite. Il exécute la méthode du premier paramètre, dont le nom est donné dans le deuxième paramètre.

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')()
Dersu Giritlioğlu
la source
0

Il clarifie également la page 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)

L'âge est: 23

L'âge est: 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'))

Le sexe est: masculin

AttributeError: l'objet "Personne" n'a pas d'attribut "sexe"

Barny
la source
0

J'ai essayé en Python2.7.17

Certains des collègues ont déjà répondu. Cependant, j'ai essayé d'appeler getattr (obj, 'set_value') et cela n'a pas exécuté la méthode set_value, j'ai donc changé pour getattr (obj, 'set_value') () -> Cela aide à invoquer la même chose.

Exemple de code:

Exemple 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
siva balan
la source