2013-04-29 10 views
26

Jestem bardzo nowy dla Pythona i chciałbym móc wykonać notację ., aby uzyskać dostęp do obiektu dict w python.Jak korzystać z notacji kropek dla dict w python?

Powiedzmy mam test takiego:

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

Ale chciałabym zrobić test.name dostać value. Rzeczywiście zrobiłem to poprzez nadpisanie metody __getattr__ w mojej klasie tak:

class JuspayObject: 

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

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

i to działa! kiedy zrobić:

test.name //I get value. 

Ale problemem jest to, kiedy po prostu wydrukować test sam pojawia się błąd jak:

'Sorry no key matches' 

dlaczego tak się dzieje !!!

Z góry dziękuję.

+0

Musisz zadzwonić do super klasy __getattr__, gdy pytasz o atrybut, którego nie ma w twoim dyktecie. –

+0

@DavidHeffernan Czy klasa w starym stylu, taka jak OP, ma nawet nadklasę? – Aya

+0

@Aya Nie mam pojęcia. Jeśli nie, użyj nowej klasy stylu. Kto i tak nadal korzysta ze starych klas? –

Odpowiedz

23

Zakładam, że są wygodne w JavaScript i chcą pożyczać tego rodzaju składnia ... Mogę powiedzieć z własnego doświadczenia, że ​​to nie jest świetny pomysł.

Z pewnością wygląda mniej gadatliwie i schludnie; ale na dłuższą metę jest po prostu niejasna. Dicts są dyktowane, a próba sprawienia, by zachowywały się jak obiekty z atrybutami prawdopodobnie doprowadzi do (złych) niespodzianek.

Jeśli trzeba manipulować pól obiektu, jak gdyby były słownikiem, zawsze można uciec się do korzystania z wewnętrznego atrybut __dict__ gdy jest to potrzebne, i to jest wyraźnie jasne, co robisz. Lub użyj getattr(obj, 'key'), aby uwzględnić strukturę dziedziczenia i atrybuty klas.

Ale czytając twój przykład wydaje się, że próbujesz czegoś innego ... Jako operator kropki będzie już wyglądał w atrybucie __dict__ bez dodatkowego kodu.

+3

Przykład złego zaskoczenia: jeśli masz klucz w dykcie, który jest słowem kluczowym Pythona, np. ciąg '' for'', a następnie dostęp do atrybutu kończy się niepowodzeniem i nie ma eleganckiego sposobu na poprawną obsługę tego przypadku. Cała idea jest zasadniczo zepsuta od samego początku. – wim

+0

Z drugiej strony automatyczne uzupełnianie powoduje, że kod jest bardziej wydajny i mniej podatny na błędy. To by działało idealnie * Jeśli struktura dyktowana może być wstępnie zdefiniowana. * – roundar

2

można użyć named tuple?

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) 

+0

To jest interesujący sposób dostępu do struktury danych za pomocą notacji kropkowanej, ale nie wydaje się być szczególnie kompatybilny z JSON lub dyktatem. Istnieją biblioteki, które używają nazwanych krotek pod okładkami, które zapewniają obsługę JSON i dyktowanie. Spróbuj https://github.com/dsc/bunch lub https://github.com/kennknowles/python-jsonpath-rw – MarkHu

28

tę funkcjonalność już exists in the standard libraries, więc polecam po prostu użyć swojej klasie.

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

Dodawanie, modyfikowanie i usuwanie wartości uzyskuje się z regularnego dostępu atrybutu, to można użyć stwierdzenia typu n.key = val i del n.key.

Aby powrócić do dict ponownie:

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

Oczywiście, klawisze twojej dict powinny być identyfikatory ciągów za dostęp atrybutu działał poprawnie.

Prosta przestrzeń nazw została dodana w Pythonie 3.3. W starszych wersjach językowych argparse.Namespace ma podobne zachowanie.

+0

[Fabric] (http://www.fabfile.org) również ma ładne minimalne [wdrożenie ] (https://github.com/fabric/fabric/blob/1.13.1/fabric/utils.py#L186), które również opublikowałem [tutaj] (http://stackoverflow.com/a/41274937/307614). – Dave

2

Należy zachować ostrożność podczas używania __getattr__, ponieważ jest on używany do wielu wbudowanych funkcji Pythona.

spróbować czegoś takiego ...

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

'__getattr__' jest używany tylko jako rezerwowy, jeśli nie jest dopasowana żadna inna reguła wyszukiwania, więc nie trzeba szukać' self .__ dict __ [key] ', to już zostało zrobione, jeśli zostanie wywołane' __getattr__'. Po prostu podniesienie 'AttributeError', jeśli nie powiodło się wyszukiwanie na self._response ['key']. –

+0

@brunodesthuilliers Wygląda na to, że masz rację. Dziwne. Może to było potrzebne tylko w czasach Pythona v1.5. – Aya

2

__getattr__ jest użyta w przypadku gdy wszystkie inne zasady lookup atrybut nie powiodły się. Kiedy próbujesz "wydrukować" swój obiekt, Python szuka metody __repr__, a ponieważ nie implementujesz jej w swojej klasie, kończy się ona wywoływaniem __getattr__ (tak, w Pythonie również są to atrybuty). Nie powinieneś zakładać, który klucz getattr zostanie wywołany z, a co najważniejsze, __getattr__ musi wywołać AttributeError, jeśli nie może rozwiązać key.

Na marginesie: nie stosować self.__dict__ dla zwykłego dostępu atrybutu, wystarczy użyć zwykłego notacji atrybutu:

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) 

Teraz, jeśli klasa ma żadnej innej odpowiedzialności (i wersja Pythona jest> = 2,6 i nie musisz obsługiwać starszych wersji), możesz po prostu użyć nazwanego dziesiętnie: http://docs.python.org/2/library/collections.html#collections.namedtuple