Wie verwende ich die Punktnotation für das Diktieren in Python?

77

Ich bin sehr neu in Python und ich wünschte, ich könnte .Notation machen, um auf Werte von a zuzugreifen dict.

Nehmen wir an, ich habe folgendes test:

>>> test = dict()
>>> test['name'] = 'value'
>>> print(test['name'])
value

Aber ich wünschte, ich könnte tun test.name, um zu bekommen value. Tatsächlich habe ich die __getattr__Methode in meiner Klasse folgendermaßen überschrieben :

class JuspayObject:

    def __init__(self,response):
        self.__dict__['_response'] = response

    def __getattr__(self,key): 
        try:
            return self._response[key]
        except KeyError,err:
            sys.stderr.write('Sorry no key matches')

und das funktioniert! wenn ich es tue:

test.name // I get value.

Aber das Problem ist, wenn ich nur testalleine drucke, bekomme ich den Fehler als:

'Sorry no key matches'

Warum passiert das?

Batman
quelle
Sie müssen super class getattr aufrufen, wenn Sie nach Attributen fragen, die nicht in Ihrem Diktat enthalten sind.
David Heffernan
@DavidHeffernan Hat eine Klasse im alten Stil wie das Beispiel des OP überhaupt eine Oberklasse?
Aya
@Aya Keine Ahnung. Wenn nicht, verwenden Sie eine neue Stilklasse. Wer benutzt noch alte Klassen?
David Heffernan
1
@DavidHeffernan Nun, es gibt immer noch ziemlich viele Klassen im alten Stil in der Standard-Python-Bibliothek, z cgi.py.
Aya

Antworten:

142

Diese Funktionalität ist bereits in den Standardbibliotheken vorhanden. Ich empfehle daher, nur deren Klasse zu verwenden.

>>> from types import SimpleNamespace
>>> d = {'key1': 'value1', 'key2': 'value2'}
>>> n = SimpleNamespace(**d)
>>> print(n)
namespace(key1='value1', key2='value2')
>>> n.key2
'value2'

Das Hinzufügen, Ändern und Entfernen von Werten wird durch regulären Attributzugriff erreicht, dh Sie können Anweisungen wie n.key = valund verwenden del n.key.

Um noch einmal zu einem Diktat zurückzukehren:

>>> vars(n)
{'key1': 'value1', 'key2': 'value2'}

Die Schlüssel in Ihrem Diktat sollten Zeichenfolgenkennungen sein , damit der Attributzugriff ordnungsgemäß funktioniert.

In Python 3.3 wurde ein einfacher Namespace hinzugefügt. Hat für ältere Versionen der Sprache ein argparse.Namespaceähnliches Verhalten.

wim
quelle
2
Fabric hat auch eine schöne minimale Implementierung , die ich auch hier gepostet habe .
Dave
4
Wäre schön, wenn Sie dies wiederholen könnten .. dh Zugriffmyobj.subdict.subdict
Pithikos
2
@Pithikos Es gibt eine Drittanbieter-Bibliothek, die diese Funktionalität bereitstellt. Schauen Sie sich die Python-Box an .
wim
cool, funktioniert aber nicht mit verschachtelten Diktaten ...
eadmaster
@eadmaster Hast du die beiden Kommentare direkt über deinen gelesen?
wim
41

Ich gehe davon aus, dass Sie sich in Javascript wohl fühlen und diese Art von Syntax ausleihen möchten ... Ich kann Ihnen aus eigener Erfahrung sagen, dass dies keine großartige Idee ist.

Es sieht sicher weniger ausführlich und ordentlich aus; aber auf lange Sicht ist es nur dunkel. Diktate sind Diktate, und der Versuch, sie wie Objekte mit Attributen zu verhalten, wird wahrscheinlich zu (schlechten) Überraschungen führen.

Wenn Sie die Felder eines Objekts so bearbeiten müssen, als wären sie ein Wörterbuch, können Sie jederzeit auf das interne __dict__Attribut zurückgreifen, wenn Sie es benötigen. Dann ist explizit klar, was Sie tun. Oder verwenden Sie getattr(obj, 'key'), um auch die Vererbungsstruktur und die Klassenattribute zu berücksichtigen.

Wenn Sie jedoch Ihr Beispiel lesen, versuchen Sie anscheinend etwas anderes ... Da der Punktoperator das __dict__Attribut bereits ohne zusätzlichen Code durchsucht.

fortran
quelle
10
Beispiel für eine schlechte Überraschung: Wenn Sie im Diktat einen Schlüssel haben, der zufällig ein Python-Schlüsselwort ist, z. B. die Zeichenfolge 'for', schlägt der Attributzugriff fehl und es gibt keine elegante Möglichkeit, diesen Fall richtig zu behandeln. Die ganze Idee ist von Anfang an grundlegend gebrochen.
wim
4
Auf der anderen Seite sorgt die automatische Vervollständigung für effizienteren, weniger fehleranfälligen Code. Das würde perfekt funktionieren, wenn die Diktatstruktur vordefiniert werden kann.
Roundar
7

Könnten Sie ein benanntes Tupel verwenden?

from collections import namedtuple
Test = namedtuple('Test', 'name foo bar')
my_test = Test('value', 'foo_val', 'bar_val')
print(my_test)
print(my_test.name)

Yann
quelle
2
Dies ist eine interessante Möglichkeit, über eine gepunktete Notation auf eine Datenstruktur zuzugreifen, scheint jedoch nicht besonders kompatibel mit JSON oder dict zu sein. Es gibt Bibliotheken, die benannte Tupel verwenden, die JSON- und Dikt-Unterstützung bieten. Versuchen Sie es mit github.com/dsc/bunch oder github.com/kennknowles/python-jsonpath-rw
MarkHu
für Python> = 3.6, berücksichtigen Siefrom typing import NamedTuple
user9074332
6

Zusätzlich zu dieser Antwort kann man auch Unterstützung für verschachtelte Dikte hinzufügen:

from types import SimpleNamespace

class NestedNamespace(SimpleNamespace):
    def __init__(self, dictionary, **kwargs):
        super().__init__(**kwargs)
        for key, value in dictionary.items():
            if isinstance(value, dict):
                self.__setattr__(key, NestedNamespace(value))
            else:
                self.__setattr__(key, value)

nested_namespace = NestedNamespace({
    'parent': {
        'child': {
            'grandchild': 'value'
        }
    },
    'normal_key': 'normal value',
})


print(nested_namespace.parent.child.grandchild)  # value
print(nested_namespace.normal_key)  # normal value

Beachten Sie, dass dies keine Punktnotation für Diktate unterstützt, die sich irgendwo in zB Listen befinden.

Michael H.
quelle
5

__getattr__wird als Fallback verwendet, wenn alle anderen Attributsuchregeln fehlgeschlagen sind. Wenn Sie versuchen, Ihr Objekt zu "drucken", sucht Python nach einer __repr__Methode, und da Sie sie nicht in Ihrer Klasse implementieren, wird sie schließlich aufgerufen __getattr__(ja, in Python sind Methoden auch Attribute). Sie sollten nicht davon ausgehen, mit welchem ​​Schlüssel getattr aufgerufen wird, und vor allem __getattr__einen AttributeError auslösen müssen, wenn er nicht aufgelöst werden kann key.

Als Randnotiz: Nicht self.__dict__für den normalen Attributzugriff verwenden, sondern nur die einfache Attributnotation verwenden:

class JuspayObject:

    def __init__(self,response):
        # don't use self.__dict__ here
        self._response = response

    def __getattr__(self,key):
        try:
            return self._response[key]
        except KeyError,err:
            raise AttributeError(key)

Wenn Ihre Klasse keine andere Verantwortung hat (und Ihre Python-Version> = 2.6 ist und Sie ältere Versionen nicht unterstützen müssen), können Sie einfach ein benanntes Tupel verwenden: http://docs.python.org/2/library/ collection.html # collection.namedtuple

bruno desthuilliers
quelle
2

Sie müssen bei der Verwendung vorsichtig sein __getattr__, da es für viele integrierte Python-Funktionen verwendet wird.

Versuchen Sie so etwas ...

class JuspayObject:

    def __init__(self,response):
        self.__dict__['_response'] = response

    def __getattr__(self, key):
        # First, try to return from _response
        try:
            return self.__dict__['_response'][key]
        except KeyError:
            pass
        # If that fails, return default behavior so we don't break Python
        try:
            return self.__dict__[key]
        except KeyError:
            raise AttributeError, key

>>> j = JuspayObject({'foo': 'bar'})
>>> j.foo
'bar'
>>> j
<__main__.JuspayObject instance at 0x7fbdd55965f0>
Aya
quelle
1
__getattr__wird nur als Fallback verwendet, wenn keine andere Suchregel übereinstimmt, sodass keine Suche erforderlich ist. self.__dict__[key]Wenn dies __getattr__aufgerufen wird , wurde dies bereits durchgeführt . Es reicht aus, nur zu erhöhen, AttributeErrorwenn die Suche nach self._response ['key'] fehlgeschlagen ist.
Bruno Desthuilliers
@brunodesthuilliers Sieht so aus, als hättest du recht. Seltsam. Vielleicht war dies früher nur in den Tagen von Python v1.5 notwendig.
Aya
-1

Fügen Sie __repr__()der Klasse eine Methode hinzu, damit Sie den anzuzeigenden Text anpassen können

print text

Weitere Informationen finden Sie hier: https://web.archive.org/web/20121022015531/http://diveintopython.net/object_oriented_framework/special_class_methods2.html

Makyen
quelle
Während dies den in der Frage erwähnten speziellen Fall technisch lösen würde, ist das Hacken des eingebauten Verhaltens zum Patchen anderer Hacks des eingebauten Verhaltens objektiv schlechter Code.
Vdwees