Atrybuty obiektów Pythona - metodologia dostępu

Załóżmy, że mam klasę z pewnymi atrybutami. Jak najlepiej (w sensie Pythonic-OOP) uzyskać dostęp do tych atrybutów ? Tak jak obj.attr ? A może napisz get accessors ? Jakie są akceptowane style nazewnictwa dla takich rzeczy ?

Edit: Czy możesz rozwinąć najlepsze praktyki nazywania atrybutów z pojedynczym lub podwójnym podkreśleniem wiodącym ? Widzę, że w większości modułów używany jest pojedynczy podkreślnik.


Jeśli to pytanie zostało już zadane (a ja mam przeczucie ma, choć wyszukiwanie nie przyniosło rezultatów), proszę wskazać na to - a ten zamknę.

Author: user, 2008-10-03

7 answers

Ogólnie przyjętym sposobem robienia rzeczy jest używanie prostych atrybutów, tak jak

>>> class MyClass:
...     myAttribute = 0
... 
>>> c = MyClass()
>>> c.myAttribute 
0
>>> c.myAttribute = 1
>>> c.myAttribute
1

Jeśli uważasz, że potrzebujesz umieć pisać gettery i settery, to czego chcesz szukać to "właściwości klasy Pythona" i Artykuł Ryana Tomayko na temat Getters / Setters / Fuxors to świetne miejsce na początek (choć trochę długie)

 23
Author: willurd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-09 16:51:51

W odniesieniu do podkreślenia pojedynczego i podwójnego: oba wskazują na to samo pojęcie "privateness". Oznacza to, że ludzie będą wiedzieć, że atrybut (czy to metoda, czy "normalny" atrybut danych lub cokolwiek innego) nie jest częścią publicznego API obiektu. Ludzie będą wiedzieć, że dotykanie go bezpośrednio to zaproszenie do katastrofy.

Dodatkowo atrybuty podkreślenia (ale nie atrybuty podkreślenia pojedynczego) są wymazane z nazwy , aby dostęp do nich przez przypadek Z podklas lub gdziekolwiek indziej poza obecną klasą jest mniej prawdopodobny. Nadal można uzyskać do nich dostęp, ale nie tak trywialnie. Na przykład:

>>> class ClassA:
...     def __init__(self):
...         self._single = "Single"
...         self.__double = "Double"
...     def getSingle(self):
...         return self._single
...     def getDouble(self):
...         return self.__double
... 
>>> class ClassB(ClassA):
...     def getSingle_B(self):
...         return self._single
...     def getDouble_B(self):
...         return self.__double
... 
>>> a = ClassA()
>>> b = ClassB()

Możesz teraz uzyskać trywialny dostęp do a._single i b._single i uzyskać atrybut _single utworzony przez ClassA:

>>> a._single, b._single
('Single', 'Single')
>>> a.getSingle(), b.getSingle(), b.getSingle_B()
('Single', 'Single', 'Single')

Ale próba bezpośredniego dostępu do atrybutu __double na instancji a lub b nie zadziała:

>>> a.__double
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: ClassA instance has no attribute '__double'
>>> b.__double
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: ClassB instance has no attribute '__double'

I chociaż metody zdefiniowane w ClassA mogą dostać się do niego bezpośrednio (gdy wywołane NA każda instancja):

>>> a.getDouble(), b.getDouble()
('Double', 'Double')

Metody zdefiniowane na ClassB nie mogą:

>>> b.getDouble_B()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in getDouble_B
AttributeError: ClassB instance has no attribute '_ClassB__double'

I właśnie w tym błędzie dostajesz podpowiedź o tym, co się dzieje. Atrybut __double name, gdy jest dostępny wewnątrz klasy, jest zmieniany tak, aby zawierał nazwę klasy, do której jest dostępny w. Kiedy ClassA próbuje uzyskać dostęp self.__double, w rzeczywistości zamienia się -- w czasie kompilacji -- w dostęp self._ClassA__double, a także dla ClassB. (Jeśli metoda w {[14] } miała przypisać do __double, nie uwzględniona w kodzie dla zwięzłości, nie dotyka ClassA ' s __double, ale tworzy nowy atrybut.) Nie ma innej ochrony tego atrybutu, więc nadal możesz uzyskać do niego bezpośredni dostęp, jeśli znasz właściwą nazwę: {]}

>>> a._ClassA__double, b._ClassA__double
('Double', 'Double')

Więc dlaczego to jest problem?

Cóż, jest to problem, kiedy chcesz dziedziczyć i zmieniać zachowanie dowolnego kodu, który ma do czynienia z tym atrybutem. Albo musisz ponownie zaimplementować wszystko, co bezpośrednio dotyka tego atrybutu podwójnego podkreślenia, albo musisz odgadnąć nazwę klasy i ręcznie zmienić nazwę. Problem pogarsza się, gdy atrybut double-underscore jest rzeczywiście metodą: nadpisanie metody lub wywołanie metody w podklasie oznacza ręczne zmienianie nazwy lub ponowne zaimplementowanie całego kodu, który wywołuje metodę, aby nie używać nazwy double-underscore. Nie wspominając już o dostępie do atrybutu dynamicznie, za pomocą getattr(): będziesz musiał tam również ręcznie sterować.

Z drugiej strony, ponieważ atrybut jest tylko trywialnie przepisany, oferuje jedynie powierzchowną "ochronę". Każdy fragment kodu może dostać się do atrybutu przez ręczne zniekształcanie, chociaż spowoduje to, że ich kod będzie zależny od nazwy twojej klasy , a wysiłki po twojej stronie, aby refaktorować kod lub zmienić nazwę klasy (zachowując tę samą nazwę widoczną dla użytkownika, co jest powszechną praktyką w Pythonie) niepotrzebnie złamałyby ich kod. Mogą również "oszukać" pytona, aby zrobił namaglowanie nazwy dla nich, nazywając ich klasę tak samo jak twoją: zauważ, że nie ma nazwy modułu zawartej w zniekształconej nazwie atrybutu. I wreszcie, atrybut double-underscore jest nadal widoczny we wszystkich listach atrybutów i we wszystkich formach introspekcji, które nie dbają o pomijanie atrybutów zaczynających się od (single) podkreślenia.

Więc, jeśli używasz nazw z podwójnym podkreśleniem, używaj ich wyjątkowo oszczędnie, ponieważ mogą okazać się dość niewygodne i nigdy nie używaj ich do metod lub cokolwiek innego podklasa może chcieć reimplementować, nadpisać lub uzyskać bezpośredni dostęp. I zdaj sobie sprawę z tego, że podwojenie znaków podkreślenia nie daje żadnej prawdziwej ochrony. W końcu użycie jednego czołowego podkreślenia wygrywa tak samo dużo i daje mniej bólu (potencjalnego, przyszłego). Użyj pojedynczego podkreślenia.

 56
Author: Thomas Wouters,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-15 09:50:48

Edit: czy możesz rozwinąć najlepsze praktyki nazywania atrybutów z pojedynczym lub podwójnym podkreśleniem wiodącym ? Widzę, że w większości modułów używany jest pojedynczy podkreślnik.

Pojedynczy podkreślenie nie oznacza nic specjalnego dla Pythona, jest to po prostu najlepsza praktyka, aby powiedzieć "Hej, prawdopodobnie nie chcesz uzyskać dostępu do tego, chyba że wiesz, co robisz". Podwójne podkreślenie sprawia jednak, że python mangle nazwę wewnętrznie czyni ją dostępną tylko z klasy, w której jest zdefiniowany.

Podwójny początek i koniec podkreślenia oznacza specjalną funkcję, taką jak __add__, która jest wywoływana przy użyciu operatora+.

Czytaj więcej w PEP 8 , szczególnie w sekcji "konwencje nazewnictwa".

 8
Author: Anders Waldenborg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-15 09:52:44

Myślę, że większość po prostu uzyskuje do nich bezpośredni dostęp, bez potrzeby stosowania metod get/set.

>>> class myclass:
...     x = 'hello'
...
>>>
>>> class_inst = myclass()
>>> class_inst.x
'hello'
>>> class_inst.x = 'world'
>>> class_inst.x
'world'

BTW, możesz użyć funkcji dir (), aby zobaczyć, jakie atrybuty/metody są dołączone do Twojej instancji:

>>> dir(class_inst)
['__doc__', '__module__', 'x']

Dwa przednie paski "_ _ " są używane, aby uczynić atrybut lub funkcję prywatną. W przypadku innych konwencji patrz PEP 08: http://www.python.org/dev/peps/pep-0008/

 3
Author: monkut,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-03 06:31:41

Python nie musi definiować accesorów od samego początku, ponieważ Zamiana atrybutów na właściwości jest szybka i bezbolesna. Zobacz następujące, aby zobaczyć żywą demonstrację:

Wyzdrowienie z uzależnienia

 1
Author: Ignacio Vazquez-Abrams,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-03 06:26:39

Nie ma sensu robić getter / setters w Pythonie, nie możesz i tak chronić rzeczy i jeśli potrzebujesz wykonać dodatkowy kod podczas pobierania / ustawiania właściwości, spójrz na właściwość () builtin(python-C 'help (property)')

 0
Author: Anders Waldenborg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-03 06:43:09

Niektórzy używają getterów i seterów. W zależności od używanego stylu kodowania można je nazwać getSpam i seteggs. Ale można również dokonać atrybuty tylko readonly lub przypisać tylko. To trochę niezręczne. One way is overriding the

> __getattr__

I

> __setattr__

Metody.

Edit:

Chociaż moja odpowiedź jest nadal prawdziwa, to nie jest w porządku, jak się zorientowałem. Istnieją lepsze sposoby tworzenia accessorów w Pythonie i nie są zbyt niewygodne.
 -2
Author: Vasil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-03 11:27:09