PPy3/Moduły: Różnice pomiędzy wersjami
(Utworzono nową stronę "=Moduły i biblioteki= Wspomnieliśmy wcześniej, że problemu raz rozwiązanego nie warto rozwiązywać po raz drugi, trzeci, ... (no chyba, że mamy pomysł na jakie...") |
|||
(Nie pokazano 4 pośrednich wersji utworzonych przez tego samego użytkownika) | |||
Linia 48: | Linia 48: | ||
</source> | </source> | ||
− | Przy tej postaci instrukcji <tt>import</tt>, udostępniona w ramach programu zostaje tylko funkcja <tt>witaj</tt> i należy się do niej odwoływać przez ,,gołą" nazwę. Obiektów importowanych z modułu o nazwie wskazanej po słowie <tt>from</tt> może być więcej, należy podać ich nazwy po słowie <tt>import</tt>, oddzielone przecinkami. | + | Przy tej postaci instrukcji <tt>import</tt>, udostępniona w ramach programu zostaje tylko funkcja <tt>witaj</tt> i należy się do niej odwoływać przez ,,gołą" nazwę. Obiektów importowanych z modułu o nazwie wskazanej po słowie <tt>from</tt> może być więcej, należy podać ich nazwy po słowie <tt>import</tt>, oddzielone przecinkami. |
+ | Można też jeszcze inaczej: | ||
+ | <source lang=python> | ||
+ | #! /usr/bin/python3 | ||
+ | |||
+ | from witaj import * | ||
+ | |||
+ | witaj('Jakubie') | ||
+ | |||
+ | </source> | ||
+ | |||
+ | Taka instrukcja poleca udostępnić ''wszystkie'' nazwy, jakie zostały zdefiniowane w module <tt>witaj</tt>. W tym akurat przypadku jest tylko jedna taka nazwa, nie dzieje się więc nic innego niż w poprzednim przykładzie. Jeżeli jednak moduł <tt>witaj</tt> zostanie w przyszłości wzbogacony o dalsze definicje, to używając instrukcji <tt>import</tt> z gwiazdką jak powyżej, będziemy te definicje importować do swojego programu zupełnie nieświadomie; nie będzie w tym nic złego, dopóki się nie okaże np. że nazwa któregoś z obiektów zdefiniowanych w tym module pokrywa się z jakąś nazwą występującą w bibliotece standardowej, i to nazwą obiektu, z jakiego postanowiliśmy również skorzystać w naszym kodzie - może się wtedy zdarzyć, że będziemy wywoływać wcale nie tę funkcję, o którą nam chodziło... Więcej o możliwych konfliktach nazw powiemy dalej, na dziś warto zapamiętać: '''instrukcja <tt>from ... import *</tt> nie powinna występować w ostatecznej wersji kodu żadnego programu''' (nie ma nic złego w używaniu jej np. przy interakcyjnym testowaniu działania funkcji z danego modułu, itp.). | ||
+ | |||
+ | Instrukcja <tt>import</tt> we wszystkich jej wersjach jest instrukcją Pythona jak każda inna; można ją np. wywoływać warunkowo (wewnątrz instrukcji <tt>if</tt>, czy też wewnątrz definicji funkcji - w takim przypadku, nazwy zaimportowane mają zasięg jedynie lokalny, ograniczony do wnętrza funkcji, podobnie jak zmienne lokalne, wprowadzone wewnątrz definicji funkcji. | ||
+ | |||
+ | ==Biblioteka standardowa== | ||
+ | |||
+ | Domyślna instalacja środowiska Python jest od razu zaopatrzona w dość bogatą kolekcję gotowych modułów, zawierających rozwiązania problemów już dawno rozwiązanych i często przydatne programiście. Biblioteka standardowa jest doskonale udokumentowana - [https://docs.python.org/3/library/index.html wystarczy zajrzeć tutaj], i powielanie tej dokumentacji nie miałoby sensu - przykłady zastosowań zobaczymy wkrótce. | ||
+ | |||
+ | Niekiedy rozwiązanie jakiegoś problemu wymaga sięgnięcia do kodu na ,,niższym poziomie" niż Python, np. w języku C (lub też może być to po prostu opłacalne ze względów wydajnościowych). Biblioteka standardowa zawiera również i takie moduły, obok modułów, których zawartością jest kod w Pythonie. Moduły takie można też tworzyć samodzielnie, ale jest to temat spoza zakresu tego kursu. | ||
+ | |||
+ | Korzystając z modułów biblioteki standardowej warto nieco uważać na możliwość konfliktów nazw; nie należy np. własnemu programowi lub modułowi nadawać nazwy identycznej z nazwą któregoś z modułów standardowych, z którego zamierzamy skorzystać w ramach danego projektu - dla instrukcji <tt>import</tt> moduł własny programisty ma pierwszeństwo, i w efekcie moduł własny ,,przesłoni" standardowy. Ale na wszelki wypadek lepiej jest unikać wszystkich nazw modułów standardowych (a nuż w trakcie rozwoju naszego projektu postanowimy wykorzystać w nim kolejne moduły standardowe?). | ||
+ | |||
+ | ==Funkcja <tt>dir</tt>== | ||
+ | |||
+ | Funkcję <tt>dir</tt> można wykorzystać do zbadania zawartości modułu: | ||
+ | <source lang=python> | ||
+ | In [1]: import witaj | ||
+ | |||
+ | In [2]: dir(witaj) | ||
+ | Out[2]: | ||
+ | ['__builtins__', | ||
+ | '__cached__', | ||
+ | '__doc__', | ||
+ | '__file__', | ||
+ | '__loader__', | ||
+ | '__name__', | ||
+ | '__package__', | ||
+ | '__spec__', | ||
+ | 'witaj'] | ||
+ | </source> | ||
+ | |||
+ | Wynikiem jest lista nazw zdefiniowanych w podanym module. Nazwy otoczone znakami podkreślenia (podwójnymi) mają specjalne znaczenie i powstają ,,automatycznie"; nie powinniśmy sami definiować nazw o zbliżonym kształcie - chyba że świadomie, i wiedząc dokładnie co chcemy tym osiągnąć. | ||
+ | |||
+ | ==Jestem modułem, czy programem?== | ||
+ | |||
+ | Nazwa <tt>__name__</tt> w module jest dość przydatna. Ponieważ każdy plik o nazwie kończącej się na <tt>.py</tt> może pełnić zarówno rolę modułu, jak i programu wykonywalnego (,,głównego"), to dobrze jest, aby w kodzie zawartym w tym pliku można było rozpoznać, w jakiej roli został on uruchomiony. Jeżeli plik wykorzystywany jest w charakterze modułu, tj. poprzez instrukcję <tt>import</tt>, to: | ||
+ | <source lang=python> | ||
+ | In [4]: witaj.__name__ == 'witaj' | ||
+ | Out[4]: True | ||
+ | </source> | ||
+ | |||
+ | czyli pod nazwą <tt>__name__</tt> kryje się napis, będący nazwą modułu. Jeżeli jednak plik został uruchomiony w charakterze programu głównego, to treścią tej nazwy będzie napis <tt>'__main__'</tt>. Jest szeroko przyjętą praktyką umieszczania poleceń, które mają być wykonywane tylko w przypadku, gdy plik zostanie uruchomiony jako program główny, wewnątrz instrukcji <tt>if</tt>: | ||
+ | <source lang=python> | ||
+ | (...) | ||
+ | |||
+ | if __name__ == '__main__': | ||
+ | # blok instrukcji uruchamiających program | ||
+ | |||
+ | </source> | ||
+ | |||
+ | Pozwala to na wykorzystywanie tego samego pliku w obu rolach: programu głównego, i modułu. | ||
+ | |||
+ | ==Paczki modułów== | ||
+ | |||
+ | Moduły mogą mieć strukturę wielopoziomową - wtedy mówimy o paczkach. Inaczej mówiąc, paczka jest obiektem, zawierającym w sobie moduły - lub kolejne paczki, ... itd. aż do poziomu modułów, które już zawierają definicje funkcji i/lub innych obiektów (klas, itp.). | ||
+ | |||
+ | <source lang=python> | ||
+ | |||
+ | # można importować moduły podając pełną "drogę" do nich | ||
+ | |||
+ | import paczka.modul1 | ||
+ | import paczka.modul2 | ||
+ | |||
+ | # to importuje nazwy paczka.modul1, paczka.modul2 | ||
+ | |||
+ | # lub, jeśli wolimy: | ||
+ | |||
+ | from paczka import modul1, modul1 | ||
+ | |||
+ | # w tym przypadku zaimportowaliśmy moduły pod "krótkimi" nazwami: modul1, modul2 | ||
+ | |||
+ | # albo możemy sięgnąć po konkretne funkcje, itp: | ||
+ | |||
+ | from paczka.modul1 import pewna_funkcja | ||
+ | |||
+ | </source> | ||
+ | |||
+ | Struktura paczek zawierających moduły odpowiada strukturze folderów na dysku zawierających odpowiednie pliki <tt>.py</tt>. Szczegóły [https://docs.python.org/3/tutorial/modules.html#packages w dokumentacji]. | ||
=Ćwiczenia= | =Ćwiczenia= |
Aktualna wersja na dzień 11:22, 4 sie 2016
Spis treści
Moduły i biblioteki
Wspomnieliśmy wcześniej, że problemu raz rozwiązanego nie warto rozwiązywać po raz drugi, trzeci, ... (no chyba, że mamy pomysł na jakieś jakościowo nowe rozwiązanie). Zgodnie z tą zasadą, kod stanowiący rozwiązanie opakowuje się w funkcję - z której następnie można korzystać wielokrotnie. Ale na razie wiemy tylko, że z funkcji możemy korzystać w obrębie programu, w którym występuje definicja tejże funkcji. A co z innymi programami? Metoda kopiuj/wklej to nie jest najlepszy pomysł - wyobraźmy sobie chociaż, że w kodzie funkcji wykryliśmy drobny błąd (albo możliwość istotnego ulepszenia); będziemy teraz wyszukiwać wszystkich programów, w których ten kod uprzednio wkleiliśmy, aby je skorygować? Zamiast tego, lepiej jest skorzystać z stworzonego dokładnie w tym celu mechanizmu modułów - plików, w których umieszczamy definicje funkcji (i ewentualnie innych obiektów), z których będzie można korzystać w wielu różnych programach.
Ułatwienie wielokrotnego użytku kodu nie jest jedyną korzyścią z dzielenia kodu programu na funkcje. Warto to robić również, gdy wielokrotnego użytku się nie przewiduje - o ile zadanie do wykonania przez program nie jest trywialne proste, a kod je realizujący bardzo krótki. Wprowadzenie do kodu pewnej struktury wynikającej z podziału różnych elementów algorytmu pomiędzy funkcje sprzyja czytelności programu i ułatwia jego ewentualną modyfikację w przyszłości.
Najprostszy moduł
Jako moduł może służyć dowolny plik zawierający kod w Pythonie, opatrzony nazwą z końcówką .py (tutaj nazwa ma znaczenie). Zazwyczaj w module umieszcza się jedynie definicje (funkcji, klas, ewent. stałych nazwanych), a nie - kod faktycznie wykonywany.
Nazwijmy plik o poniższej treści witaj.py:
#! /usr/bin/python3
def witaj(imie):
print('Witaj {}!'.format(imie))
Wykorzystaliśmy tu znaną już nam metodę format do zgrabnego wstawienia imienia do treści powitania. Ten sam skutek dałoby się osiągnąć stosując operację sklejania napisów:
print('Witaj ' + imie + '!')
użycie formatowania jednak jest zalecane, zwłaszcza w bardziej złożonych przypadkach.
Uruchomienie pliku witaj.py jako programu nie spowoduje żadnego widocznego skutku - funkcja witaj zostanie wprawdzie zdefiniowana, ale program zakończy działanie zanim zostanie ona w ogóle użyta (wywołana). Chcąc wykorzystać tę funkcję w oddzielnym programie, musimy najpierw w kodzie tego programu wskazać, gdzie znajduje się definicja tej funkcji. Do tego służy polecenie import:
#! /usr/bin/python3
import witaj
witaj.witaj('Heleno')
Notacja witaj.witaj w tym przypadku wskazuje, że chodzi o funkcję witaj (słowo po kropce) z modułu o nazwie witaj (to przed kropką). Oczywiście nazwy te równie dobrze mogą brzmieć różnie, a w jednym module można zdefiniować wiele funkcji. Polecenie import witaj nadaje, w ramach tego programu, znaczenie nazwie witaj - będzie ona oznaczać moduł, którego zawartością (elementami) są funkcje (i ew. inne obiekty) zdefiniowane w pliku witaj.py.
Można również inaczej:
#! /usr/bin/python3
from witaj import witaj
witaj('Heleno')
Przy tej postaci instrukcji import, udostępniona w ramach programu zostaje tylko funkcja witaj i należy się do niej odwoływać przez ,,gołą" nazwę. Obiektów importowanych z modułu o nazwie wskazanej po słowie from może być więcej, należy podać ich nazwy po słowie import, oddzielone przecinkami.
Można też jeszcze inaczej:
#! /usr/bin/python3
from witaj import *
witaj('Jakubie')
Taka instrukcja poleca udostępnić wszystkie nazwy, jakie zostały zdefiniowane w module witaj. W tym akurat przypadku jest tylko jedna taka nazwa, nie dzieje się więc nic innego niż w poprzednim przykładzie. Jeżeli jednak moduł witaj zostanie w przyszłości wzbogacony o dalsze definicje, to używając instrukcji import z gwiazdką jak powyżej, będziemy te definicje importować do swojego programu zupełnie nieświadomie; nie będzie w tym nic złego, dopóki się nie okaże np. że nazwa któregoś z obiektów zdefiniowanych w tym module pokrywa się z jakąś nazwą występującą w bibliotece standardowej, i to nazwą obiektu, z jakiego postanowiliśmy również skorzystać w naszym kodzie - może się wtedy zdarzyć, że będziemy wywoływać wcale nie tę funkcję, o którą nam chodziło... Więcej o możliwych konfliktach nazw powiemy dalej, na dziś warto zapamiętać: instrukcja from ... import * nie powinna występować w ostatecznej wersji kodu żadnego programu (nie ma nic złego w używaniu jej np. przy interakcyjnym testowaniu działania funkcji z danego modułu, itp.).
Instrukcja import we wszystkich jej wersjach jest instrukcją Pythona jak każda inna; można ją np. wywoływać warunkowo (wewnątrz instrukcji if, czy też wewnątrz definicji funkcji - w takim przypadku, nazwy zaimportowane mają zasięg jedynie lokalny, ograniczony do wnętrza funkcji, podobnie jak zmienne lokalne, wprowadzone wewnątrz definicji funkcji.
Biblioteka standardowa
Domyślna instalacja środowiska Python jest od razu zaopatrzona w dość bogatą kolekcję gotowych modułów, zawierających rozwiązania problemów już dawno rozwiązanych i często przydatne programiście. Biblioteka standardowa jest doskonale udokumentowana - wystarczy zajrzeć tutaj, i powielanie tej dokumentacji nie miałoby sensu - przykłady zastosowań zobaczymy wkrótce.
Niekiedy rozwiązanie jakiegoś problemu wymaga sięgnięcia do kodu na ,,niższym poziomie" niż Python, np. w języku C (lub też może być to po prostu opłacalne ze względów wydajnościowych). Biblioteka standardowa zawiera również i takie moduły, obok modułów, których zawartością jest kod w Pythonie. Moduły takie można też tworzyć samodzielnie, ale jest to temat spoza zakresu tego kursu.
Korzystając z modułów biblioteki standardowej warto nieco uważać na możliwość konfliktów nazw; nie należy np. własnemu programowi lub modułowi nadawać nazwy identycznej z nazwą któregoś z modułów standardowych, z którego zamierzamy skorzystać w ramach danego projektu - dla instrukcji import moduł własny programisty ma pierwszeństwo, i w efekcie moduł własny ,,przesłoni" standardowy. Ale na wszelki wypadek lepiej jest unikać wszystkich nazw modułów standardowych (a nuż w trakcie rozwoju naszego projektu postanowimy wykorzystać w nim kolejne moduły standardowe?).
Funkcja dir
Funkcję dir można wykorzystać do zbadania zawartości modułu:
In [1]: import witaj
In [2]: dir(witaj)
Out[2]:
['__builtins__',
'__cached__',
'__doc__',
'__file__',
'__loader__',
'__name__',
'__package__',
'__spec__',
'witaj']
Wynikiem jest lista nazw zdefiniowanych w podanym module. Nazwy otoczone znakami podkreślenia (podwójnymi) mają specjalne znaczenie i powstają ,,automatycznie"; nie powinniśmy sami definiować nazw o zbliżonym kształcie - chyba że świadomie, i wiedząc dokładnie co chcemy tym osiągnąć.
Jestem modułem, czy programem?
Nazwa __name__ w module jest dość przydatna. Ponieważ każdy plik o nazwie kończącej się na .py może pełnić zarówno rolę modułu, jak i programu wykonywalnego (,,głównego"), to dobrze jest, aby w kodzie zawartym w tym pliku można było rozpoznać, w jakiej roli został on uruchomiony. Jeżeli plik wykorzystywany jest w charakterze modułu, tj. poprzez instrukcję import, to:
In [4]: witaj.__name__ == 'witaj'
Out[4]: True
czyli pod nazwą __name__ kryje się napis, będący nazwą modułu. Jeżeli jednak plik został uruchomiony w charakterze programu głównego, to treścią tej nazwy będzie napis '__main__'. Jest szeroko przyjętą praktyką umieszczania poleceń, które mają być wykonywane tylko w przypadku, gdy plik zostanie uruchomiony jako program główny, wewnątrz instrukcji if:
(...)
if __name__ == '__main__':
# blok instrukcji uruchamiających program
Pozwala to na wykorzystywanie tego samego pliku w obu rolach: programu głównego, i modułu.
Paczki modułów
Moduły mogą mieć strukturę wielopoziomową - wtedy mówimy o paczkach. Inaczej mówiąc, paczka jest obiektem, zawierającym w sobie moduły - lub kolejne paczki, ... itd. aż do poziomu modułów, które już zawierają definicje funkcji i/lub innych obiektów (klas, itp.).
# można importować moduły podając pełną "drogę" do nich
import paczka.modul1
import paczka.modul2
# to importuje nazwy paczka.modul1, paczka.modul2
# lub, jeśli wolimy:
from paczka import modul1, modul1
# w tym przypadku zaimportowaliśmy moduły pod "krótkimi" nazwami: modul1, modul2
# albo możemy sięgnąć po konkretne funkcje, itp:
from paczka.modul1 import pewna_funkcja
Struktura paczek zawierających moduły odpowiada strukturze folderów na dysku zawierających odpowiednie pliki .py. Szczegóły w dokumentacji.