Che cos'è esattamente getattr () e come si usa?


295

Di recente ho letto della getattr()funzione . Il problema è che non riesco ancora a capire l'idea del suo utilizzo. L'unica cosa che capisco getattr()è che getattr(li, "pop")è lo stesso della chiamata li.pop.

Non ho capito quando il libro menzionava come lo usi per ottenere un riferimento a una funzione senza conoscerne il nome fino al runtime. Forse sono io a essere un noob in programmazione, in generale. Qualcuno potrebbe fare luce sull'argomento? Quando e come posso usarlo esattamente?


Con quale parte hai problemi? Attributi come stringhe? Funzioni di prima classe?
Ignacio Vazquez-Abrams,

1
Penso che il mio problema sia capire il concetto di getattr (). Ancora non capisco il suo scopo.
Terence Ponce,

@Terence la mia risposta non chiarisce le cose?
Alois Cochard,

@Alois, la tua risposta ha sicuramente chiarito alcuni dei miei dubbi, ma non riesco ancora a capire a cosa serva getattr ().
Terence Ponce,

6
@ S. Lott, l'ho fatto. La documentazione aveva solo la definizione, quindi ero un po 'confuso riguardo al suo utilizzo. Ora capisco getattr dopo aver letto di più a riguardo.
Terence Ponce,

Risposte:


88

getattr(object, 'x') è completamente equivalente a object.x.

Ci sono solo due casi in cui getattrpuò essere utile.

  • non puoi scrivere object.x, perché non sai in anticipo quale attributo desideri (proviene da una stringa). Molto utile per la meta-programmazione.
  • si desidera fornire un valore predefinito. object.ygenererà un AttributeErrorse non c'è y. Ma getattr(object, 'y', 5)tornerà 5.

2
Penso che questa dovrebbe essere la risposta accettata. Molto chiaro e al punto.
yuqli

290

Gli oggetti in Python possono avere attributi: attributi e funzioni dei dati per lavorare con questi (metodi). In realtà, ogni oggetto ha attributi incorporati.

Per esempio si dispone di un oggetto person, che ha diversi attributi: name, gender, etc.

È possibile accedere a questi attributi (che si tratti di metodi o oggetti di dati) di solito la scrittura: person.name, person.gender, person.the_method(), etc.

Ma cosa succede se non si conosce il nome dell'attributo nel momento in cui si scrive il programma? Ad esempio, il nome dell'attributo è memorizzato in una variabile chiamata attr_name.

Se

attr_name = 'gender'

quindi, invece di scrivere

gender = person.gender

tu puoi scrivere

gender = getattr(person, attr_name)

Qualche pratica:

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

getattrgenererà AttributeErrorse l'attributo con il nome specificato non esiste nell'oggetto:

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

Ma puoi passare un valore predefinito come terzo argomento, che verrà restituito se tale attributo non esiste:

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

È possibile utilizzare getattrinsieme dirper scorrere su tutti i nomi degli attributi e ottenere i loro valori:

>>> 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 pratico per questo sarebbe trovare tutti i metodi i cui nomi iniziano con teste chiamarli .

Simile a getattrc'è c'è setattrche ti permette di impostare un attributo di un oggetto che abbia il suo nome:

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

9
Quindi mi sembra che getattr(..)debba essere usato in 2 scenari: 1. quando il nome dell'attributo è un valore all'interno di una variabile (ad es. getattr(person, some_attr)) E 2. quando dobbiamo usare il terzo argomento posizionale per il valore di default (ad es getattr(person, 'age', 24).). Se vedo uno scenario come getattr(person, 'age')questo, mi sembra identico a quello person.ageche mi porta a pensare che person.agesia più Pythonic. È corretto?
wpcarro,

102

Per me, getattrè più semplice spiegare in questo modo:

Ti permette di chiamare metodi basati sul contenuto di una stringa invece di digitare il nome del metodo.

Ad esempio, non puoi farlo:

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

perché x non è del tipo builtin, ma str. Tuttavia, puoi farlo:

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

Ti consente di connetterti dinamicamente con oggetti in base al tuo input. L'ho trovato utile quando ho a che fare con oggetti e moduli personalizzati.


2
Questa è una risposta piuttosto semplice e precisa.
user6037143

43

Un caso d'uso piuttosto comune getattrè il mapping dei dati alle funzioni.

Ad esempio, in un framework Web come Django o Pylons, getattrè semplice mappare l'URL di una richiesta Web alla funzione che lo gestirà. Se guardi sotto il cappuccio dell'instradamento di Pylons, ad esempio, vedrai che (per impostazione predefinita, almeno) taglia l'URL di una richiesta, come:

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

in "clienti" e "elenco". Quindi cerca una classe controller denominata CustomerController. Supponendo che trovi la classe, crea un'istanza della classe e quindi la utilizza getattrper ottenere il suo listmetodo. Quindi chiama quel metodo, passandogli la richiesta come argomento.

Una volta afferrata questa idea, diventa davvero facile estendere la funzionalità di un'applicazione Web: basta aggiungere nuovi metodi alle classi del controller e quindi creare collegamenti nelle pagine che utilizzano gli URL appropriati per tali metodi. Tutto ciò è reso possibile da getattr.


13

Ecco un esempio rapido e sporco di come una classe potrebbe eseguire diverse versioni di un metodo di salvataggio in base al sistema operativo su cui viene eseguita 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

Ora usiamo questa classe in un esempio:

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

7

Oltre a tutte le risposte sorprendenti qui, c'è un modo per usare getattrper salvare molte righe di codice e tenerlo comodo. Questo pensiero venne dopo la terribile rappresentazione del codice che a volte poteva essere una necessità.

Scenario

Supponiamo che la struttura della directory sia la seguente:

- superheroes.py
- properties.py

E, si dispone di funzioni per ottenere informazioni su Thor, Iron Man, Doctor Strangein superheroes.py. Scrivi in ​​modo molto intelligente le proprietà di tutti loro properties.pyin un compatto dicte quindi accedervi.

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

Ora, supponiamo che tu voglia restituire le capacità di ciascuno di essi su richiesta superheroes.py. Quindi, ci sono funzioni come

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

... e più funzioni che restituiscono valori diversi in base alle chiavi e al supereroe.

Con l'aiuto di getattr, potresti fare qualcosa del tipo:

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

Hai notevolmente ridotto il numero di righe di codice, funzioni e ripetizioni!

Oh, e ovviamente, se hai nomi cattivi come properties_of_thorper le variabili, puoi crearle e accedervi semplicemente

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

NOTA: per questo particolare problema, ci possono essere modi più intelligenti per affrontare la situazione, ma l'idea è quella di dare un'idea dell'utilizzo getattrnei posti giusti per scrivere un codice più pulito.


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

2
Potresti elaborare di più la tua risposta aggiungendo un po 'più di descrizione della soluzione che offri?
abarisone,

3

A volte uso getattr(..)per inizializzare pigramente attributi di importanza secondaria appena prima che vengano utilizzati nel codice.

Confronta quanto segue:

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

A questa:

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

Il vantaggio del secondo modo è che n_calls_to_plotappare solo intorno al punto nel codice in cui viene utilizzato. Questo è buono per la leggibilità, perché (1) puoi immediatamente vedere con quale valore inizia leggendo come viene usato, (2) non introduce una distrazione nel __init__(..)metodo, che idealmente dovrebbe riguardare lo stato concettuale della classe , piuttosto che qualche contatore di utilità che viene utilizzato solo da uno dei metodi della funzione per motivi tecnici, come l'ottimizzazione, e non ha nulla a che fare con il significato dell'oggetto.


3

Abbastanza frequentemente quando creo un file XML da dati archiviati in una classe, riceverei frequentemente errori se l'attributo non esistesse o fosse di tipo None. In questo caso, il mio problema non era sapere quale fosse il nome dell'attributo, come indicato nella tua domanda, ma piuttosto i dati venivano mai memorizzati in quell'attributo.

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

Se lo hasattrfacessi, ritornerebbe Trueanche se il valore dell'attributo fosse di tipo Nonee questo causerebbe il fallimento del mio setcomando ElementTree .

hasattr(temp, 'hair')
>>True

Se il valore dell'attributo fosse di tipo None, getattrlo restituirebbe anche, il che provocherebbe il fallimento del mio setcomando ElementTree .

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

Uso il seguente metodo per occuparmi di questi casi ora:

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

Questo è quando e come lo uso getattr.


3

Un altro uso di getattr () nell'implementazione di un'istruzione switch in Python. Utilizza entrambe le riflessioni per ottenere il tipo di 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]))

Mi piace questa risposta ma vi prego di correggere i piccoli errori di battitura
maggio

2

setattr ()

Usiamo setattr per aggiungere un attributo alla nostra istanza di classe. Passiamo l'istanza della classe, il nome dell'attributo e il valore.

getattr ()

Con getattr riportiamo questi valori

Per esempio

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

employee = Employee()

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

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

print(value)

1

Penso che questo esempio sia autoesplicativo. Esegue il metodo del primo parametro, il cui nome è dato nel secondo parametro.

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')()

0

Chiarisce anche da 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'età è: 23 anni

L'età è: 23 anni

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

Il sesso è: maschio

AttributeError: l'oggetto 'Person' non ha attributo 'sex'


0

Ho provato in Python2.7.17

Alcuni colleghi hanno già risposto. Comunque ho provato a chiamare getattr (obj, 'set_value') e questo non ha eseguito il metodo set_value, quindi ho cambiato in getattr (obj, 'set_value') () -> Questo aiuta a invocare lo stesso.

Codice di esempio:

Esempio 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.