Discussion:
__iter__ - obiekty iterowalne a iteratory
(Wiadomość utworzona zbyt dawno temu. Odpowiedź niemożliwa.)
semisiu
2007-11-09 22:45:23 UTC
Permalink
Witam :)

Jestem początkującym pythonowcem i mam pytanie w związku z obiektami
iterowalnymi. Wyobraźmy sobie, że mam taką klasę:


class Zwierzatka:
def __init__(self):
self._lista = []
def dodaj(self, imie, wiek):
self._lista.append({'imie': imie, 'wiek': wiek})
def __iter__(self):
return self._lista.__iter__()

zw = Zwierzatka()

# Dodajemy sobie zwierzatka.
zw.dodaj('Azor', 7)
zw.dodaj('Burek', 11)
zw.dodaj('Koko', 9)
zw.dodaj('Wandzia', 8)

# Wypisujemy nasze zwierzatka.
for z in zw:
print z['imie'], z['wiek']



Czy taka definicja Zwierzątek jest poprawna? Czy powinienem
zaimplementować metodę next()? Pytam, bo Python Library Reference
wyraźnie mówi, że obiekty iterowane muszą implementować metody
__iter__() oraz next(), natomiast moje Zwierzątka nie mają metody
next() i wszystko gra. Czy może mylę tutaj pojęcia "iteratora" i
"obiektu iterowalnego"? Czy chodzi tutaj o to, że self._lista jest
"iteratorem" (i dlatego musi mieć metodę `next()`), a `zw` jest
"obiektem iterowalnym" poprzez posiadanie metody __iter__() (i dlatego
nie musi mieć metody `next()`)? Co, jeśli bym do Zwierzątek dodał
metodę next() - czy i kiedy byłaby ona wywoływana?

Z nieco innej beczki: czy takie iterowanie obiektów jest w ogóle
"eleganckie", czy może lepiej dawać klasom metodę np "wezIterator()" i
wówczas w pętli pisać:
for z in zw.wezIterator():
print z['imie'], z['wiek']
zwłaszcza przy bardziej skomplikowanych klasach? Imho po to stworzono
w pythonie protokół iteracyjny, ale może się mylę.


Wybaczcie banalne pytania, ale nikt chyba nie powiedział, że takowych
nie wolno zadawać na grupach ;-)

Pozdrawiam,
Michał :)
Rob Wolfe
2007-11-11 16:50:49 UTC
Permalink
Post by semisiu
Witam :)
Jestem początkującym pythonowcem i mam pytanie w związku z obiektami
self._lista = []
self._lista.append({'imie': imie, 'wiek': wiek})
return self._lista.__iter__()
Lepiej jest używać bardziej ogólnej funkcji `iter`, która
póki co wywołuje metodę `__iter__` obiektu, ale w przyszłości
nie musi tak być i lepiej korzystać z bardziej uniwersalnych
mechanizmów:

def __iter__(self):
return iter(self._lista)
Post by semisiu
zw = Zwierzatka()
# Dodajemy sobie zwierzatka.
zw.dodaj('Azor', 7)
zw.dodaj('Burek', 11)
zw.dodaj('Koko', 9)
zw.dodaj('Wandzia', 8)
# Wypisujemy nasze zwierzatka.
print z['imie'], z['wiek']
Czy taka definicja Zwierzątek jest poprawna? Czy powinienem
zaimplementować metodę next()? Pytam, bo Python Library Reference
wyraźnie mówi, że obiekty iterowane muszą implementować metody
__iter__() oraz next(), natomiast moje Zwierzątka nie mają metody
Nie, obiekt iterowalny musi implementować *tylko* `__iter__`.
Iterator musi implementować `__iter__` i `next`.
Post by semisiu
next() i wszystko gra. Czy może mylę tutaj pojęcia "iteratora" i
"obiektu iterowalnego"? Czy chodzi tutaj o to, że self._lista jest
"iteratorem" (i dlatego musi mieć metodę `next()`), a `zw` jest
"obiektem iterowalnym" poprzez posiadanie metody __iter__() (i dlatego
nie musi mieć metody `next()`)? Co, jeśli bym do Zwierzątek dodał
metodę next() - czy i kiedy byłaby ona wywoływana?
Metoda `__iter__` mówi tylko o tym, że obiekt jest iterowalny.
Obiekt iterowalny nie musi posiadać metody `next`. Za to zgodnie
z protokołem iteratora metoda `__iter__` *zawsze* musi zwrócić
obiekt, który ma zaimplementowane zarówno metodę `next` jak
i `__iter__`.
Definicja Twojej klasy jest poprawna (z małą uwagą powyżej).
W metodzie `__iter__` zwraca ona obiekt (w tym przypadku
iterator listy), który posiada już zaimplementowane metody
Post by semisiu
lst = [1, 2, 3]
type(lst)
<type 'list'>
Post by semisiu
it = iter(lst)
type(it)
<type 'listiterator'>
Post by semisiu
it.__iter__
<method-wrapper object at 0xb7a81fcc>
Post by semisiu
it.next
<method-wrapper object at 0xb7a81dcc>
Post by semisiu
it.next()
1
Post by semisiu
it.next()
2

Powoduje to, iż niejako delegujesz iterowanie po Twojej
kolekcji `Zwierzatka` do obiektu zwróconego przez metodę `__iter__`,
czyli w tym przypadku iteratora listy. W związku z tym implementowanie
metody `next` w klasie `Zwierzatka` nie ma sensu.
Post by semisiu
Z nieco innej beczki: czy takie iterowanie obiektów jest w ogóle
"eleganckie",
Jak najbardziej jest eleganckie. Załóżmy, że Twoja klasa odczytuje
dane z jakiegoś ogromnego pliku. Wtedy połączenie niejawnego
wykorzystania mechanizmu iteratora przez pętlę `for` z użyciem
generatorów jest potężnym i jednocześnie czytelnym narzędziem:

class Zwierzatka(object):
def __init__(self):
self._f = open('zwierzatka.dat')

def __iter__(self):
for line in self._f:
yield line


Tutaj metoda `__iter__` zwraca obiekt generatora, który posiada
zaimplementowane metody `next` i `__iter__`. Dodatkowo dane
z pliku są odczytywane sukcesywnie bez wrzucania go w całości
do pamięci.
Post by semisiu
czy może lepiej dawać klasom metodę np "wezIterator()" i
print z['imie'], z['wiek']
zwłaszcza przy bardziej skomplikowanych klasach? Imho po to stworzono
w pythonie protokół iteracyjny, ale może się mylę.
Przy bardziej skomplikowanych klasach to jest nieuniknione. Nie mniej
zawsze należy udostępnić jakiś domyślny sposób iterowania po kolekcji
i ten iterator zwracać z metody `__iter__`:

<code>
class Zwierzatka(object):
class Iterator(object):
def __init__(self, seq):
self.seq = seq
self.index = 0
def __iter__(self):
return self
def next(self):
item = None
try:
item = self.seq[self.index]
except IndexError:
raise StopIteration
self.index += 1
return item

class ReverseIterator(object):
def __init__(self, seq):
self.seq = seq
self.index = len(seq) - 1
def __iter__(self):
return self
def next(self):
if self.index < 0:
raise StopIteration
item = self.seq[self.index]
self.index -= 1
return item

def __init__(self):
self._lista = []

def dodaj(self, imie, wiek):
self._lista.append({'imie': imie, 'wiek': wiek})

def __getitem__(self, i):
return self._lista[i]

def __len__(self):
return len(self._lista)

def __iter__(self):
return self.Iterator(self)

def iterator(self):
return self.Iterator(self)

def reverse_iterator(self):
return self.ReverseIterator(self)

zw = Zwierzatka()
zw.dodaj('Azor', 7)
zw.dodaj('Burek', 11)
zw.dodaj('Koko', 9)
zw.dodaj('Wandzia', 8)
for z in zw:
print z['imie'], z['wiek']
print list(zw)
izw = iter(zw)
print type(izw)
print izw.next()
print izw.next()
for z in izw:
print z['imie'], z['wiek']
print "\niterator\n"
izw = zw.iterator()
for z in izw:
print z['imie'], z['wiek']
print "\nreverse_iterator\n"
izw = zw.reverse_iterator()
for z in izw:
print z['imie'], z['wiek']
</code>

