Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00396 011397 10766726 na godz. na dobę w sumie
Profesjonalne programowanie w Pythonie. Poziom ekspert. Wydanie II - ebook/pdf
Profesjonalne programowanie w Pythonie. Poziom ekspert. Wydanie II - ebook/pdf
Autor: , Liczba stron: 496
Wydawca: Helion Język publikacji: polski
ISBN: 978-83-283-3034-4 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> programowanie >> python - programowanie
Porównaj ceny (książka, ebook (-20%), audiobook).

Twórcy Pythona niemal od początku starali się opracować wieloparadygmatowy język zorientowany na czytelność kodu i produktywność programisty. Dziś język ten jest uważany za wszechstronny i potężny, a do tego cechuje się prostotą i elastycznością. Nadaje się zarówno do pisania niedużych skryptów, jak i wielkich systemów, a także do wysoce specjalistycznych zadań, jak choćby analiza danych w celach naukowych. Mimo to pisanie kodu, który jest wydajny, prosty w utrzymaniu oraz łatwy w użyciu, wciąż sprawia problemy nawet zaawansowanym programistom Pythona.

Niniejsza książka jest zbiorem praktyk stosowanych przez najlepszych programistów pracujących z Pythonem. Jest przeznaczona dla osób zawodowo zajmujących się rozwojem oprogramowania oraz dla ambitnych pasjonatów w tej dziedzinie. Poza opisem zaawansowanych technik programowania w Pythonie znalazły się tu również informacje o narzędziach i technikach stosowanych obecnie przez profesjonalnych programistów. Opisano metody zarządzania kodem, tworzenia, dokumentowania i testowania kodu oraz zasady optymalizacji oprogramowania. Przedstawiono również wzorce projektowe, które szczególnie docenią programiści Pythona.

Najważniejsze zagadnienia przedstawione w książce:

Python — niezawodne narzędzie dla profesjonalisty!


Michał Jaworski — od blisko 10 lat programuje w Pythonie. Napisał Graceful - framework REST oparty na bibliotece Falcon. Obecnie jest architektem oprogramowania w firmie Opera Software. Poza tym projektuje wysoce wydajne rozproszone usługi sieciowe i angażuje się w liczne projekty open source.

Tarek Ziadé — jest kierownikiem ds. technicznych w firmie Mozilla. Zajmuje się usługami sieciowymi o wielkiej skali w Pythonie na potrzeby przeglądarki Firefox. Jest także założycielem Afpy, Francuskiej Grupy Użytkowników Pythona. Wielokrotnie był prelegentem podczas konferencji Solutions Linux, PyCon, OSCON, EuroPython i innych.

Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

