Zanurkuj w Pythonie/Potęga introspekcji - wszystko razem
Wszystko razem
edytujOstatnia linia kodu, jedyna której jeszcze nie rozpracowaliśmy, to ta, która odwala całą robotę. Teraz umieszczamy wszystkie puzzle w jednym miejscu i nadchodzi czas, aby je ułożyć.
To jest najważniejsza część apihelper.py:
print "\n".join(["%s %s" %
(method.ljust(spacing),
processFunc(unicode(getattr(object, method).__doc__)))
for method in methodList])
Zauważmy, że jest to tylko jedno wyrażenie podzielone na wiele linii, ale które nie używa znaku kontynuacji (znaku odwrotnego ukośnika, \). Pamiętasz jak powiedzieliśmy, że pewne instrukcje mogą być rozdzielone na kilka linii bez używania odwrotnego ukośnika? Wyrażenia listowe są jednym z tego typu wyrażeń, ponieważ całe wyrażenie jest zawarte w nawiasach kwadratowych.
Zacznijmy od końca i posuwajmy się w tył. Wyrażenie
for method in methodList
okazuje się być wyrażeniem listowym. Jak wiemy, methodList
jest listą wszystkich metod danego obiektu, które nas interesują, więc za pomocą tej pętli przechodzimy tę listę wykorzystując zmienną method
.
>>> import odbchelper >>> object = odbchelper #(1) >>> method = 'buildConnectionString' #(2) >>> getattr(object, method) #(3) <function buildConnectionString at 010D6D74> >>> print getattr(object, method).__doc__ #(4) Tworzy łańcuchów znaków na podstawie słownika parametrów. Zwraca łańcuch znaków.
- W funkcji
info
,object
jest obiektem do którego otrzymujemy pomoc, a ten obiekt zostaje przekazany jako argument. - Podczas iterowania listy
methodList
,method
jest nazwą aktualnej metody. - Używając funkcji
getattr
otrzymujemy referencję do funkcjimethod
z modułuobject
. - Teraz wypisanie notki dokumentacyjnej będzie bardzo proste.
Następnym elementem puzzli jest użycie unicode
na notce dokumentacyjnej. Jak sobie przypominamy, unicode
jest wbudowaną funkcją, która przekształca dane na unikod, ale notka dokumentacyjna jest zawsze łańcuchem znaków, więc po co ją jeszcze konwertować na unikod? Nie każda funkcja posiada notkę dokumentacyjną, a jeśli ona nie istnieje, atrybut __doc__
ma wartość None
.
unicode
na notkach dokumentacyjnych?
>>> def foo(): print 2 >>> foo() 2 >>> foo.__doc__ #(1) >>> foo.__doc__ == None #(2) True >>> unicode(foo.__doc__) #(3) u'None'
- Możemy łatwo zdefiniować funkcję, która nie posiada notki dokumentacyjnej, tak więc jej atrybut
__doc__
ma wartośćNone
. Dezorientujące jest to, że jeśli bezpośrednio odwołamy się do atrybutu__doc__
, IDE w ogóle nic nie wypisze. Jednak jeśli się trochę zastanowimy nad tym, takie zachowanie IDE ma jednak pewien sens[1] - Możemy sprawdzić, że wartość atrybutu
__doc__
aktualnie wynosiNone
przez porównanie jej bezpośrednio z tą wartością. - W tym przypadku funkcja
unicode
przyjmuje pustą wartość,None
i zwraca jej unikodową reprezentację, czyli'None'
. li.append.__doc__
jest łańcuchem znaków. Zauważmy, że wszystkie angielskie notki dokumentacyjne Pythona korzystają ze znaków ASCII, dlatego możemy spokojnie je przekonwertować do unikodu za pomocą funkcjiunicode
.
W SQL musimy skorzystać z IS NULL zamiast z = NULL , aby porównać coś z pustą wartością. W Pythonie możemy użyć albo == None albo is None , lecz is None jest szybsze.
|
Teraz kiedy już mamy pewność, że otrzymamy unikod, możemy przekazać otrzymany unikodowy napis do processFunc
, którą już zdefiniowaliśmy jako funkcję zwijającą lub niezwijającą białe znaki (w zależności od przekazanego argumentu). Czy już wiemy, dlaczego wykorzystaliśmy unicode
? Do przekonwertowania wartości None
na reprezentację w postaci unikodowego łańcucha znaków. processFunc
przyjmuje argument będący unikodem i wywołuje jego metodę split
. Nie zadziałałoby to, gdybyśmy przekazali samo None
, ponieważ None
nie posiada metody o nazwie split
i rzucony zostałby wyjątek. Może się zastanawiasz, dlaczego nie konwertujemy do str
? Ponieważ tworzone przez nas notki są napisami unikodowymi, w których nie wszystkie znaki należą do ASCII, a zatem str
rzuciłby wyjątek.
Idąc wstecz, widzimy, że ponownie używamy formatowania łańcucha znaków, aby połączyć wartości zwrócone przez processFunc
i przez metodę ljust
. Jest to metoda łańcucha znaków (dodajmy, że napis unikodowy także jest łańcuchem znaków, tylko nieco o większych możliwościach), której jeszcze nie poznaliśmy.
ljust
>>> s = 'buildConnectionString' >>> s.ljust(30) #(1) 'buildConnectionString ' >>> s.ljust(20) #(2) 'buildConnectionString'
ljust
wypełnia napis spacjami do zadanej długości. Z tej możliwości korzysta funkcjainfo
, aby stworzyć dwie kolumny na wyjściu i aby wszystkie notki dokumentacyjne umieścić w drugiej kolumnie.- Jeśli podana długość jest mniejsza niż długość napisu,
ljust
zwróci po prostu napis niezmieniony. Metoda ta nigdy nie obcina łańcucha znaków.
Już prawie skończyliśmy. Mając nazwę metody method
uzupełnioną spacjami poprzez ljust
i (prawdopodobnie zwiniętą) notkę dokumentacyjną otrzymaną z wywołania processFunc
, łączymy je i otrzymujemy pojedynczy napis, łańcuch znaków. Ponieważ odwzorowujemy listę methodList
, dostajemy listę złożoną z takich łańcuchów znaków. Używając metody join
z napisu "\n"
, łączymy tę listę w jeden łańcuch znaków, gdzie każdy elementem listy znajduje się w oddzielnej linii i ostatecznie wypisujemy rezultat.
>>> li = ['a', 'b', 'c']
>>> print "\n".join(li) #(1)
a
b
c
- Ta sztuczka może być pomocna do znajdowania błędów, gdy pracujemy na listach, a w Pythonie zawsze pracujemy na listach.
I to już był ostatni element puzzli. Teraz powinieneś zrozumieć ten kod.
print "\n".join(["%s %s" %
(method.ljust(spacing),
processFunc(unicode(getattr(object, method).__doc__)))
for method in methodList])
- ↑ Pamiętamy, że każda funkcja zwraca pewną wartość? Jeśli funkcja nie wykorzystuje instrukcji
return
, zostaje zwróconeNone
, a częste wyświetlanieNone
po wykonaniu pewnych funkcji przez IDE Pythona mogłoby być trochę uciążliwe.