Ktoś mógłby się zastanawiać dlaczego metoda `__iter__` występuje zarówno
w obiekcie iterowalnym, jak i w samym iteratorze. Takie założenie
zostało przyjęte w protokole iteratora dla większej wygody korzystania
z tego mechanizmu. Metoda `__iter__` jest wywoływana niejawnie w różnych
kontekstach, np. przez pętlę `for` i zapewnienie gwarancji, iż zawsze
zwróci ona obiekt iteratora (choćby to była referencja do samego siebie)
bardzo upraszcza sprawę. Obiekty wbudowane oczywiście również hołdują
Post by semisiu
it = iter([1, 2, 3])
id(it)
-1213719092
Post by semisiu
it2 = iter(it)
id(it2)
-1213719092
Post by semisiu
it
<listiterator object at 0xb7a81dcc>
Post by semisiu
it2
<listiterator object at 0xb7a81dcc>

RW
Grzegorz Makarewicz
2007-11-11 17:44:12 UTC
Permalink
Post by Rob Wolfe
Ktoś mógłby się zastanawiać dlaczego metoda `__iter__` występuje zarówno
w obiekcie iterowalnym, jak i w samym iteratorze. Takie założenie
zostało przyjęte w protokole iteratora dla większej wygody korzystania
z tego mechanizmu. Metoda `__iter__` jest wywoływana niejawnie w różnych
kontekstach, np. przez pętlę `for` i zapewnienie gwarancji, iż zawsze
zwróci ona obiekt iteratora (choćby to była referencja do samego siebie)
bardzo upraszcza sprawę.
python 2.5 rules
na pspc (1.5.6 fork)- ma to w ...

mak
Grzegorz Makarewicz
2007-11-11 17:39:39 UTC
Permalink
Post by semisiu
Witam :)
Jestem początkującym pythonowcem i mam pytanie w związku z obiektami
stara klasa :)
Post by semisiu
Czy taka definicja Zwierzątek jest poprawna?
jak najbardziej
Post by semisiu
Czy powinienem zaimplementować metodę next()? Pytam, bo Python Library Reference
wyraźnie mówi, że obiekty iterowane muszą implementować metody
__iter__() oraz next(), natomiast moje Zwierzątka nie mają metody
next() i wszystko gra.
nie musisz, next ma sie nijak do definicji klasy, next jest wymagany dla
iteratora, a nie klasy.
for i in zwierzadka:
...
for i in range len(zwieradka)
...
Post by semisiu
czy może lepiej dawać klasom metodę np "wezIterator()" i
print z['imie'], z['wiek']
przy konstrukcji 'for z in zw.wezIterator()' wynik musi byc enumeratorem

fuj :), jezeli cos piszesz to masz jakis zamiar, jezeli z czegos
korzystasz, to musisz sie temu zamiarowi podporzadkowac

z['imie'] - to bardzo nieszczesliwe
z.imie :) - to jest poprawne

mak
semisiu
2007-11-15 12:34:44 UTC
Permalink
Dziękuję wszystkim za wyczerpujące odpowiedzi. Myślę, że nadawałyby
się na sensowny artykuł o tych zagadnieniach. Wyjaśniło się też kilka
wątpliwości, o które bałem się zapytać. William, dzięki za
rozjaśnienie umysłu - pojąłem w mig :)
Post by Rob Wolfe
self._f = open('zwierzatka.dat')
yield line
Yield jest jednym z wielu plusów pythona, a gdy zobaczyłem powyższe
zastosowanie, dostałem dreszczy ;)
Post by Rob Wolfe
z['imie'] - to bardzo nieszczesliwe
z.imie :) - to jest poprawne
Też tak to sobie wyobrażałem, ale napisałem w takiej a nie innej
formie dla uproszczenia rozumowania. Jednak skoro już poruszyłeś tę
kwestię, zadam problem, jaki ostatnio nasunął mi się w oderwaniu od
tego topiku.

Mianowicie czy lepiej zdefiniować sobie klasę z polami z góry
określonymi:

class Zwierzak(object):
imie = ''
wiek = 0

i analogicznie dla innych stworów? Czy może taki zapis:

class OrganizmZywy(object):
def __setattr__(self, name, value):
self.__dict__[name] = value
def __getattr__(self, name):
self.__dict__[name] # Dla uproszczenia nie sprawdzam, czy klucz
istnieje.
def __iter__(self):
# ...
pass
def next(self):
# ...
pass
# i tak dalej...

będzie oznaczać znaczny spadek wydajności przy częstym tworzeniu
obiektów klasy OrganizmZywy i iterowaniu po liście takich obiektów?

Inaczej mówiąc, czy przesłanianie dostępu do atrybutów poprzez metody
__setattr__() i __getattr__() jest dużo (jak dużo?) mniej wydajne, niż
gdybyśmy tych metod nie użyli? Czy należy iść z modą stosowania
typowych akcesorów postaci setWiek(), getWiek(), czy lepiej
wykorzystać pełen potencjał pythona w tym względzie i używać
__setattr__() i __getattr__()?

Pozdrawiam, Michał :)
Adam Byrtek
2007-11-22 00:27:36 UTC
Permalink
Post by semisiu
Inaczej mówiąc, czy przesłanianie dostępu do atrybutów poprzez metody
__setattr__() i __getattr__() jest dużo (jak dużo?) mniej wydajne, niż
gdybyśmy tych metod nie użyli? Czy należy iść z modą stosowania
typowych akcesorów postaci setWiek(), getWiek(), czy lepiej
wykorzystać pełen potencjał pythona w tym względzie i używać
__setattr__() i __getattr__()?
Zostawmy programistom Javy wątpliwą przyjemność tworzenia szablonowych
getterów i setterów dla każdej własności, "tak na wszelki wypadek". W
Pythonie gdy zachodzi realna potrzeba owinięcia własności metodą, można
użyć properties, jest to bardziej przejrzyste niż proponowane przez
Ciebie rozwiązanie z __(get|set)attr__().

Popieram pogląd, że dla użytkownika klasy gettery i settery powinny być
przezroczyste, zgodnie z bardzo dobrą zasadą:
http://en.wikipedia.org/wiki/Uniform_access_principle

Pozdrawiam,
Adam
Seweryn Habdank-Wojewódzki
2007-11-22 00:38:33 UTC
Permalink
Witam
Post by Adam Byrtek
Pythonie gdy zachodzi realna potrzeba owinięcia własności metodą, można
użyć properties, jest to bardziej przejrzyste niż proponowane przez
Ciebie rozwiązanie z __(get|set)attr__().
IMHO z małym wyjątkiem, kiedy planujesz *obserwować* jakąś własność, to
setter jest jedynym słusznym rozwiązaniem.

Pozdrawiam.
--
|\/\/|   Seweryn Habdank-Wojewódzki
\/\/
Grzegorz Makarewicz
2007-11-22 03:18:36 UTC
Permalink
Post by Seweryn Habdank-Wojewódzki
Witam
Post by Adam Byrtek
Pythonie gdy zachodzi realna potrzeba owinięcia własności metodą, można
użyć properties, jest to bardziej przejrzyste niż proponowane przez
Ciebie rozwiązanie z __(get|set)attr__().
IMHO z małym wyjątkiem, kiedy planujesz *obserwować* jakąś własność, to
setter jest jedynym słusznym rozwiązaniem.
Pozdrawiam.
pyhon rules :)

po prostu masz wtedy zle napisany program pokrycie __set(attr|item)__
jest dopuszczelne, ale to nie moze staniwc regoly zachowania - jezeli na
takich wyjatkach chcesz opierac program - to pisz e C/C++ nie w pythonie

z zadnym wyjatkiem :) - albo piszesz poprawnie, alebo pisz se kaszane w
C/C++

mak
Seweryn Habdank-Wojewódzki
2007-11-22 07:58:57 UTC
Permalink
Witam
Post by Grzegorz Makarewicz
Post by Seweryn Habdank-Wojewódzki
Post by Adam Byrtek
Pythonie gdy zachodzi realna potrzeba owinięcia własności metodą, można
użyć properties, jest to bardziej przejrzyste niż proponowane przez
Ciebie rozwiązanie z __(get|set)attr__().
IMHO z małym wyjątkiem, kiedy planujesz *obserwować* jakąś własność, to
setter jest jedynym słusznym rozwiązaniem.
po prostu masz wtedy zle napisany program pokrycie __set(attr|item)__
jest dopuszczelne, ale to nie moze staniwc regoly zachowania
A obserwowalność to częsta potrzeba, czy rzadkie widzimisię?