Tytuł oryginału: Expert Python Programming, Second Edition Tłumaczenie: Michał Jaworski ISBN: 978-83-283-3033-7 Copyright © 2016 Packt Publishing First published in the English language under the title ‘Expert Python Programming - Second Edition’ – 9781785886850. Polish edition copyright © 2017 by Helion SA All rights reserved. All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from the Publisher. Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną, fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje naruszenie praw autorskich niniejszej publikacji. Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich właścicieli. Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce informacje były kompletne i rzetelne. Nie biorą jednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION nie ponoszą również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji zawartych w książce. Wydawnictwo HELION ul. Kościuszki 1c, 44-100 GLIWICE tel. 32 231 22 19, 32 230 98 63 e-mail: helion@helion.pl WWW: http://helion.pl (księgarnia internetowa, katalog książek) Pliki z przykładami omawianymi w książce można znaleźć pod adresem: ftp://ftp.helion.pl/przyklady/prprpe.zip Drogi Czytelniku! Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres http://helion.pl/user/opinie/prprpe Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję. Printed in Poland. • Kup książkę • Poleć książkę • Oceń książkę • Księgarnia internetowa • Lubię to! » Nasza społeczność Spis tre(cid:258)ci O autorach O recenzencie Przedmowa Rozdzia(cid:239) 1. Obecny status Pythona Gdzie jeste(cid:258)my i dok(cid:200)d zmierzamy? Dlaczego i jak zmienia si(cid:218) Python B(cid:200)d(cid:283) na bie(cid:285)(cid:200)co ze zmianami j(cid:218)zyka — dokumenty PEP Popularno(cid:258)(cid:202) Pythona 3 w chwili pisania tej ksi(cid:200)(cid:285)ki G(cid:239)ówne ró(cid:285)nice pomi(cid:218)dzy Pythonem 3 a Pythonem 2 Korzy(cid:258)ci p(cid:239)yn(cid:200)ce ze znajomo(cid:258)ci starej wersji Pythona G(cid:239)ówne ró(cid:285)nice sk(cid:239)adni i cz(cid:218)ste pu(cid:239)apki Popularne narz(cid:218)dzia i techniki u(cid:285)ywane w celu utrzymania kompatybilno(cid:258)ci Nie tylko CPython Dlaczego powiniene(cid:258) si(cid:218) przejmowa(cid:202)? Stackless Python Jython IronPython PyPy Nowoczesne podej(cid:258)cia do programowania w Pythonie Izolacja (cid:258)rodowisk Pythona na poziomie aplikacji Zalety stosowania izolacji Popularne rozwi(cid:200)zania Które rozwi(cid:200)zanie wybra(cid:202)? 11 12 13 19 20 20 21 22 23 23 24 26 30 30 31 31 32 33 34 34 36 37 41 Poleć książkęKup książkę Spis tre(cid:286)ci Izolacja (cid:258)rodowisk Pythona na poziomie systemu operacyjnego Wirtualne (cid:258)rodowiska robocze z wykorzystaniem narz(cid:218)dzia Vagrant Konteneryzacja czy wirtualizacja? Popularne narz(cid:218)dzia pracy ukierunkowane na produktywno(cid:258)(cid:202) Alternatywne pow(cid:239)oki Pythona — IPython, bpython, ptpython Interaktywne debuggery Przydatne materia(cid:239)y Podsumowanie Rozdzia(cid:239) 2. Najlepsze praktyki sk(cid:239)adniowe — poni(cid:285)ej poziomu klas Typy wbudowane Pythona Ci(cid:200)gi znaków i bajtów Kolekcje Zaawansowane elementy sk(cid:239)adni Iteratory Instrukcja yield Dekoratory Zarz(cid:200)dcy kontekstu — instrukcja with Inne elementy sk(cid:239)adni, o których mo(cid:285)esz jeszcze nie wiedzie(cid:202) Konstrukcja for ... else ... Adnotacje funkcji Podsumowanie Rozdzia(cid:239) 3. Najlepsze praktyki sk(cid:239)adniowe — powy(cid:285)ej poziomu klas Dziedziczenie po typach wbudowanych Uzyskiwanie dost(cid:218)pu do metod klas nadrz(cid:218)dnych Klasy w starym stylu oraz funkcja super() w Pythonie 2 Porz(cid:200)dek rozpatrywania metod w Pythonie Pu(cid:239)apki zwi(cid:200)zane z funkcj(cid:200) super() Najlepsze praktyki Zaawansowane wzorce dost(cid:218)pu do atrybutów Deskryptory W(cid:239)a(cid:258)ciwo(cid:258)ci Sloty Metaprogramowanie Dekoratory jako metoda metaprogramowania Dekoratory klas Wykorzystanie metody __new__() w celu nadpisania procesu tworzenia instancji klas Metaklasy Rady dotycz(cid:200)ce automatycznego generowania kodu Podsumowanie 4 42 43 45 45 46 48 49 50 51 52 52 56 67 67 69 72 83 87 87 88 89 91 92 94 96 97 101 104 104 105 111 114 114 115 116 118 120 127 134 Poleć książkęKup książkę Rozdzia(cid:239) 4. W(cid:239)a(cid:258)ciwy dobór nazw PEP 8 i najlepsze praktyki nazewnicze Kiedy i dlaczego przestrzega(cid:202) zasad PEP 8? Poza PEP 8 — wytyczne stylu w zespo(cid:239)ach Notacje nazewnicze Zmienne Zmienne publiczne i prywatne Funkcje i metody W(cid:239)a(cid:258)ciwo(cid:258)ci Klasy Modu(cid:239)y i pakiety Dobre praktyki nazewnicze U(cid:285)ycie prefiksów is oraz has przy elementach logicznych U(cid:285)ycie liczby mnogiej przy zmiennych przechowuj(cid:200)cych kolekcje Precyzyjne opisywanie s(cid:239)owników Unikanie zbyt ogólnych okre(cid:258)le(cid:241) Unikanie istniej(cid:200)cych nazw Najlepsze praktyki dla argumentów funkcji i metod Projektowanie argumentów metod(cid:200) przyrostow(cid:200) Ufaj argumentom i testom Ostro(cid:285)ne wykorzystanie magicznych argumentów *args oraz **kwargs Nazwy klas Nazwy modu(cid:239)ów i pakietów Przydatne narz(cid:218)dzia Pylint pep8 i flake8 Podsumowanie Rozdzia(cid:239) 5. Tworzenie i dystrybucja pakietów Tworzenie pakietów Zamieszanie wokó(cid:239) narz(cid:218)dzi do tworzenia i dystrybuowania pakietów Konfiguracja projektu W(cid:239)asne polecenia skryptu setup.py Praca z pakietami podczas ich rozwoju Pakiety przestrzeni nazw Zastosowanie pakietów przestrzeni nazw PEP 420 — domy(cid:258)lne pakiety przestrzeni nazw Pakiety przestrzeni nazw w starszych wersjach Pythona Praca z repozytorium pakietów Python Package Index — repozytorium pakietów Pythona Dystrybucje (cid:283)ród(cid:239)owe a dystrybucje budowane Samodzielne pliki wykonywalne Kiedy samodzielne pliki wykonywalne s(cid:200) u(cid:285)yteczne? Popularne narz(cid:218)dzia Bezpiecze(cid:241)stwo kodu Pythona w samodzielnych plikach wykonywalnych Podsumowanie Spis tre(cid:286)ci 135 135 136 136 137 138 140 142 145 145 146 146 146 146 147 147 148 149 150 150 152 154 154 155 155 157 158 159 160 160 162 172 172 174 174 176 177 178 179 181 184 186 186 193 195 5 Poleć książkęKup książkę Spis tre(cid:286)ci Rozdzia(cid:239) 6. Zdalne wdro(cid:285)enia kodu Manifest Twelve-Factor App Automatyzacja wdro(cid:285)e(cid:241) z wykorzystaniem narz(cid:218)dzia Fabric W(cid:239)asne repozytorium pakietów lub kopie lustrzane PyPI Utrzymywanie kopii lustrzanych PyPI Wdro(cid:285)enia z wykorzystaniem dystrybucji pakietów Popularne konwencje i dobre praktyki Hierarchia systemu plików Izolacja Wykorzystanie narz(cid:218)dzi nadzoru nad procesami Kod aplikacji powinien by(cid:202) uruchomiony w przestrzeni u(cid:285)ytkownika Korzystanie ze wstecznych serwerów proxy protoko(cid:239)u HTTP Prze(cid:239)adowywanie procesów bez zastojów Instrumentacja i monitorowanie kodu Logowanie b(cid:239)(cid:218)dów (Sentry oraz raven) Monitorowanie metryk systemowych i aplikacji Obs(cid:239)uga logów Podsumowanie Rozdzia(cid:239) 7. Rozszerzenia Pythona w innych j(cid:218)zykach programowania Inne j(cid:218)zyki, czyli C lub C++ Jak dzia(cid:239)aj(cid:200) rozszerzenia w C i C++ Dlaczego warto tworzy(cid:202) rozszerzenia Zwi(cid:218)kszanie wydajno(cid:258)ci w krytycznych sekcjach kodu Integracja kodu napisanego w innych j(cid:218)zykach programowania Integracja zewn(cid:218)trznych bibliotek dynamicznych Tworzenie w(cid:239)asnych wbudowanych typów danych Pisanie rozszerze(cid:241) Zwyczajne rozszerzenia w C Cython Wyzwania zwi(cid:200)zane z rozszerzeniami Dodatkowa z(cid:239)o(cid:285)ono(cid:258)(cid:202) Debugowanie Korzystanie z dynamicznych bibliotek bez pisania rozszerze(cid:241) ctypes CFFI Podsumowanie Rozdzia(cid:239) 8. Zarz(cid:200)dzanie kodem Systemy kontroli wersji Scentralizowane systemy kontroli wersji Rozproszone systemy kontroli wersji Systemy scentralizowane czy rozproszone? Korzystaj z systemu Git, je(cid:258)li tylko mo(cid:285)esz Git flow oraz GitHub flow 6 197 198 200 205 206 207 215 215 216 216 218 219 219 221 221 224 226 230 231 232 232 234 235 236 236 236 237 238 253 257 258 258 259 259 265 266 267 268 268 271 274 274 275 Poleć książkęKup książkę Ci(cid:200)g(cid:239)e procesy programistyczne Ci(cid:200)g(cid:239)a integracja oprogramowania Ci(cid:200)g(cid:239)e dostarczanie oprogramowania Ci(cid:200)g(cid:239)e wdra(cid:285)anie oprogramowania Popularne narz(cid:218)dzia do ci(cid:200)g(cid:239)ej integracji Wybór odpowiednich narz(cid:218)dzi i cz(cid:218)ste pu(cid:239)apki Podsumowanie Rozdzia(cid:239) 9. Dokumentowanie projektu Siedem zasad technicznego pisania Pisz w dwóch krokach Skieruj przekaz do konkretnej grupy czytelników Korzystaj z prostego stylu Ogranicz zakres informacji Korzystaj z realistycznych przyk(cid:239)adów Dokumentuj lekko, ale jednocze(cid:258)nie wystarczaj(cid:200)co Korzystaj z szablonów Poradnik reStructuredText Struktura sekcji Listy numerowane i wypunktowania Formatowanie znakowe Bloki dos(cid:239)owne Odno(cid:258)niki Budowanie dokumentacji Budowanie portfolio dokumentacji Tworzenie w(cid:239)asnego portfolio Projektowanie krajobrazu dokumentacji Budowanie dokumentacji a systemy ci(cid:200)g(cid:239)ej integracji Podsumowanie Rozdzia(cid:239) 10. Programowanie sterowane testami Nie testuj(cid:218) Zasady programowania sterowanego testami Mo(cid:285)liwe rodzaje testów Narz(cid:218)dzia testowe standardowej biblioteki Pythona Testuj(cid:218) Pu(cid:239)apki modu(cid:239)u unittest Alternatywy dla modu(cid:239)u unittest Mierzenie pokrycia kodu testami Fa(cid:239)szywe obiekty zast(cid:218)pcze i atrapy Testowanie kompatybilno(cid:258)ci (cid:258)rodowisk i zale(cid:285)no(cid:258)ci Programowanie sterowane dokumentami Podsumowanie Spis tre(cid:286)ci 279 280 284 285 285 294 297 299 300 300 301 302 303 303 304 305 305 307 309 310 310 311 312 312 319 319 324 325 327 327 328 332 335 340 340 341 349 351 358 361 363 7 Poleć książkęKup książkę Spis tre(cid:286)ci Rozdzia(cid:239) 11. Optymalizacja — ogólne zasady i techniki profilowania Trzy zasady optymalizacji Przede wszystkim spraw, aby kod dzia(cid:239)a(cid:239) poprawnie Pracuj z perspektywy u(cid:285)ytkownika Utrzymuj kod czytelnym Strategia optymalizacyjna Poszukaj innego winowajcy Skaluj sprz(cid:218)t Napisz test wydajno(cid:258)ciowy Identyfikowanie w(cid:200)skich garde(cid:239) wydajno(cid:258)ci Profilowanie czasu u(cid:285)ycia procesora Profilowanie zu(cid:285)ycia pami(cid:218)ci Profilowanie po(cid:239)(cid:200)cze(cid:241) sieciowych Podsumowanie Rozdzia(cid:239) 12. Optymalizacja — wybrane skuteczne techniki Redukcja z(cid:239)o(cid:285)ono(cid:258)ci Z(cid:239)o(cid:285)ono(cid:258)(cid:202) cyklomatyczna Notacja du(cid:285)ego O Upraszczanie Przeszukiwanie list Korzystanie ze zbiorów w miejscu list Ukró(cid:202) zewn(cid:218)trzne wywo(cid:239)ania, zredukuj nak(cid:239)ad pracy Korzystanie z modu(cid:239)u collections Stosowanie kompromisów architektonicznych Stosowanie heurystyk i algorytmów aproksymacyjnych Stosowanie kolejek zada(cid:241) i opó(cid:283)nionego przetwarzania Stosowanie probabilistycznych struktur danych Buforowanie Buforowanie deterministyczne Buforowanie niedeterministyczne Us(cid:239)ugi buforuj(cid:200)ce Podsumowanie Rozdzia(cid:239) 13. Przetwarzanie wspó(cid:239)bie(cid:285)ne i równoleg(cid:239)e Dlaczego wspó(cid:239)bie(cid:285)no(cid:258)(cid:202)? Wielow(cid:200)tkowo(cid:258)(cid:202) Czym jest wielow(cid:200)tkowo(cid:258)(cid:202)? Jak Python radzi sobie z w(cid:200)tkami Kiedy nale(cid:285)y korzysta(cid:202) z wielow(cid:200)tkowo(cid:258)ci? Przetwarzanie wieloprocesowe Wbudowany modu(cid:239) multiprocessing Programowanie asynchroniczne Kooperacyjna wielozadaniowo(cid:258)(cid:202) i asynchroniczne operacje wej(cid:258)cia/wyj(cid:258)cia S(cid:239)owa kluczowe async i await 8 365 365 366 367 367 368 368 369 370 370 370 379 389 390 391 392 394 394 397 397 398 398 399 403 403 404 408 409 410 412 413 416 419 420 421 422 423 424 439 442 447 448 449 Poleć książkęKup książkę Spis tre(cid:286)ci asyncio w starszych wersjach Pythona Praktyczny przyk(cid:239)ad programu asynchronicznego Integracja nieasynchronicznego kodu z async za pomoc(cid:200) modu(cid:239)u futures Podsumowanie Rozdzia(cid:239) 14. Przydatne wzorce projektowe Wzorce kreacyjne Singleton Wzorce strukturalne Adapter Pe(cid:239)nomocnik Fasada Wzorce czynno(cid:258)ciowe Obserwator Odwiedzaj(cid:200)cy Szablon Podsumowanie Skorowidz 453 454 456 459 461 462 462 465 466 481 482 483 483 485 488 491 492 9 Poleć książkęKup książkę Poleć książkęKup książkę 7 Rozszerzenia Pythona w innych j(cid:218)zykach programowania Podczas pisania aplikacji bazuj(cid:200)cych na Pythonie nie musisz ogranicza(cid:202) si(cid:218) tylko do samego j(cid:218)zyka Python. Jedn(cid:200) z ciekawych alternatyw — j(cid:218)zyk Hy — przedstawi(cid:239)em w rozdziale 3., „Najlepsze praktyki sk(cid:239)adniowe — powy(cid:285)ej poziomu klas”. Pozwala on na pisanie modu(cid:239)ów, pakietów, a nawet ca(cid:239)ych aplikacji z wykorzystaniem innego j(cid:218)zyka (dialektu Lispa) dzia(cid:239)aj(cid:200)ce- go w wirtualnej maszynie Pythona. W kwestii dostarczanych mo(cid:285)liwo(cid:258)ci powinien by(cid:202) trakto- wany na równi z Pythonem (pomimo swojej ca(cid:239)kowicie odmiennej sk(cid:239)adni), poniewa(cid:285) kompiluje si(cid:218) do tego samego kodu bajtowego. Niestety, oznacza to, (cid:285)e kod napisany w Hy posiada te same ograniczenia co zwyk(cid:239)y kod napisany w Pythonie: (cid:81) u(cid:285)yteczno(cid:258)(cid:202) w(cid:200)tków jest znacznie ograniczona z powodu istnienia mechanizmu GIL; (cid:81) nie jest to j(cid:218)zyk kompilowany; (cid:81) nie dostarcza mechanizmu statycznego typowania oraz wi(cid:200)(cid:285)(cid:200)cych si(cid:218) z nim potencjalnych optymalizacji. Jednym z rozwi(cid:200)za(cid:241) powy(cid:285)szych ogranicze(cid:241) s(cid:200) rozszerzenia napisane w ca(cid:239)kowicie innych j(cid:218)zy- kach programowania, które udost(cid:218)pniaj(cid:200) swoje interfejsy za pomoc(cid:200) API rozszerze(cid:241) Pythona. W tym rozdziale omówi(cid:218) podstawowe powody do pisania w(cid:239)asnych rozszerze(cid:241) Pythona w innych j(cid:218)zykach programowania oraz popularne narz(cid:218)dzia maj(cid:200)ce na celu u(cid:239)atwienie procesu ich two- rzenia. Dowiesz si(cid:218): (cid:81) jak napisa(cid:202) proste rozszerzenie w j(cid:218)zyku C z wykorzystaniem API Python/C; (cid:81) jak zrobi(cid:202) to samo z wykorzystaniem Cythona; Poleć książkęKup książkę Profesjonalne programowanie w Pythonie. Poziom ekspert (cid:81) jakie s(cid:200) najwi(cid:218)ksze wyzwania zwi(cid:200)zane z pisaniem rozszerze(cid:241) oraz generowane przez nie problemy; (cid:81) jak korzysta(cid:202) z kompilowanych bibliotek dynamicznych bez potrzeby tworzenia dedykowanych rozszerze(cid:241) z wykorzystaniem surowego kodu Pythona. Inne j(cid:218)zyki, czyli C lub C++ Kiedy mówi(cid:218) o innych j(cid:218)zykach (w kontek(cid:258)cie rozszerze(cid:241) Pythona), niemal zawsze mam na my(cid:258)li wy(cid:239)(cid:200)cznie C i C++. Nawet narz(cid:218)dzia typu Cython i Pyrex, udost(cid:218)pniaj(cid:200)ce nadzbiory j(cid:218)zyka Py- thon na potrzeby budowania rozszerze(cid:241), s(cid:200) tak naprawd(cid:218) kompilatorami typu (cid:283)ród(cid:239)a-do-(cid:283)róde(cid:239), które generuj(cid:200) kod C na podstawie rozszerzonej sk(cid:239)adni Pythona. Jest oczywi(cid:258)cie mo(cid:285)liwe (cid:239)adowanie w Pythonie dynamicznych/wspó(cid:239)dzielonych bibliotek. Ozna- cza to, (cid:285)e w Pythonie mo(cid:285)na wykorzysta(cid:202) kod dowolnego innego j(cid:218)zyka, który umo(cid:285)liwia kompila- cj(cid:218) tego typu bibliotek. Jednak(cid:285)e biblioteki dynamiczne i wspó(cid:239)dzielone s(cid:200) z definicji uniwersalne. Mo(cid:285)na ich u(cid:285)ywa(cid:202) w kodzie ka(cid:285)dego j(cid:218)zyka, który pozwala na ich (cid:239)adowanie. Oznacza to, (cid:285)e za rozszerzenia Pythona mo(cid:285)na uzna(cid:202) tylko te biblioteki, które korzystaj(cid:200) bezpo(cid:258)rednio z interfejsu Python/C. Niestety, pisanie w(cid:239)asnych rozszerze(cid:241) w j(cid:218)zyku C b(cid:200)d(cid:283) C++ z wykorzystaniem surowego API Python/C jest zadaniem dosy(cid:202) wymagaj(cid:200)cym, i to nie tylko z powodu konieczno(cid:258)ci posiadania szerokiej wiedzy na temat przynajmniej jednego z dwóch j(cid:218)zyków uwa(cid:285)anych generalnie za trudne do opanowania. Otó(cid:285) stworzenie nawet prostego rozszerzenia wymaga napisania ogromnej ilo(cid:258)ci elementów sta(cid:239)ych. S(cid:200) to powtarzaj(cid:200)ce si(cid:218) fragmenty kodu, które maj(cid:200) na celu powi(cid:200)zanie Twojej zaimplementowanej logiki z Pythonem i jego wbudowanymi typami danych. Mimo wszyst- ko dobrze jest zna(cid:202) typow(cid:200) budow(cid:218) rozszerze(cid:241) Pythona napisanych w C, poniewa(cid:285): (cid:81) pomo(cid:285)e Ci to lepiej zrozumie(cid:202) ogóln(cid:200) budow(cid:218) i za(cid:239)o(cid:285)enia Pythona; (cid:81) pewnego dnia mo(cid:285)esz by(cid:202) zmuszony debugowa(cid:202) lub utrzymywa(cid:202) rozszerzenie napisane w C lub C++; (cid:81) pomo(cid:285)e Ci to zrozumie(cid:202) mechanizmy dzia(cid:239)ania wysokopoziomowych narz(cid:218)dzi do budowania rozszerze(cid:241). Jak dzia(cid:239)aj(cid:200) rozszerzenia w C i C++ Interpreter Pythona jest w stanie za(cid:239)adowa(cid:202) rozszerzenia z dynamicznych/wspó(cid:239)dzielonych bibliotek, je(cid:258)li tylko udost(cid:218)pniaj(cid:200) one odpowiedni interfejs z wykorzystaniem API Python/C. Definicja tego API jest za(cid:239)(cid:200)czana w kodzie (cid:283)ród(cid:239)owym rozszerzenia za pomoc(cid:200) pliku nag(cid:239)ówko- wego j(cid:218)zyka C o nazwie Python.h dystrybuowanego wraz z kodem (cid:283)ród(cid:239)owym Pythona. W wielu dystrybucjach Linuksa plik nag(cid:239)ówkowy Python.h jest dostarczany w ramach osobnego pakietu oprogramowania (np. python-dev w systemach Debian/Ubuntu). W Windowsie plik nag(cid:239)ówkowy dostarczany jest domy(cid:258)lnie wraz z dystrybucj(cid:200) Pythona i znajduje si(cid:218) w katalogu includes/ pod (cid:258)cie(cid:285)k(cid:200) instalacyjn(cid:200) interpretera. 232 Poleć książkęKup książkę Rozdzia(cid:225) 7. • Rozszerzenia Pythona w innych j(cid:266)zykach programowania Interfejs Python/C tradycyjnie ulega drobnym zmianom z ka(cid:285)dym wydaniem Pythona. W wi(cid:218)k- szo(cid:258)ci przypadków s(cid:200) to jedynie nowe funkcjonalno(cid:258)ci dodawane do API, przez co zachowywana jest wsteczna kompatybilno(cid:258)(cid:202) na poziomie (cid:283)róde(cid:239). Niestety, wydania Pythona z regu(cid:239)y nie s(cid:200) binarnie wstecznie kompatybilne z powodu zmian w binarnym interfejsie aplikacji (ang. Application Binary Interface — ABI). Oznacza to, (cid:285)e rozszerzenie musi by(cid:202) zbudowane osobno dla ka(cid:285)dej wspieranej wersji Pythona. Dodatkowo ró(cid:285)ne systemy operacyjne nie s(cid:200) mi(cid:218)dzy sob(cid:200) binarnie kompatybilne, co czyni w(cid:239)a(cid:258)ciwie niemo(cid:285)liwym dostarczenie gotowej binarnej dystrybucji skompilowanego rozszerzenia dzia(cid:239)aj(cid:200)cej w ka(cid:285)dym mo(cid:285)liwym (cid:258)rodowisku. Dlatego w(cid:239)a(cid:258)nie wi(cid:218)kszo(cid:258)(cid:202) rozszerze(cid:241) dystrybuowana jest jedynie w postaci (cid:283)ród(cid:239)owej. Od Pythona w wersji 3.2 podzbiór interfejsu Python/C zosta(cid:239) okre(cid:258)lony jako posiadaj(cid:200)cy stabil- ne ABI. Jest wi(cid:218)c mo(cid:285)liwe skompilowanie rozszerzenia opartego na ograniczonym API Python/C (tym o stabilnym ABI), które b(cid:218)dzie dzia(cid:239)a(cid:239)o ze wszystkimi wydaniami interpretera Pythona w wersjach 3.2 i wy(cid:285)szych. Ogranicza to jednak zakres dost(cid:218)pnych elementów API i nie rozwi(cid:200)zuje problemu dystrybucji rozszerze(cid:241) dla starszych wersji Pythona oraz dla ró(cid:285)nych systemów opera- cyjnych. Jest to wi(cid:218)c kompromis, przy którym traci si(cid:218) wyj(cid:200)tkowo du(cid:285)o, a zyskuje wci(cid:200)(cid:285) niewiele. Niezwykle wa(cid:285)n(cid:200) informacj(cid:200), o której musisz wiedzie(cid:202), jest fakt, (cid:285)e API Python/C to funkcjonal- no(cid:258)(cid:202) dost(cid:218)pna jedynie dla interpretera CPython. Twórcy g(cid:239)ównych alternatywnych implementacji Pythona (PyPy, Jython oraz IronPython) od lat próbuj(cid:200) umo(cid:285)liwi(cid:202) bezproblemowe wykorzystanie rozszerze(cid:241), ale na chwil(cid:218) obecn(cid:200) (cid:285)adna z nich nie dostarcza w pe(cid:239)ni dzia(cid:239)aj(cid:200)cego rozwi(cid:200)zania. Jedyn(cid:200) alternatywn(cid:200) implementacj(cid:200) Pythona oferuj(cid:200)c(cid:200) pe(cid:239)ne wsparcie dla rozszerze(cid:241) jest Stac- kless Python, poniewa(cid:285) jest to tak naprawd(cid:218) interpreter bazuj(cid:200)cy na zmodyfikowanych (cid:283)ród(cid:239)ach CPythona. Rozszerzenia C/C++ dla Pythona musz(cid:200) by(cid:202) skompilowane do postaci dynamicznych/ wspó(cid:239)dzielonych bibliotek, zanim b(cid:218)d(cid:200) mog(cid:239)y by(cid:202) wykorzystane, poniewa(cid:285) z oczywistych wzgl(cid:218)dów niemo(cid:285)liwe jest bezpo(cid:258)rednie zaimportowanie (cid:283)róde(cid:239) w C/C++ z poziomu kodu Pythona. Na szcz(cid:218)(cid:258)cie biblioteki distutils i setuptools udost(cid:218)pniaj(cid:200) odpowiednie klasy pomocni- cze pozwalaj(cid:200)ce zdefiniowa(cid:202) rozszerzenia jako modu(cid:239)y pakietu Pythona. Dzi(cid:218)ki temu rozszerzenia mog(cid:200) by(cid:202) budowane i dystrybuowane za pomoc(cid:200) skryptu setup.py, tak jak zwyk(cid:239)e pakiety Pythona. Poni(cid:285)szy listing zawiera przyk(cid:239)ad skryptu setup.py z oficjalnej dokumentacji Pytho- na odpowiedzialny za pakowanie prostego pakietu zawieraj(cid:200)cego wbudowane rozszerzenia: from distutils.core import setup, Extension module1 = Extension( demo , sources=[ demo.c ] ) setup( name= PackageName , version= 1.0 , 233 Poleć książkęKup książkę Profesjonalne programowanie w Pythonie. Poziom ekspert description= To jest pakiet demonstracyjny. , ext_modules=[module1] ) Taki skrypt wymaga u(cid:285)ycia dodatkowego polecenia podczas typowej procedury dystrybucyjnej: python setup.py build Polecenie to spowoduje skompilowanie wszystkich rozszerze(cid:241) podanych w li(cid:258)cie argumentu ext_modules zgodnie z dodatkowymi ustawieniami kompilatora zawartymi w wywo(cid:239)aniu konstruktora klasy Extension(). Kompilatorem u(cid:285)ytym w procesie budowy b(cid:218)dzie domy(cid:258)lny kompilator dla Twojego (cid:258)rodowiska. Sam krok kompilacji nie jest wymagany, je(cid:258)li rozszerzenie ma by(cid:202) dystrybuowane w formie (cid:283)ród(cid:239)owej. W takim przypadku musisz si(cid:218) upewni(cid:202), (cid:285)e (cid:258)rodowi- sko docelowe zawiera wszystkie sk(cid:239)adniki potrzebne w procesie kompilacji, takie jak kompilator, pliki nag(cid:239)ówkowe, oraz dodatkowe biblioteki potrzebne w czasie linkowania (je(cid:258)li Twoje rozsze- rzenie takowych wymaga). Wi(cid:218)cej szczegó(cid:239)ów na temat pakowania i dystrybucji rozszerze(cid:241) Py- thona zawartych b(cid:218)dzie w podrozdziale „Wyzwania zwi(cid:200)zane z rozszerzeniami”. Dlaczego warto tworzy(cid:202) rozszerzenia Nie da si(cid:218) prosto odpowiedzie(cid:202) na pytanie o to, kiedy warto zdecydowa(cid:202) si(cid:218) na pisanie w(cid:239)asne- go rozszerzenia Pythona w j(cid:218)zyku C b(cid:200)d(cid:283) C++. Praktyczna zasada mog(cid:239)aby brzmie(cid:202): „Tylko wtedy, gdy nie ma innej opcji”. Jest to jednak bardzo subiektywna regu(cid:239)a, gdy(cid:285) pozostawia miej- sce na osobist(cid:200) interpretacj(cid:218) tego, co jest, a co nie jest wykonywalne w Pythonie. W rzeczywisto(cid:258)ci trudno wskaza(cid:202) cho(cid:202)by jeden problem, którego nie da(cid:239)oby si(cid:218) w ca(cid:239)o(cid:258)ci wyeliminowa(cid:202), pos(cid:239)u- guj(cid:200)c si(cid:218) wy(cid:239)(cid:200)cznie czystym kodem Pythona. Ca(cid:239)kowicie inna kwestia to to, czy ostateczne roz- wi(cid:200)zanie b(cid:218)dzie wystarczaj(cid:200)co dobre i wydajne. W praktyce rozszerzenia Pythona w C/C++ s(cid:200) szczególnie u(cid:285)yteczne w nast(cid:218)puj(cid:200)cych sytuacjach: (cid:81) omijanie mechanizmu GIL (globalnej blokady interpretera) w programowaniu wielow(cid:200)tkowym; (cid:81) zwi(cid:218)kszanie wydajno(cid:258)ci w krytycznych sekcjach kodu; (cid:81) integracja zewn(cid:218)trznych bibliotek dynamicznych; (cid:81) integracja kodu napisanego w innych j(cid:218)zykach programowania; (cid:81) tworzenie w(cid:239)asnych wbudowanych typów danych. Nie oznacza to jednak, (cid:285)e którykolwiek z powy(cid:285)szych problemów jest nierozwi(cid:200)zywalny bez u(cid:285)ycia rozszerze(cid:241). Nawet tak kluczowe ograniczenie interpretera jak GIL mo(cid:285)e by(cid:202) przezwy- ci(cid:218)(cid:285)one dzi(cid:218)ki wykorzystaniu ca(cid:239)kowicie innego podej(cid:258)cia do przetwarzania równoleg(cid:239)ego: zasto- sowania zielonych w(cid:200)tków b(cid:200)d(cid:283) modelu przetwarzania opartego na wielu procesach. 234 Poleć książkęKup książkę Rozdzia(cid:225) 7. • Rozszerzenia Pythona w innych j(cid:266)zykach programowania Zwi(cid:218)kszanie wydajno(cid:258)ci w krytycznych sekcjach kodu B(cid:200)d(cid:283)my szczerzy: g(cid:239)ównym powodem, dla którego tak wielu programistów wybiera Pythona, na pewno nie jest wydajno(cid:258)(cid:202). Kod Pythona nie wykonuje si(cid:218) szybko, za to mo(cid:285)emy za jego pomoc(cid:200) niezwykle szybko tworzy(cid:202) nowe oprogramowanie. Mimo wszystko, bez wzgl(cid:218)du na to, jak wybit- nymi programistami jeste(cid:258)my, pr(cid:218)dzej czy pó(cid:283)niej natrafimy na problem, którego wydajnie roz- wi(cid:200)za(cid:202) w Pythonie si(cid:218) nie da. W wi(cid:218)kszo(cid:258)ci przypadków rozwi(cid:200)zywanie problemów wydajno(cid:258)ci to kwestia doboru odpowied- nich algorytmów i struktur danych, a nie zadanie ograniczania sta(cid:239)ego narzutu wydajno(cid:258)ciowego zwi(cid:200)zanego z u(cid:285)yt(cid:200) technologi(cid:200). W(cid:239)a(cid:258)ciwie uciekanie si(cid:218) do rozszerze(cid:241) Pythona w celu ugrania kilku cyklów procesora w sytuacji, gdy kod jest napisany (cid:283)le i nie wykorzystuje odpowiednich algorytmów, to niezwykle szkodliwa praktyka. Dosy(cid:202) cz(cid:218)sto wydajno(cid:258)(cid:202) kodu mo(cid:285)na znacznie poprawi(cid:202) bez potrzeby zwi(cid:218)kszania z(cid:239)o(cid:285)ono(cid:258)ci projektu i do(cid:239)(cid:200)czania nowego j(cid:218)zyka programi- stycznego do technologicznego stosu aplikacji. Je(cid:258)li to tylko mo(cid:285)liwe, zwi(cid:218)kszanie wydajno(cid:258)ci aplikacji powinno odbywa(cid:202) si(cid:218) przede wszystkim w ten sposób. Jednak w pewnych sytuacjach mo(cid:285)e si(cid:218) okaza(cid:202), (cid:285)e kod napisany w Pythonie nawet z wykorzystaniem najlepszych znanych algo- rytmów i struktur danych nie b(cid:218)dzie w stanie zmie(cid:258)ci(cid:202) si(cid:218) w pewnych arbitralnych limitach czasu wykonania b(cid:200)d(cid:283) dost(cid:218)pnych zasobów. Przyk(cid:239)adow(cid:200) dziedzin(cid:200) biznesow(cid:200) nak(cid:239)adaj(cid:200)c(cid:200) (cid:258)ci(cid:258)le okre(cid:258)lone ograniczenia na wydajno(cid:258)(cid:202) aplika- cji jest rynek handlu przestrzeni(cid:200) reklamow(cid:200) w modelu RTB (ang. Real Time Bidding). W skrócie model RTB opiera si(cid:218) na zakupie i sprzeda(cid:285)y internetowej przestrzeni reklamowej w modelu aukcyjnym przypominaj(cid:200)cym handel udzia(cid:239)ami na gie(cid:239)dach. Handel przestrzeni(cid:200) reklamow(cid:200) odbywa si(cid:218) z regu(cid:239)y za po(cid:258)rednictwem wyspecjalizowanej us(cid:239)ugi gie(cid:239)dowej informuj(cid:200)cej w czasie rzeczywistym platformy popytowe (ang. Demand Side Platform — DSP) o pojawiaj(cid:200)cych si(cid:218) mo(cid:285)liwo(cid:258)ciach zakupu powierzchni. I to jest miejsce, w którym wszystko zaczyna by(cid:202) naprawd(cid:218) ekscytuj(cid:200)ce. Wi(cid:218)kszo(cid:258)(cid:202) gie(cid:239)d reklamowych na rynku RTB korzysta z protoko(cid:239)u OpenRTB (bazuj(cid:200)- cego na HTTP) w komunikacji z potencjalnymi kupcami znajduj(cid:200)cymi si(cid:218) po stronie DSP. W tym modelu to kupcy s(cid:200) faktycznymi serwerami HTTP, a gie(cid:239)da poprzez (cid:285)(cid:200)dania informuje o nowych mo(cid:285)liwo(cid:258)ciach kupna. Gie(cid:239)dy z regu(cid:239)y nak(cid:239)adaj(cid:200) bardzo (cid:258)cis(cid:239)e ograniczenia dotycz(cid:200)ce czasu, w jakim odpowied(cid:283) na (cid:285)(cid:200)danie musi by(cid:202) obs(cid:239)u(cid:285)ona (zwykle pomi(cid:218)dzy 50 a 100 ms). Ograniczenie to dotyczy ca(cid:239)o(cid:258)ci komunikacji — od pierwszego bajta wys(cid:239)anego przez gie(cid:239)d(cid:218) a(cid:285) po ostatni bajt odebrany w odpowiedzi, wliczaj(cid:200)c w to czas podró(cid:285)y pakietów TCP w obie strony. Aby jeszcze podgrza(cid:202) atmosfer(cid:218), warto wspomnie(cid:202), (cid:285)e dla platform DSP nie jest rzadko(cid:258)ci(cid:200) obs(cid:239)u- giwanie dziesi(cid:200)tek tysi(cid:218)cy (cid:285)(cid:200)da(cid:241) na sekund(cid:218). Mo(cid:285)liwo(cid:258)(cid:202) przyspieszenia czasu przetwarzania (cid:285)(cid:200)da(cid:241) HTTP nawet o par(cid:218) milisekund mo(cid:285)e decydowa(cid:202) o sukcesie ca(cid:239)ego produktu w tej specy- ficznej bran(cid:285)y. Oznacza to, (cid:285)e w okre(cid:258)lonych sytuacjach przeniesienie nawet prostego kodu do C mo(cid:285)e mie(cid:202) praktyczny sens, oczywi(cid:258)cie pod warunkiem, (cid:285)e przenoszony kod stanowi w(cid:200)skie gard(cid:239)o wydajno(cid:258)ci i nie mo(cid:285)e ju(cid:285) zosta(cid:202) ulepszony w (cid:285)aden inny sposób. Zgodnie z tym, co kiedy(cid:258) rzek(cid:239) Guido van Rossum: „Je(cid:258)li potrzebujesz szybko(cid:258)ci (…) — nie jeste(cid:258) w stanie pobi(cid:202) p(cid:218)tli napisanej w C”. 235 Poleć książkęKup książkę Profesjonalne programowanie w Pythonie. Poziom ekspert Integracja kodu napisanego w innych j(cid:218)zykach programowania W czasie krótkiej historii informatyki powsta(cid:239)o wyj(cid:200)tkowo du(cid:285)o u(cid:285)ytecznych bibliotek. By(cid:239)oby wielk(cid:200) strat(cid:200) zapominanie o ca(cid:239)ym tym dziedzictwie za ka(cid:285)dym razem, gdy pojawia si(cid:218) nowy j(cid:218)zyk programowania. Z drugiej strony, niemo(cid:285)liwe jest przenoszenie ka(cid:285)dego istniej(cid:200)cego kawa(cid:239)- ka oprogramowania do wszystkich mo(cid:285)liwych j(cid:218)zyków. J(cid:218)zyki C i C++ s(cid:200) najwa(cid:285)niejszymi j(cid:218)zykami dostarczaj(cid:200)cymi ogrom bibliotek i wzorcowych implementacji wielu algorytmów, które móg(cid:239)by(cid:258) chcie(cid:202) zintegrowa(cid:202) w swoich aplikacjach bez potrzeby przenoszenia ich kodu do Pythona. Na szcz(cid:218)(cid:258)cie interpreter CPython jest napisany w C, dlatego najbardziej naturalna droga integracji takich bibliotek wiedzie przez w(cid:239)asne rozszerzenia Pythona. Integracja zewn(cid:218)trznych bibliotek dynamicznych Integracja kodu napisanego w innych j(cid:218)zykach programowania nie ko(cid:241)czy si(cid:218) na C/C++. Wiele bibliotek, szczególnie oprogramowania osób trzecich o zamkni(cid:218)tym kodzie, dystrybu- owanych jest w postaci skompilowanych plików binarnych. W j(cid:218)zyku C niezwykle (cid:239)atwo jest (cid:239)adowa(cid:202) takie dynamiczne/wspó(cid:239)dzielone biblioteki i wywo(cid:239)ywa(cid:202) ich funkcje. Oznacza to, (cid:285)e mo(cid:285)esz korzysta(cid:202) z dowolnej skompilowanej biblioteki dynamicznej, je(cid:258)li tylko opakujesz j(cid:200) od- powiednim rozszerzeniem za pomoc(cid:200) API Python/C. Oczywi(cid:258)cie nie jest to jedyna dost(cid:218)pna droga. Istniej(cid:200) narz(cid:218)dzia (takie jak ctypes czy cffi), które pozwalaj(cid:200) na (cid:239)adowanie dynamicznych bibliotek za pomoc(cid:200) czystego kodu Pythona oraz bez konieczno(cid:258)ci pisania w(cid:239)asnych rozszerze(cid:241) w C. W wielu przypadkach rozszerzenia opieraj(cid:200)ce si(cid:218) na interfejsie Python/C wci(cid:200)(cid:285) pozostaj(cid:200) du(cid:285)o lepszym wyborem, poniewa(cid:285) pozwalaj(cid:200) na lepsz(cid:200) separacj(cid:218) warstwy integracji (napisanej w C) od reszty Twojej aplikacji. Tworzenie w(cid:239)asnych wbudowanych typów danych Python dostarcza bardzo wszechstronnej kolekcji wbudowanych typów. Niektóre z nich s(cid:200) oparte na najnowocze(cid:258)niejszych implementacjach (przynajmniej w CPythonie), które w dodatku s(cid:200) specjalnie dopasowane do wykorzystania w kontek(cid:258)cie tego j(cid:218)zyka. Liczba dost(cid:218)pnych typów i kontenerów mo(cid:285)e by(cid:202) imponuj(cid:200)ca dla nowicjuszy, ale nie pokrywaj(cid:200) one wszystkich mo(cid:285)liwych potrzeb u(cid:285)ytkowników. Mo(cid:285)esz oczywi(cid:258)cie tworzy(cid:202) wiele nowych struktur danych bezpo(cid:258)rednio w Pythonie, opieraj(cid:200)c je na istniej(cid:200)cych ju(cid:285) typach wbudowanych lub definiuj(cid:200)c od zera za pomoc(cid:200) w(cid:239)asnych klas. Niestety, tak zdefiniowane struktury danych w specyficznych przypadkach mog(cid:200) nie oferowa(cid:202) wystarczaj(cid:200)cej wydajno(cid:258)ci. Ca(cid:239)a si(cid:239)a z(cid:239)o(cid:285)onych struktur danych (takich jak dict) bierze si(cid:218) w(cid:239)a(cid:258)nie 236 Poleć książkęKup książkę Rozdzia(cid:225) 7. • Rozszerzenia Pythona w innych j(cid:266)zykach programowania z wewn(cid:218)trznej implementacji interpretera. W przypadku CPythona korzystamy wi(cid:218)c z wydajnej implementacji w j(cid:218)zyku C. Dlaczego by nie pój(cid:258)(cid:202) t(cid:200) sam(cid:200) drog(cid:200) i nie zdefiniowa(cid:202) w(cid:239)asnych typów danych równie(cid:285) w C? Pisanie rozszerze(cid:241) Zgodnie z tym, co zosta(cid:239)o ju(cid:285) powiedziane, pisanie rozszerze(cid:241) nie jest zadaniem prostym. Oferuj(cid:200) one jednak w zamian wiele zalet. Najprostsz(cid:200) i rekomendowan(cid:200) technik(cid:200) tworzenia rozszerze(cid:241) jest wykorzystanie zaawansowanych narz(cid:218)dzi takich jak Cython czy Pyrex lub (w przypadku in- tegracji kodu/bibliotek innych j(cid:218)zyków) oparcie swojego kodu na bibliotekach ctypes lub cffi. Projekty te znacznie zwi(cid:218)ksz(cid:200) Twoj(cid:200) produktywno(cid:258)(cid:202) i sprawi(cid:200), (cid:285)e kod rozszerzenia b(cid:218)dzie (cid:239)atwiej- szy w rozwoju, czytaniu oraz utrzymaniu. Mimo wszystko, je(cid:258)li jest to dla Ciebie nowy temat, dobrze, (cid:285)eby(cid:258) zacz(cid:200)(cid:239) swoj(cid:200) przygod(cid:218) z rozsze- rzeniami od napisania jakiego(cid:258) z wykorzystaniem jedynie j(cid:218)zyka C i interfejsu Python/C. Poprawi to Twoje zrozumienie dzia(cid:239)ania rozszerze(cid:241) i sprawi, (cid:285)e docenisz zalety alternatywnych rozwi(cid:200)za(cid:241). Przez wzgl(cid:200)d na prostot(cid:218) we(cid:283)miemy na warsztat pewien prosty problem algorytmiczny i spróbu- jemy zaimplementowa(cid:202) jego rozwi(cid:200)zanie w postaci rozszerzenia Pythona na dwa ró(cid:285)ne sposoby: (cid:81) tworz(cid:200)c rozszerzenie bezpo(cid:258)rednio w j(cid:218)zyku C; (cid:81) korzystaj(cid:200)c z j(cid:218)zyka Cython. Naszym zadaniem b(cid:218)dzie napisanie funkcji wyznaczaj(cid:200)cej n-ty wyraz ci(cid:200)gu Fibonacciego. Potrze- ba tworzenia rozszerzenia wy(cid:239)(cid:200)cznie w celu implementacji takiej funkcji jest ma(cid:239)o prawdopo- dobna. Jednak(cid:285)e jest to problem na tyle prosty, (cid:285)e b(cid:218)dzie (cid:258)wietnie s(cid:239)u(cid:285)y(cid:239) jako podstawa wi(cid:200)zania kodu w j(cid:218)zyku C z Pythonem za pomoc(cid:200) interfejsu Python/C. Naszym celem jest przede wszyst- kim uzyskanie przejrzystego i prostego rozwi(cid:200)zania, dlatego nie b(cid:218)dziemy starali si(cid:218) wykorzy- sta(cid:202) metody optymalnej. Za wzorcow(cid:200) implementacj(cid:218) pos(cid:239)u(cid:285)y nam nast(cid:218)puj(cid:200)cy modu(cid:239) Pythona: Modu(cid:273) Pythona dostarczaj(cid:261)cy funkcj(cid:266) fibonacci. def fibonacci(n): Zwró(cid:252) n-ty wyraz ci(cid:261)gu Fibonacciego wyznaczony rekurencyjnie. if n 2: return 1 else: return fibonacci(n - 1) + fibonacci(n - 2) Zauwa(cid:285), (cid:285)e jest to jedna z najprostszych mo(cid:285)liwych implementacji funkcji fibonacci() i z pewno- (cid:258)ci(cid:200) mo(cid:285)na by j(cid:200) usprawni(cid:202) na wiele sposobów. Nie b(cid:218)dziemy jednak na tym etapie stosowa(cid:202) (cid:285)adnego z oczywistych ulepsze(cid:241) (np. memoizacji), poniewa(cid:285) nie s(cid:200) one celem naszego zadania. Na tej samej zasadzie b(cid:218)dziemy si(cid:218) powstrzymywa(cid:202) od stosowania oczywistych optymalizacji na pó(cid:283)niejszych etapach omawiania kodu w C oraz Cythonie, pomimo (cid:285)e te j(cid:218)zyki dostarczaj(cid:200) ku temu wi(cid:218)cej sposobno(cid:258)ci. 237 Poleć książkęKup książkę Profesjonalne programowanie w Pythonie. Poziom ekspert Zwyczajne rozszerzenia w C Zanim w pe(cid:239)ni zanurkujesz w tematyk(cid:218) rozszerze(cid:241) Pythona napisanych w C, nale(cid:285)(cid:200) Ci si(cid:218) s(cid:239)owa ostrze(cid:285)enia. Je(cid:258)li zamierzasz rozszerza(cid:202) Pythona za pomoc(cid:200) C, powiniene(cid:258) sprawnie pos(cid:239)ugiwa(cid:202) si(cid:218) oboma tymi j(cid:218)zykami programowania. Szczegó(cid:239)owa wiedza o mechanizmach j(cid:218)zyka jest wa(cid:285)na zw(cid:239)aszcza w przypadku C. Brak bieg(cid:239)o(cid:258)ci w tym j(cid:218)zyku mo(cid:285)e prowadzi(cid:202) do prawdziwych katastrof ze wzgl(cid:218)du na (cid:239)atwo(cid:258)(cid:202) pope(cid:239)niania krytycznych b(cid:239)(cid:218)dów. Je(cid:258)li zdecydowa(cid:239)e(cid:258) si(cid:218) ju(cid:285) na pisanie rozszerze(cid:241) Pythona w C, to zak(cid:239)adam, (cid:285)e znasz ten j(cid:218)zyk w stopniu pozwalaj(cid:200)cym Ci zrozumie(cid:202) proste przyk(cid:239)ady zawarte w tym rozdziale. W kontek(cid:258)cie C b(cid:218)dziemy t(cid:239)umaczy(cid:202) jedynie zawi(cid:239)o(cid:258)ci interfejsu Python/C oraz niektóre mechanizmy dzia(cid:239)ania interpretera CPython. Wszystko dlatego, (cid:285)e jest to ksi(cid:200)(cid:285)ka o Pythonie, a nie (cid:285)adnym innym j(cid:218)zyku programowania. Je(cid:258)li nie potrafisz programowa(cid:202) w C, zdecydowanie nie powiniene(cid:258) próbowa(cid:202) pisa(cid:202) w nim swojego pierwszego rozszerzenia. Lepiej zostawi(cid:202) to zadanie innym i skorzysta(cid:202) z bardziej przyst(cid:218)pnych dla pocz(cid:200)tkuj(cid:200)cych narz(cid:218)dzi Cython lub Pyrex. Interfejs Python/C, mimo (cid:285)e stworzony i zaprojektowany z wielk(cid:200) staranno(cid:258)ci(cid:200), nie jest dobrym materia(cid:239)em wprowadzaj(cid:200)- cym do j(cid:218)zyka C. Tak jak zosta(cid:239)o to zapowiedziane wcze(cid:258)niej, spróbujemy przenie(cid:258)(cid:202) nasz(cid:200) pythonow(cid:200) funkcj(cid:218) fibonacci() do j(cid:218)zyka C i udost(cid:218)pni(cid:202) nasze rozwi(cid:200)zanie interpreterowi Pythona. Surowa imple- mentacja funkcji w C (tj. bez kodu wi(cid:200)(cid:285)(cid:200)cego j(cid:200) z interpreterem za pomoc(cid:200) interfejsu Python/C) b(cid:218)dzie analogiczna do wcze(cid:258)niejszego przyk(cid:239)adu w Pythonie: long long fibonacci(unsigned int n) { if (n 2) { return 1; } else { return fibonacci(n - 2) + fibonacci(n - 1); } } Poni(cid:285)szy listing zawiera kod w pe(cid:239)ni funkcjonalnego rozszerzenia udost(cid:218)pniaj(cid:200)cego powy(cid:285)sz(cid:200) funkcj(cid:218) w skompilowanym module: #include Python.h long long fibonacci(unsigned int n) { if (n 2) { return 1; } else { return fibonacci(n-2) + fibonacci(n-1); } } static PyObject* fibonacci_py(PyObject* self, PyObject* args) { PyObject *result = NULL; 238 Poleć książkęKup książkę Rozdzia(cid:225) 7. • Rozszerzenia Pythona w innych j(cid:266)zykach programowania long n; if (PyArg_ParseTuple(args, l , n)) { result = Py_BuildValue( L , fibonacci((unsigned int)n)); } return result; } static char fibonacci_docs[] = fibonacci(n): Zwró(cid:202) n-ty wyraz ci(cid:200)gu Fibonacciego wyznaczony rekurencyjnie.\n ; static PyMethodDef fibonacci_module_methods[] = { { fibonacci , (PyCFunction)fibonacci_py, METH_VARARGS, fibonacci_docs}, {NULL, NULL, 0, NULL} }; static struct PyModuleDef fibonacci_module_definition = { PyModuleDef_HEAD_INIT, fibonacci , Modu(cid:239) rozszerzenia dostarczaj(cid:200)cy funkcj(cid:218) fibonacci. , -1, fibonacci_module_methods }; PyMODINIT_FUNC PyInit_fibonacci(void) { Py_Initialize(); return PyModule_Create( fibonacci_module_definition); } Powy(cid:285)szy przyk(cid:239)ad mo(cid:285)e by(cid:202) nieco przyt(cid:239)aczaj(cid:200)cy, poniewa(cid:285) wyeksponowanie prostej w za(cid:239)o(cid:285)e- niu funkcji wymaga(cid:239)o u(cid:285)ycia co najmniej czterokrotno(cid:258)ci pocz(cid:200)tkowego kodu. Nie martw si(cid:218), za- raz omówi(cid:218) ka(cid:285)dy z elementów tego kodu. Zanim jednak przyst(cid:200)pi(cid:218) do tej analizy, poka(cid:285)(cid:218) jeszcze, jak zamkn(cid:200)(cid:202) powy(cid:285)szy kod w postaci pakietu gotowego do dystrybucji. Minimalna konfiguracja setuptools b(cid:218)dzie wymaga(cid:239)a u(cid:285)ycia konstruktora klasy setuptools.Extension w celu poinfor- mowania interpretera o tym, które pliki (cid:283)ród(cid:239)owe rozszerzenia wymagaj(cid:200) kompilacji: from setuptools import setup, Extension setup( name= fibonacci , 239 Poleć książkęKup książkę Profesjonalne programowanie w Pythonie. Poziom ekspert ext_modules=[ Extension( fibonacci , [ fibonacci.c ]), ] ) Proces kompilacji rozszerzenia mo(cid:285)e by(cid:202) jawnie zainicjowany za pomoc(cid:200) polecenia build skryptu setup.py, ale zostanie równie(cid:285) wykonany automatycznie podczas instalacji pakietu. Poni(cid:285)szy listing przedstawia rezultat instalacji naszego rozszerzenia w trybie roboczym oraz zapis interaktywnej sesji interpretera z u(cid:285)yciem tak skompilowanego modu(cid:239)u: $ ls -1a fibonacci.c setup.py $ pip install -e . Obtaining file:///Users/swistakm/dev/book/chapter7 Installing collected packages: fibonacci Running setup.py develop for fibonacci Successfully installed Fibonacci $ ls -1ap build/ fibonacci.c fibonacci.cpython-35m-darwin.so fibonacci.egg-info/ setup.py $ python Python 3.5.1 (v3.5.1:37a07cee5969, Dec 5 2015, 21:12:44) [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin Type help , copyright , credits or license for more information. import fibonacci help(fibonacci.fibonacci) Help on built-in function fibonacci in fibonacci: fibonacci.fibonacci = fibonacci(...) fibonacci(n): Zwró(cid:202) n-ty wyraz ci(cid:200)gu Fibonacciego wyznaczony rekurencyjnie. [fibonacci.fibonacci(n) for n in range(10)] [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] Bli(cid:285)sze spojrzenie na interfejs Python/C Wiesz ju(cid:285), jak poprawnie spakowa(cid:202), skompilowa(cid:202) oraz zainstalowa(cid:202) w(cid:239)asne rozszerzenie Pythona w j(cid:218)zyku C. Wiesz równie(cid:285), (cid:285)e dzia(cid:239)a ono tak, jak oczekiwali(cid:258)my. Nadszed(cid:239) wi(cid:218)c czas, aby przyj- rze(cid:202) si(cid:218) dok(cid:239)adnie poszczególnym elementom jego kodu. 240 Poleć książkęKup książkę Rozdzia(cid:225) 7. • Rozszerzenia Pythona w innych j(cid:266)zykach programowania Modu(cid:239) rozszerzenia rozpoczyna si(cid:218) od dyrektywy preprocesora C do(cid:239)(cid:200)czaj(cid:200)cej plik nag(cid:239)ówkowy Python.h: #include Python.h Dyrektywa ta za(cid:239)(cid:200)cza definicj(cid:218) ca(cid:239)ego interfejsu Python/C i jest wszystkim, czego potrzebujesz, aby móc napisa(cid:202) w(cid:239)asne rozszerzenie. W bardziej realistycznych przypadkach Twój kod b(cid:218)dzie wymaga(cid:239) u(cid:285)ycia wi(cid:218)kszej liczby dyrektyw include, aby(cid:258) móg(cid:239) wykorzysta(cid:202) elementy standardo- wej biblioteki j(cid:218)zyka C oraz funkcje z pozosta(cid:239)ych bibliotek. Nasz przyk(cid:239)ad jest wyj(cid:200)tkowo pro- sty, dlatego nie wymaga za(cid:239)(cid:200)czenia (cid:285)adnych innych plików nag(cid:239)ówkowych. Dalej w kodzie znajduje si(cid:218) kluczowy element naszego modu(cid:239)u: long long fibonacci(unsigned int n) { if (n 2) { return 1; } else { return fibonacci(n - 2) + fibonacci(n - 1); } } Powy(cid:285)sza funkcja fibonacci() jest jedyn(cid:200) u(cid:285)yteczn(cid:200) cz(cid:218)(cid:258)ci(cid:200) naszego kodu wykonuj(cid:200)c(cid:200) jak(cid:200)kol- wiek prac(cid:218). Jest to czysta implementacja C, której Python nie jest w stanie domy(cid:258)lnie zrozu- mie(cid:202). Reszta kodu b(cid:218)dzie odpowiada(cid:239)a za stworzenie odpowiedniej warstwy interfejsu ekspo- nuj(cid:200)cej powy(cid:285)sz(cid:200) funkcj(cid:218) za pomoc(cid:200) API Python/C. Pierwszym krokiem w udost(cid:218)pnieniu naszego kodu j(cid:218)zyka C interpreterowi CPythona jest stworzenie funkcji j(cid:218)zyka C kompatybilnej z interfejsem tego interpretera. W Pythonie wszyst- ko jest obiektem. Oznacza to, (cid:285)e funkcja C wywo(cid:239)ywana w Pythonie musi równie(cid:285) zwraca(cid:202) prawdziwe obiekty Pythona. Interfejs Python/C dostarcza typ PyObject i ka(cid:285)da funkcja wywo- (cid:239)ywana przez interpreter musi zwraca(cid:202) wska(cid:283)nik zmiennej tego typu. Sygnatura naszej funkcji jest nast(cid:218)puj(cid:200)ca: static PyObject* fibonacci_py(PyObject* self, PyObject* args) Zwró(cid:202) uwag(cid:218) na fakt, (cid:285)e powy(cid:285)sza sygnatura nie okre(cid:258)la dok(cid:239)adnej listy przyjmowanych argu- mentów, a jedynie obiekt PyObject* args b(cid:218)d(cid:200)cy wska(cid:283)nikiem krotki z przekazanymi do funkcji warto(cid:258)ciami. Ostateczna weryfikacja przekazanej listy argumentów musi si(cid:218) odby(cid:202) w ciele sa- mej funkcji i tym w(cid:239)a(cid:258)nie zajmuje si(cid:218) funkcja fibonacci_py(). Analizuje list(cid:218) argumentów args, zak(cid:239)adaj(cid:200)c, (cid:285)e zawiera ona tylko jeden element typu unsigned int, i przekazuj(cid:200)c jego warto(cid:258)(cid:202) do wywo(cid:239)ania fibonacci() w celu uzyskania warto(cid:258)ci wskazanego wyrazu ci(cid:200)gu Fibonacciego: static PyObject* fibonacci_py(PyObject* self, PyObject* args) { PyObject *result = NULL; long n; if (PyArg_ParseTuple(args, l , n)) { 241 Poleć książkęKup książkę Profesjonalne programowanie w Pythonie. Poziom ekspert result = Py_BuildValue( L , fibonacci((unsigned int)n)); } return result; } Powy(cid:285)szy przyk(cid:239)ad zawiera pewne powa(cid:285)ne b(cid:239)(cid:218)dy, które z (cid:239)atwo(cid:258)ci(cid:200) powinien dostrzec do(cid:258)wiadczony pro- gramista. Spróbuj je odnale(cid:283)(cid:202) na w(cid:239)asn(cid:200) r(cid:218)k(cid:218) w ramach (cid:202)wiczenia pracy z rozszerzeniami w C. Dla zwi(cid:218)z(cid:239)o(cid:258)ci pozostawi(cid:218) chwilowo kwesti(cid:218) wspomnianych b(cid:239)(cid:218)dów nierozwi(cid:200)zan(cid:200). Zajm(cid:218) si(cid:218) nimi pó(cid:283)niej, podczas omawiania obs(cid:239)ugi b(cid:239)(cid:218)dów oraz wyj(cid:200)tków. Litera(cid:239) znakowy l w wywo(cid:239)aniu PyArg_ParseTuple(args, l , n) oznacza, (cid:285)e lista argumentów args powinna zawiera(cid:202) jedynie pojedyncz(cid:200) warto(cid:258)(cid:202) typu long. W przypadku b(cid:239)(cid:218)du wywo(cid:239)anie to zwróci warto(cid:258)(cid:202) NULL i zapisze informacje o wyj(cid:200)tku w odpowiednim globalnym miejscu prze- chowywania informacji o b(cid:239)(cid:218)dach, niezale(cid:285)nym dla ka(cid:285)dego w(cid:200)tku interpretera. Szczegó(cid:239)ami mechanizmu obs(cid:239)ugi wyj(cid:200)tków zajm(cid:218) si(cid:218) w podrozdziale „Obs(cid:239)uga wyj(cid:200)tków”. W(cid:239)a(cid:258)ciwa sygnatura funkcji odpowiedzialnej za analiz(cid:218) argumentów to int PyArg_ParseTuple (cid:180)(PyObject *args, const char *format, ...). Zaraz za argumentem format przyjmuje ona list(cid:218) wska(cid:283)ników dla parametrów wyj(cid:258)ciowych. Lista ta mo(cid:285)e mie(cid:202) zmienn(cid:200) d(cid:239)ugo(cid:258)(cid:202). Mechanizm dzia(cid:239)ania jest analogiczny do funkcji scanf() dost(cid:218)pnej w standardowej bibliotece j(cid:218)zyka C. Je(cid:258)li nasze za(cid:239)o(cid:285)enie oka(cid:285)e si(cid:218) nieprawid(cid:239)owe i u(cid:285)ytkownik przeka(cid:285)e niew(cid:239)a(cid:258)ciw(cid:200) list(cid:218) argumentów, to PyArg_ParseTuple() spowoduje podniesienie odpowiedniego wyj(cid:200)tku. Jest to bardzo komforto- wy sposób opisu sygnatur funkcji, trzeba si(cid:218) tylko do niego przyzwyczai(cid:202). Oczywi(cid:258)cie nie jest to sposób tak wygodny jak ten stosowany bezpo(cid:258)rednio w kodzie Pythona. W dodatku tak zdefinio- wane sygnatury nie mog(cid:200) by(cid:202) analizowane za pomoc(cid:200) typowych narz(cid:218)dzi introspekcji obiektów j(cid:218)zyka dost(cid:218)pnych w Pythonie. Musisz pami(cid:218)ta(cid:202) o tej wadzie, decyduj(cid:200)c si(cid:218) na tworzenie rozsze- rze(cid:241) w j(cid:218)zyku C. Wspomina(cid:239)em ju(cid:285) o tym, (cid:285)e interpreter Pythona wymaga, aby funkcje rozszerze(cid:241) zwraca(cid:239)y obiekty. Oznacza to, (cid:285)e rezultatem wywo(cid:239)ania funkcji fibonacci_py() nie mo(cid:285)e by(cid:202) surowa war- to(cid:258)(cid:202) typu long long otrzymana z funkcji fibonacci(). Próba zapisania kodu w ten sposób spowodowa(cid:239)aby b(cid:239)(cid:200)d kompilacji, poniewa(cid:285) niemo(cid:285)liwe jest automatyczne rzutowanie typów pomi(cid:218)dzy obiektami Pythona a podstawowymi typami j(cid:218)zyka C. Zamiast tego nale(cid:285)y zastosowa(cid:202) funkcj(cid:218) Py_BuildValue(*format, ...), która jest odpowiednikiem funkcji PyArg_ParseTuple() i przyjmuje podobny zbiór argumentów. G(cid:239)ówna ró(cid:285)nica jest taka, (cid:285)e parametry zawarte po argu- mencie format stanowi(cid:200) parametry wej(cid:258)ciowe, a nie wyj(cid:258)ciowe. W zwi(cid:200)zku z tym nale(cid:285)y u(cid:285)y(cid:202) warto(cid:258)ci zmiennych, a nie ich wska(cid:283)ników. Po zdefiniowaniu funkcji fibonacci_py() wi(cid:218)kszo(cid:258)(cid:202) pracy mamy ju(cid:285) za sob(cid:200). Ostatnim krokiem jest przeprowadzenie odpowiedniej inicjalizacji modu(cid:239)u oraz dostarczenie odpowiednich metada- nych sprawiaj(cid:200)cych, (cid:285)e korzystanie z modu(cid:239)u b(cid:218)dzie (cid:239)atwiejsze dla u(cid:285)ytkowników. Jest to sta(cid:239)y element kodu ka(cid:285)dego rozszerzenia i w przypadku prostych modu(cid:239)ów mo(cid:285)e by(cid:202) obj(cid:218)to(cid:258)ciowo wi(cid:218)kszy ni(cid:285) w(cid:239)a(cid:258)ciwy kod, który chcemy udost(cid:218)pni(cid:202) w Pythonie. Zwykle sk(cid:239)ada si(cid:218) z paru statycz- nych struktur danych oraz jednej funkcji inicjalizacyjnej wywo(cid:239)ywanej podczas importu modu(cid:239)u. 242 Poleć książkęKup książkę Rozdzia(cid:225) 7. • Rozszerzenia Pythona w innych j(cid:266)zykach programowania Na pocz(cid:200)tku tworzymy statyczny ci(cid:200)g znaków zawieraj(cid:200)cy docstring funkcji fibonacci_py(): static char fibonacci_docs[] = fibonacci(n): Zwró(cid:202) n-ty wyraz ci(cid:200)gu Fibonacciego wyznaczony rekurencyjnie.\n ; Zwró(cid:202) uwag(cid:218) na to, (cid:285)e powy(cid:285)szy ci(cid:200)g znaków móg(cid:239)by by(cid:202) pó(cid:283)niej osadzony bezpo(cid:258)rednio w zmiennej fibonacci_module_methods. Dobr(cid:200) praktyk(cid:200) jest jednak odseparowanie docstringów od siebie i przechowywanie ich blisko kodu funkcji, które opisuj(cid:200). Nast(cid:218)pn(cid:200) cz(cid:218)(cid:258)ci(cid:200) kodu naszego rozszerzenia jest tablica struktur typu PyMethodDef definiuj(cid:200)cych metody (funkcje) dost(cid:218)pne w module. Struktura PyMethodDef zawiera dok(cid:239)adnie cztery pola: (cid:81) char* ml_name — definiuje nazw(cid:218) funkcji. (cid:81) PyCFunction ml_meth — jest wska(cid:283)nikiem funkcji C. (cid:81) int ml_flags — jest polem bitowym zawieraj(cid:200)cym flagi okre(cid:258)laj(cid:200)ce konwencj(cid:218) wywo(cid:239)ywania lub wi(cid:200)zania funkcji. Konwencje wi(cid:200)zania maj(cid:200) zastosowanie tylko do metod klas. (cid:81) char* ml_doc — jest wska(cid:283)nikiem ci(cid:200)gu znaków dokumentuj(cid:200)cego funkcj(cid:218)/metod(cid:218). Tablica ta zawsze musi si(cid:218) ko(cid:241)czy(cid:202) warto(cid:258)ci(cid:200) stra(cid:285)nicz(cid:200) {NULL, NULL, 0, NULL} wskazuj(cid:200)c(cid:200) na koniec struktury. W naszym przypadku utworzyli(cid:258)my statyczn(cid:200) tablic(cid:218) PyMethodDef fibonacci_ module_methods[] sk(cid:239)adaj(cid:200)c(cid:200) si(cid:218) tylko z dwóch elementów (wliczaj(cid:200)c warto(cid:258)(cid:202) stra(cid:285)nicz(cid:200)): static PyMethodDef fibonacci_module_methods[] = { { fibonacci , (PyCFunction)fibonacci_py, METH_VARARGS, fibonacci_docs}, {NULL, NULL, 0, NULL} }; Poszczególne elementy pierwszego wpisu przek(cid:239)adaj(cid:200) si(cid:218) na pola struktury PyMethodDef w nast(cid:218)- puj(cid:200)cy sposób: (cid:81) ml_name = fibonacci — funkcja C fibonacci_py() b(cid:218)dzie dost(cid:218)pna w module Pythona pod nazw(cid:200) fibonacci. (cid:81) ml_meth = (PyCFunction)fibonacci_py — rzutowanie na wska(cid:283)nik typu PyCFunction jest wymagane przez struktur(cid:218) interfejsu Python/C i wynika z konwencji wywo(cid:239)ywania zdefiniowanej dalej za pomoc(cid:200) pola ml_flags. (cid:81) ml_flags = METH_VARARGS — METH_VARARGS definiuje konwencj(cid:218) wywo(cid:239)ywania pozwalaj(cid:200)c(cid:200) na wywo(cid:239)anie funkcji ze zmienn(cid:200) list(cid:200) argumentów pozycyjnych bez mo(cid:285)liwo(cid:258)ci stosowania argumentów kluczowych. (cid:81) ml_doc = fibonacci_docs — funkcja modu(cid:239)u Pythona zostanie udokumentowana za pomoc(cid:200) ci(cid:200)gu znakowego fibonacci_docs. Po skompletowaniu tablicy funkcji mo(cid:285)emy przyst(cid:200)pi(cid:202) do tworzenia struktury danych definiuj(cid:200)- cej ca(cid:239)y modu(cid:239) Pythona. Do tego celu nale(cid:285)y wykorzysta(cid:202) typ danych PyModuleDef zawieraj(cid:200)cy wiele pól. Cz(cid:218)(cid:258)(cid:202) z nich jest stosowana jedynie w z(cid:239)o(cid:285)onych przypadkach, gdy konieczna jest 243 Poleć książkęKup książkę Profesjonalne programowanie w Pythonie. Poziom ekspert bardzo dok(cid:239)adna kontrola nad procesem inicjalizacji modu(cid:239)u. W naszym przyk(cid:239)adzie b(cid:218)dziemy zainteresowani tylko pi(cid:218)cioma polami tej struktury: (cid:81) PyModuleDef_Base m_base — zawsze powinno by(cid:202) zainicjalizowane warto(cid:258)ci(cid:200) PyModuleDef_HEAD_INIT. (cid:81) char* m_name — okre(cid:258)la nazw(cid:218) modu(cid:239)u udost(cid:218)pnianego w ramach rozszerzenia. (cid:81) char* m_doc — jest to wska(cid:283)nik ci(cid:200)gu znaków dokumentuj(cid:200)cego zawarto(cid:258)(cid:202) modu(cid:239)u. W wi(cid:218)kszo(cid:258)ci przypadków tylko jeden modu(cid:239) Pythona jest implementowany w pojedynczym pliku (cid:283)ród(cid:239)owym rozszerzenia, dlatego zwykle zawarto(cid:258)(cid:202) tego ci(cid:200)gu osadza si(cid:218) bezpo(cid:258)rednio w strukturze. (cid:81) Py_ssize_t m_size — jest to rozmiar przestrzeni pami(cid:218)ci zaalokowanej na potrzeby przechowywania dodatkowego stanu modu(cid:239)u. Przestrze(cid:241) ta potrzebna b(cid:218)dzie jedynie, gdy konieczna jest szczególna obs(cid:239)uga wielu interpreterów lub wykorzystywana jest wielofazowa inicjalizacja modu(cid:239)u. W wi(cid:218)kszo(cid:258)ci przypadków nie b(cid:218)dziesz korzysta(cid:202) z tej funkcjonalno(cid:258)ci i pole to przyjmie warto(cid:258)(cid:202) -1. (cid:81) PyMethodDef* m_methods — jest to wska(cid:283)nik tablicy przechowuj(cid:200)cej opisy funkcji modu(cid:239)u Pythona utworzone z wykorzystaniem typu PyMethodDef. Je(cid:258)li modu(cid:239) nie zawiera (cid:285)adnych funkcji, pole to przyjmuje warto(cid:258)(cid:202) NULL. W naszym przypadku b(cid:218)dzie to tablica fibonacci_module_methods. Opis pozosta(cid:239)ych pól niewymaganych w naszym prostym przyk(cid:239)adzie znajdziesz w oficjalnej dokumentacji Pythona (https://docs.python.org/3/c-api/module.html). Niewymagane pola nale(cid:285)y jawnie zainicjalizowa(cid:202) warto(cid:258)ci(cid:200) NULL lub ca(cid:239)kowicie pomin(cid:200)(cid:202). Opcjonalno(cid:258)(cid:202) pozosta(cid:239)ych pól sprawia, (cid:285)e mo(cid:285)emy pos(cid:239)u(cid:285)y(cid:202) si(cid:218) uproszczon(cid:200) pi(cid:218)cioelementow(cid:200) definicj(cid:200) modu(cid:239)u fibonacci: static struct PyModuleDef fibonacci_module_definition = { PyModuleDef_HEAD_INIT, fibonacci , Modu(cid:239) rozszerzenia zawieraj(cid:200)cy implementacj(cid:218) ci(cid:200)gu Fibonacciego. , -1, fibonacci_module_methods }; Ostatnim elementem wie(cid:241)cz(cid:200)cym nasz(cid:200) prac(cid:218) jest funkcja inicjalizacyjna modu(cid:239)u. Musi ona wykorzystywa(cid:202) (cid:258)ci(cid:258)le okre(cid:258)lon(cid:200) konwencj(cid:218) nazewnicz(cid:200). To dzi(cid:218)ki niej interpreter Pythona b(cid:218)dzie w stanie j(cid:200) znale(cid:283)(cid:202) po za(cid:239)adowaniu dynamicznej/wspó(cid:239)dzielonej biblioteki. Nazwa funkcji ini- cjalizacyjnej powinna mie(cid:202) form(cid:218) PyInit_ nazwa_modu(cid:273)u . Ci(cid:200)g nazwa powinien by(cid:202) do- k(cid:239)adnie tym samym ci(cid:200)giem znaków co ci(cid:200)g u(cid:285)yty jako warto(cid:258)(cid:202) pola m_base struktury PyModuleDef oraz pierwszy argument konstruktora klasy setuptools.Extension. Je(cid:258)li Twój modu(cid:239) nie wymaga skomplikowanej inicjalizacji, funkcja inicjalizacyjna przyjmuje prost(cid:200) i krótk(cid:200) form(cid:218): PyMODINIT_FUNC PyInit_fibonacci(void) { return PyModule_Create( fibonacci_module_definition); } 244 Poleć książkęKup książkę Rozdzia(cid:225) 7. • Rozszerzenia Pythona w innych j(cid:266)zykach programowania PyMODINIT_FUNC jest makrem preprocesora, które ustala PyObject* jako typ wyj(cid:258)ciowy funkcji inicjalizacyjnej oraz zapewnia odpowiednie deklaracje dla linkera, je(cid:258)li takowe s(cid:200) wymagane przez docelow(cid:200) platform(cid:218) systemow(cid:200). Konwencje wywo(cid:239)ywania i wi(cid:200)zania funkcji W podrozdziale „Bli(cid:285)sze spojrzenie na interfejs Python/C” wspomnia(cid:239)em o polu bitowym ml_flags struktury PyMethodDef s(cid:239)u(cid:285)(cid:200)cym do definiowania konwencji wywo(cid:239)ywania i wi(cid:200)zania funkcji. Flagi s(cid:239)u(cid:285)(cid:200)ce do okre(cid:258)lania konwencji wywo(cid:239)ywania to: (cid:81) METH_VARARGS — jest to typowa konwencja wywo(cid:239)ywania dla funkcji Pythona akceptuj(cid:200)cych jedynie pozycyjne argumenty podczas wywo(cid:239)ywania. Warto(cid:258)ci pola ml_meth dla funkcji korzystaj(cid:200)cych z tej konwencji powinny przyjmowa(cid:202) typ PyCFunction. Funkcje C tej konwencji otrzymuj(cid:200) dwa argumenty typu PyObject*: obiekt self (je(cid:258)li funkcja jest metod(cid:200)) lub obiekt modu(cid:239)u (je(cid:258)li funkcja jest funkcj(cid:200) z przestrzeni nazw modu(cid:239)u). Typowa sygnatura dla funkcji tej konwencji to PyObject* function(PyObject* self, PyObject* args). (cid:81) METH_KEYWORDS — jest to konwencja wywo(cid:239)ywania dla funkcji przyjmuj(cid:200)cych argumenty kluczowe podczas wywo(cid:239)ywania. Powi(cid:200)zanym typem dla pola ml_meth jest PyCFunctionWithKeywords. Funkcje C tej konwencji otrzymuj(cid:200) trzy argumenty typu PyObject*: obiekt self, krotk(cid:218) args oraz s(cid:239)ownik argumentów kluczowych. W po(cid:239)(cid:200)czeniu z flag(cid:200) METH_VARARGS pierwsze dwa argumenty maj(cid:200) tak(cid:200) sam(cid:200) semantyk(cid:218) co argumenty funkcji konwencji METH_VARARGS. W przeciwnym razie argument args b(cid:218)dzie przyjmowa(cid:239) warto(cid:258)(cid:202) NULL. Typow(cid:200) sygnatur(cid:200) dla funkcji tej konwencji jest PyObject* function(PyObject* self, PyObject* args, PyObject* keywds) (cid:81) METH_NOARGS — jest to konwencja wywo(cid:239)ywania dla funkcji, które nie przyjmuj(cid:200) (cid:285)adnych argumentów. Powi(cid:200)zanym typem dla pola ml_meth jest PyCFunction, a wi(cid:218)c sygnatura funkcji pozostaje taka sama jak w przypadku konwencji METH_VARARGS (tj. dwa argumenty: self oraz args). Jedyna ró(cid:285)nica polega na tym, (cid:285)e argument args zawsze przyjmuje warto(cid:258)(cid:202) NULL, wi(cid:218)c nie ma potrzeby wywo(cid:239)ywania PyArg_ParseTuple() w ciele funkcji tej konwencji. Flaga METH_NOARGS nie mo(cid:285)e by(cid:202) (cid:239)(cid:200)czona z (cid:285)adnymi innymi flagami konwencji wywo(cid:239)ywania. (cid:81) METH_O — jest to skrócona konwencja dla funkcji i metod przyjmuj(cid:200)cych wy(cid:239)(cid:200)cznie jeden argument. Powi(cid:200)zanym typem dla pola ml_meth jest PyCFunction, a wi(cid:218)c sygnatura funkcji pozostaje taka sama jak w przypadku konwencji METH_VARARGS (tj. dwa argumenty: self oraz args). G(cid:239)ówna ró(cid:285)nica polega na tym, (cid:285)e warto(cid:258)(cid:202) przekazana w argumencie args b(cid:218)dzie pojedynczym obiektem Pythona, a nie krotk(cid:200) argumentów. Dlatego nie nale(cid:285)y korzysta(cid:202) z funkcji PyArg_ParseTuple() w cia(cid:239)ach funkcji tej konwencji. Flaga METH_O równie(cid:285) nie mo(cid:285)e by(cid:202) (cid:239)(cid:200)czona z (cid:285)adnymi innymi flagami konwencji wywo(cid:239)ywania. Funkcje przyjmuj(cid:200)ce argumenty kluczowe s(cid:200) opisywane za pomoc(cid:200) flagi METH_KEYWORDS lub bito- wej kombinacji dwóch flag w postaci METH_VARARGS | METH_KEYWORDS. Funkcje tej konwencji powinny rozpakowywa(cid:202) swoje argumenty z wykorzystaniem PyArg_ParseTupleAndKeywords() 245 Poleć książkęKup książkę Profesjonalne programowanie w Pythonie. Poziom ekspert zamiast funkcji PyArg_ParseTuple() lub PyArg_UnpackTuple(). Poni(cid:285)szy listing zawiera przyk(cid:239)ad prostego modu(cid:239)u rozszerzenia z jedn(cid:200) funkcj(cid:200) zwracaj(cid:200)c(cid:200) warto(cid:258)(cid:202) None. Funkcja ta przyjmuje dwa nazwane argumenty kluczowe i wypisuje je do standardowego wyj(cid:258)cia: #include Python.h static PyObject* print_args(PyObject *self, PyObject *args, PyObject *keywds) { char *first; char *second; static char *kwlist[] = { first , second , NULL}; if (!PyArg_ParseTupleAndKeywords(args, keywds, ss , kwlist, first, second)) return NULL; printf( s s\n , first, second); Py_INCREF(Py_None); return Py_None; } static PyMethodDef module_methods[] = { { print_args , (PyCFunction)print_args, METH_VARARGS | METH_KEYWORDS, Wypisz przekazane argumenty. }, {NULL, NULL, 0, NULL} }; static struct PyModuleDef module_definition = { PyModuleDef_HEAD_INIT, kwargs , Przyk(cid:239)ad przetwarzania argumentów kluczowych. , -1, module_methods }; PyMODINIT_FUNC PyInit_kwargs(void) { return PyModule_C
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

Profesjonalne programowanie w Pythonie. Poziom ekspert. Wydanie II
Autor:
,

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: