PPy3/Moduły

Z Brain-wiki

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.

Ćwiczenia


poprzednie | strona główna | dalej

RobertJB (dyskusja) 14:51, 14 lip 2016 (CEST)