Pozdrawiam.
--
|\/\/|   Seweryn Habdank-Wojewódzki
\/\/
Adam Byrtek
2007-11-22 20:20:28 UTC
Permalink
Post by Seweryn Habdank-Wojewódzki
Post by Adam Byrtek
Pythonie gdy zachodzi realna potrzeba owinięcia własności metodą, można
użyć properties, jest to bardziej przejrzyste niż proponowane przez
Ciebie rozwiązanie z __(get|set)attr__().
IMHO z małym wyjątkiem, kiedy planujesz *obserwować* jakąś własność, to
setter jest jedynym słusznym rozwiązaniem.
Nie bardzo rozumiem, przecież property() to tylko bardziej elegancka
metoda definiowania przezroczystych dla klienta akcesorów, więc dlaczego
nie dałoby się zaimplementować w ten sposób obserwatora?

Pozdrawiam,
Adam
Adam Byrtek
2007-11-22 21:01:51 UTC
Permalink
Post by Adam Byrtek
Post by Seweryn Habdank-Wojewódzki
IMHO z małym wyjątkiem, kiedy planujesz *obserwować* jakąś własność, to
setter jest jedynym słusznym rozwiązaniem.
Nie bardzo rozumiem, przecież property() to tylko bardziej elegancka
metoda definiowania przezroczystych dla klienta akcesorów, więc dlaczego
nie dałoby się zaimplementować w ten sposób obserwatora?
Dla przypomnienia:

class C(object):
def __init__(self): self.__x = None
def getx(self): return self._x
def setx(self, value): self._x = value
def delx(self): del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")

Za http://docs.python.org/lib/built-in-funcs.html

Pozdrawiam,
Adam
Seweryn Habdank-Wojewódzki
2007-11-25 21:22:22 UTC
Permalink
Witam
Post by Adam Byrtek
Post by Adam Byrtek
Post by Seweryn Habdank-Wojewódzki
IMHO z małym wyjątkiem, kiedy planujesz *obserwować* jakąś własność, to
setter jest jedynym słusznym rozwiązaniem.
Nie bardzo rozumiem, przecież property() to tylko bardziej elegancka
metoda definiowania przezroczystych dla klienta akcesorów, więc dlaczego
nie dałoby się zaimplementować w ten sposób obserwatora?
Nie zrozumiałeś mnie, a Ja Ciebie :-). To co jest napisane poniżej jest to o
czym myślałem. Podobnie można użyć __[gs]etattr__. Raczej chodziło mi o
podkreślenie ważności settera i czasem gettera. Tyle :-).
Post by Adam Byrtek
def __init__(self): self.__x = None
def getx(self): return self._x
def setx(self, value): self._x = value
def delx(self): del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")
Pozdrawiam.
--
|\/\/|   Seweryn Habdank-Wojewódzki
\/\/
Adam Byrtek
2007-11-26 14:59:48 UTC
Permalink
Post by Seweryn Habdank-Wojewódzki
Nie zrozumiałeś mnie, a Ja Ciebie :-). To co jest napisane poniżej jest to o
czym myślałem. Podobnie można użyć __[gs]etattr__. Raczej chodziło mi o
podkreślenie ważności settera i czasem gettera. Tyle :-).
Oczywiście, oba bywają przydatne, i bardzo dobrze, że można je
wprowadzić bez zmiany interfejsu klasy. Łatwo o nieporozumienie, bo
słowo "property" można rozumieć zarówno dosłownie jako własność klasy,
jak i pythonowy sposób definiowania akcesorów.

Pozdrawiam,
Adam
semisiu
2007-12-01 20:35:14 UTC
Permalink
Post by Adam Byrtek
Zostawmy programistom Javy wątpliwą przyjemność tworzenia szablonowych
getterów i setterów dla każdej własności, "tak na wszelki wypadek". W
Pythonie gdy zachodzi realna potrzeba owinięcia własności metodą, można
użyć properties, jest to bardziej przejrzyste niż proponowane przez
Ciebie rozwiązanie z __(get|set)attr__().
Popieram pogląd, że dla użytkownika klasy gettery i settery powinny być
przezroczyste, zgodnie z bardzo dobrą zasadą:http://en.wikipedia.org/wiki/Uniform_access_principle
To jest piękne! :) Dziękuję wszystkim za wyczerpujące i cenne uwagi :)

Postanowiłem zatem dodać do mojego zwierzyńca klasę OrganizmZywy.
Każdy obiekt tej klasy ma imię, wiek i rok urodzenia. Jeśli ustawię
wiek, wówczas rok urodzenia ma zmienić się sam i na odwrót.

Jeśli w property() nie określimy gettera, nie możemy pobrać pola
(python zrzuci AttributeError: unreadable attribute). Ale moje gettery
dla wieku i dla roku urodzenia mają po prostu zwracać wartość pola.
Jak zatem zwrócić wartość unikając bezsensownego pisania getterów?
Python Library Reference sugeruje, żeby utworzyć metodę getx() (choć
podejrzewam, że napisali tak tylko dla czytelności). Mnie przyśniło
się coś takiego:

<code>
import time

class OrganizmZywy(object):
def __str__(self):
return str((self.imie, self.wiek, self.rok))

def set_rok(self, rok):
self.__rok = rok
self.__wiek = time.localtime()[0] - rok

rok = property(lambda self: self.__rok, set_rok)

def set_wiek(self, wiek):
self.__wiek = wiek
self.__rok = time.localtime()[0] - wiek

wiek = property(lambda self: self.__wiek, set_wiek)
</code>

Chodzi o lambdę.

a) Czy istnieje *jeszcze prostszy* zapis?
b) Czy lepiej jednak pozostać przy opakowywaniu każdego takiego pola
metodami typu get_wiek()?
c) Co, jeśli właściwość "wiek" jest używana (odczytywana,
modyfikowana, obliczana) w wielu miejscach w klasie OrganizmZywy (i/
lub poza nią) - czy muszę zmieniać jej nazwę na __wiek, czy nie muszę
z nią nic robić?


Pozdrawiam,
Michał :)
William
2007-12-03 10:10:08 UTC
Permalink
Ja bymn ten przykład obrócił do góry nogami... "rok" może być zwykłym prostym atrybutem liczbowym. Natomiast "wiek" setterem / getterem, który w tle modyfikuje atrybut "rok" lub w locie wylicza wiek i go zwraca. W tej chwili masz reduldantną informację i nieodporność na zmianę roku w czasie życia obiektu. Druga sprawa to atrybuty chronione klasy (podwójne podkreslenie). Prawie na pewno wystarczą tu prywatne (pojedyńcze podkreślenie). Nazwa takiej metod
Rob Wolfe
2007-12-03 21:46:29 UTC
Permalink
Post by semisiu
Postanowiłem zatem dodać do mojego zwierzyńca klasę OrganizmZywy.
Każdy obiekt tej klasy ma imię, wiek i rok urodzenia. Jeśli ustawię
wiek, wówczas rok urodzenia ma zmienić się sam i na odwrót.
Jeśli w property() nie określimy gettera, nie możemy pobrać pola
(python zrzuci AttributeError: unreadable attribute). Ale moje gettery
dla wieku i dla roku urodzenia mają po prostu zwracać wartość pola.
Jak zatem zwrócić wartość unikając bezsensownego pisania getterów?
Python Library Reference sugeruje, żeby utworzyć metodę getx() (choć
podejrzewam, że napisali tak tylko dla czytelności). Mnie przyśniło
<code>
import time
return str((self.imie, self.wiek, self.rok))
Nie wiem co przez to chciałeś osiągnąć?
Metoda `__str__` ma zwracać coś czytelnego dla człowieka, np.:

def __str__(self):
return 'wiek=%d, rok=%d' % (self.__wiek, self.__rok)
Post by semisiu
self.__rok = rok
self.__wiek = time.localtime()[0] - rok
Jak dla mnie podwójne podkreślenie to przesada, choć pewnie
obiektowi puryści się oburzą. ;)
Jedno jest wystarczające i oznacza atrybut niepubliczny.
Post by semisiu
rok = property(lambda self: self.__rok, set_rok)
self.__wiek = wiek
self.__rok = time.localtime()[0] - wiek
wiek = property(lambda self: self.__wiek, set_wiek)
</code>
Chodzi o lambdę.
a) Czy istnieje *jeszcze prostszy* zapis?
Hmmm... czy prostszy, to zależy od punktu widzenia. Patrz niżej.
Post by semisiu
b) Czy lepiej jednak pozostać przy opakowywaniu każdego takiego pola
metodami typu get_wiek()?
Gdy musisz wykonać kilka operacji przed odczytem czy zapisem,
to nie ma innego wyjścia. Lambda wystarcza tylko dla najprostszych
przypadków i wtedy jest OK.
Post by semisiu
c) Co, jeśli właściwość "wiek" jest używana (odczytywana,
modyfikowana, obliczana) w wielu miejscach w klasie OrganizmZywy (i/
lub poza nią) - czy muszę zmieniać jej nazwę na __wiek, czy nie muszę
z nią nic robić?
Wewnątrz klasy zawsze odwołuj się bezpośrednio do atrybutu, czyli
`__wiek`, a na zewnątrz oczywiście do `wiek`.

Wracając do prostszego zapisu. Dla mnie czytelne jest np. to:

<code>
class OrganizmZywy(object):
def __init__(self):
self._wiek = 0
self._rok = 0

def __str__(self):
return 'wiek=%d, rok=%d' % (self._wiek, self._rok)

__repr__ = __str__

@apply
def rok():
def fget(self):
return self._rok
def fset(self, rok):
self._rok = rok
self._wiek = time.localtime()[0] - rok
return property(**locals())

@apply
def wiek():
def fget(self):
return self._wiek
def fset(self, wiek):
self._wiek = wiek
self._rok = time.localtime()[0] - wiek
return property(**locals())
</code>

Choć pewnie nie wszyscy podzielają mój zachwyt. ;)
Jeśli nie w pełni rozumiesz jak to działa, to jednak radzę
pozostać przy tradycyjnym zapisie.

RW
Adam Byrtek
2007-12-04 18:14:35 UTC
Permalink
Post by Rob Wolfe
return 'wiek=%d, rok=%d' % (self._wiek, self._rok)
__repr__ = __str__
Nawiasem mówiąc miło jest, gdy __repr__ zwraca wartość, którą można
zdeserializować do podobnego obiektu:

"__repr__(self) [...] If at all possible, this should look like a valid
Python expression that could be used to recreate an object with the same
value (given an appropriate environment)."

Pozdrawiam,
Adam
Rob Wolfe
2007-12-04 19:43:36 UTC
Permalink
Post by Adam Byrtek
Post by Rob Wolfe
return 'wiek=%d, rok=%d' % (self._wiek, self._rok)
__repr__ = __str__
Nawiasem mówiąc miło jest, gdy __repr__ zwraca wartość, którą można
"__repr__(self) [...] If at all possible, this should look like a
valid Python expression that could be used to recreate an object with
the same value (given an appropriate environment)."
Słusznie. Jak się coś pisze dla szerokich mas, to jak najbardziej,
ale ja przyznaję się bez bicia, że już od dawna nie trzymam się tej zasady.
Może dlatego, że nigdy nie używam `eval`, a może dlatego, że jakoś
mi się to do niczego nie przydaje, ...nie pamiętam. ;)

RW
William
2007-12-05 07:46:36 UTC
Permalink
Post by Rob Wolfe
Post by Adam Byrtek
Nawiasem mówiąc miło jest, gdy __repr__ zwraca wartość, którą można
Słusznie. Jak się coś pisze dla szerokich mas, to jak najbardziej,
ale ja przyznaję się bez bicia, że już od dawna nie trzymam się tej zasady.
Może dlatego, że nigdy nie używam `eval`, a może dlatego, że jakoś
mi się to do niczego nie przydaje, ...nie pamiętam. ;)
O ile mi się dobrze wydaje, pythoniczne zalecenie zwracania przez __repr__ literału "konstruktora" wynika nie z potrzeb bibliotek serializacji, tylko konwencji debugowania / autodokumentacji kodu. Innymi słowy `x` ma pokazać developerowi jak owo x zostało utworzone. W rzeczywistym, działającym programie repr nie powinno zostać z kolei
Loading...