Was ist getattr () genau und wie verwende ich es?

295

Ich habe kürzlich über die getattr()Funktion gelesen . Das Problem ist, dass ich die Idee seiner Verwendung immer noch nicht verstehen kann. Das einzige, was ich verstehe, getattr()ist, dass getattr(li, "pop")es dasselbe ist wie anrufen li.pop.

Ich habe nicht verstanden, wann in dem Buch erwähnt wurde, wie Sie damit einen Verweis auf eine Funktion erhalten, ohne deren Namen bis zur Laufzeit zu kennen. Vielleicht bin ich im Allgemeinen ein Noob in der Programmierung. Könnte jemand etwas Licht in das Thema bringen? Wann und wie verwende ich das genau?

Terence Ponce
quelle
Mit welchem ​​Teil hast du Probleme? Attribute als Zeichenfolgen? Erstklassige Funktionen?
Ignacio Vazquez-Abrams
1
Ich denke, mein Problem ist das Verständnis des Konzepts von getattr (). Ich verstehe seinen Zweck immer noch nicht.
Terence Ponce
@Terence macht meine Antwort die Dinge nicht klarer?
Alois Cochard
@Alois, deine Antwort hat definitiv einige meiner Zweifel ausgeräumt, aber ich kann immer noch nicht ganz verstehen, wofür getattr () ist.
Terence Ponce
6
@ S.Lott, ich habe. Die Dokumentation hatte nur die Definition, so dass ich über ihre Verwendung etwas verwirrt war. Ich verstehe getattr jetzt, nachdem ich mehr darüber gelesen habe.
Terence Ponce

Antworten:

88

getattr(object, 'x') ist völlig gleichbedeutend mit object.x.

Es gibt nur zwei Fälle, in denen getattrdies nützlich sein kann.

  • Sie können nicht schreiben object.x, weil Sie nicht im Voraus wissen, welches Attribut Sie möchten (es stammt aus einer Zeichenfolge). Sehr nützlich für die Metaprogrammierung.
  • Sie möchten einen Standardwert angeben. object.ywird eine erhöhen, AttributeErrorwenn es keine gibt y. Aber getattr(object, 'y', 5)ich werde wiederkommen 5.
blue_note
quelle
2
Ich denke, dies sollte die akzeptierte Antwort sein. Sehr klar und auf den Punkt.
Yuqli
290

Objekte in Python können Attribute haben - Datenattribute und Funktionen, um mit diesen (Methoden) zu arbeiten. Tatsächlich verfügt jedes Objekt über integrierte Attribute.

Zum Beispiel können Sie ein Objekt haben person, hat , dass mehrere Attribute: name, genderusw.

Der Zugriff auf diese Attribute (es Methoden oder Datenobjekte sein) in der Regel zu schreiben: person.name, person.gender, person.the_method()etc.

Aber was ist, wenn Sie den Namen des Attributs zum Zeitpunkt des Schreibens des Programms nicht kennen? Beispielsweise haben Sie den Namen des Attributs in einer Variablen namens gespeichert attr_name.

wenn

attr_name = 'gender'

dann anstatt zu schreiben

gender = person.gender

Du kannst schreiben

gender = getattr(person, attr_name)

Einige Übungen:

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

getattrwird ausgelöst, AttributeErrorwenn das Attribut mit dem angegebenen Namen im Objekt nicht vorhanden ist:

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

Sie können jedoch einen Standardwert als drittes Argument übergeben, der zurückgegeben wird, wenn ein solches Attribut nicht vorhanden ist:

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

Sie können getattrzusammen mit verwenden dir, um alle Attributnamen zu durchlaufen und ihre Werte abzurufen:

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

Eine praktische Anwendung hierfür wäre, alle Methoden zu finden, deren Namen mit beginnen, testund sie aufzurufen .

Ähnlich wie getattrdort setattrkönnen Sie ein Attribut eines Objekts mit seinem Namen festlegen:

>>> setattr(person, 'name', 'Andrew')
>>> person.name  # accessing instance attribute
'Andrew'
>>> Person.name  # accessing class attribute
'Victor'
>>>
warvariuc
quelle
9
Daher scheint es mir, dass getattr(..)dies in zwei Szenarien verwendet werden sollte: 1. wenn der Attributname ein Wert innerhalb einer Variablen ist (z. B. getattr(person, some_attr)) und 2. wenn wir das dritte Positionsargument für den Standardwert verwenden müssen (z getattr(person, 'age', 24). B. ). Wenn ich ein Szenario wie getattr(person, 'age')dieses sehe , scheint es mir identisch zu sein, person.agewas mich zu der person.ageAnnahme veranlasst, dass es mehr pythonisch ist. Ist das korrekt?
wpcarro
102

Für mich getattrist es am einfachsten, dies folgendermaßen zu erklären:

Sie können Methoden basierend auf dem Inhalt einer Zeichenfolge aufrufen, anstatt den Methodennamen einzugeben.

Zum Beispiel können Sie dies nicht tun:

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

weil x nicht vom Typ ist builtin, aber str. Sie können dies jedoch tun:

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

Sie können sich basierend auf Ihrer Eingabe dynamisch mit Objekten verbinden. Ich fand es nützlich, wenn es um benutzerdefinierte Objekte und Module geht.

NuclearPeon
quelle
2
Dies ist eine ziemlich einfache und präzise Antwort.
user6037143
43

Ein ziemlich häufiger Anwendungsfall getattrist das Zuordnen von Daten zu Funktionen.

In einem Webframework wie Django oder Pylons getattrist es beispielsweise einfach, die URL einer Webanforderung der Funktion zuzuordnen, die sie verarbeiten wird. Wenn Sie beispielsweise unter die Haube des Routings von Pylons schauen, werden Sie feststellen, dass (zumindest standardmäßig) die URL einer Anfrage zerlegt wird, wie z.

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

in "Kunden" und "Liste". Dann wird nach einer Controller-Klasse mit dem Namen gesucht CustomerController. Angenommen, es findet die Klasse, erstellt es eine Instanz der Klasse und verwendet sie dann getattr, um ihre zu erhaltenlist Methode abzurufen. Diese Methode wird dann aufgerufen und die Anforderung als Argument übergeben.

Sobald Sie diese Idee verstanden haben, können Sie die Funktionalität einer Webanwendung ganz einfach erweitern: Fügen Sie den Controller-Klassen einfach neue Methoden hinzu und erstellen Sie dann auf Ihren Seiten Links, die die entsprechenden URLs für diese Methoden verwenden. All dies wird ermöglicht durch getattr.

Robert Rossney
quelle
13

Hier ist ein kurzes und schmutziges Beispiel dafür, wie eine Klasse verschiedene Versionen einer Speichermethode auslösen kann, je nachdem, auf welchem ​​Betriebssystem sie ausgeführt wird 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

Verwenden wir diese Klasse nun in einem Beispiel:

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

Abgesehen von all den erstaunlichen Antworten hier gibt es eine Möglichkeit, sie zu verwenden getattr zahlreiche Codezeilen zu speichern und festzuhalten. Dieser Gedanke folgte der schrecklichen Darstellung von Code, die manchmal eine Notwendigkeit sein könnte.

Szenario

Angenommen, Ihre Verzeichnisstruktur lautet wie folgt:

- superheroes.py
- properties.py

Und, haben Sie Funktionen für den Zugriff auf Informationen über Thor, Iron Man, Doctor Strangein superheroes.py. Sie schreiben die Eigenschaften aller sehr intelligent properties.pyin einem Kompakt auf dictund greifen dann darauf zu.

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

Angenommen, Sie möchten die Funktionen der einzelnen Funktionen bei Bedarf in zurückgeben superheroes.py. Es gibt also Funktionen wie

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

... und weitere Funktionen, die basierend auf den Tasten und dem Superhelden unterschiedliche Werte zurückgeben.

Mit Hilfe von getattrkönnen Sie Folgendes tun:

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

Sie haben die Anzahl der Codezeilen, Funktionen und Wiederholungen erheblich reduziert!

Oh und natürlich, wenn Sie schlechte Namen wie properties_of_thorfür Variablen haben, können diese durch einfaches Ausführen erstellt und aufgerufen werden

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

HINWEIS: Für dieses spezielle Problem kann es intelligentere Möglichkeiten geben, mit der Situation umzugehen. Die Idee ist jedoch, einen Einblick in die Verwendung getattran den richtigen Stellen zum Schreiben von sauberem Code zu geben.

Unixia
quelle
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
quelle
2
Könnten Sie Ihre Antwort näher erläutern und etwas mehr Beschreibung der von Ihnen bereitgestellten Lösung hinzufügen?
Abarisone
3

Ich verwende es manchmal getattr(..), Attribute von sekundärer Bedeutung träge zu initialisieren, kurz bevor sie im Code verwendet werden.

Vergleichen Sie Folgendes:

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

Dazu:

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

Der Vorteil des zweiten Weges besteht darin, dass er n_calls_to_plotnur an der Stelle im Code angezeigt wird, an der er verwendet wird. Dies ist gut für die Lesbarkeit, da (1) Sie sofort sehen können, mit welchem ​​Wert es beginnt, wenn Sie lesen, wie es verwendet wird, (2) es keine Ablenkung in die __init__(..)Methode einführt , die idealerweise den konzeptuellen Zustand der Klasse betreffen sollte anstelle eines Dienstprogrammzählers, der nur von einer der Methoden der Funktion aus technischen Gründen wie der Optimierung verwendet wird und nichts mit der Bedeutung des Objekts zu tun hat.

Evgeni Sergeev
quelle
3

Sehr häufig, wenn ich eine XML-Datei aus Daten erstelle, die in einer Klasse gespeichert sind, erhalte ich häufig Fehler, wenn das Attribut nicht vorhanden oder vom Typ war None. In diesem Fall wusste mein Problem nicht, wie der Attributname laut Ihrer Frage lautete, sondern es wurden Daten jemals in diesem Attribut gespeichert.

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

Wenn ich dies früher hasattrgetan hätte, würde es zurückgegeben, Trueselbst wenn der Attributwert vom Typ wäre, Noneund dies würde dazu führen, dass mein ElementTree- setBefehl fehlschlägt.

hasattr(temp, 'hair')
>>True

Wenn der Attributwert vom Typ wäre None, getattrwürde er auch zurückgegeben, was dazu führen würde, dass mein ElementTree- setBefehl fehlschlägt.

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

Ich benutze jetzt die folgende Methode, um diese Fälle zu behandeln:

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

Dies ist, wann und wie ich benutze getattr.

Btathalon
quelle
3

Eine andere Verwendung von getattr () beim Implementieren einer switch-Anweisung in Python. Es verwendet beide Reflexionen, um den Falltyp zu erhalten.

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
quelle
Ich mag diese Antwort aber bitte die kleinen Fehler beheben
kann
2

setattr ()

Wir verwenden setattr , um unserer Klasseninstanz ein Attribut hinzuzufügen. Wir übergeben die Klasseninstanz, den Attributnamen und den Wert.

getattr ()

Mit getattr rufen wir diese Werte ab

Beispielsweise

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

Ich denke, dieses Beispiel ist selbsterklärend. Es wird die Methode des ersten Parameters ausgeführt, dessen Name im zweiten Parameter angegeben ist.

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

Dies wird auch unter https://www.programiz.com/python-programming/methods/built-in/getattr erläutert

class Person:
    age = 23
    name = "Adam"

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

Das Alter ist: 23

Das Alter ist: 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'))

Das Geschlecht ist: männlich

AttributeError: Das Objekt 'Person' hat kein Attribut 'Geschlecht'.

Scheune
quelle
0

Ich habe es in Python2.7.17 versucht

Einige der Kollegen haben bereits geantwortet. Ich habe jedoch versucht, getattr (obj, 'set_value') aufzurufen, und dies hat die set_value-Methode nicht ausgeführt. Also habe ich zu getattr (obj, 'set_value') () geändert -> - Dies hilft, dasselbe aufzurufen.

Beispielcode:

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