TI/Moduły

Z Brain-wiki
Wersja z dnia 19:50, 22 maj 2015 autorstwa Jarekz (dyskusja | edycje) (→‎Liczby Fibonacciego)
(różn.) ← poprzednia wersja | przejdź do aktualnej wersji (różn.) | następna wersja → (różn.)

Wprowadzenie

Dowiedziałeś się, jak używać wielokrotnie fragmentu kodu programu dzieląc go na funkcje. A w jaki sposób można używać funkcji zdefiniowanych w innym miejscu? Jak pewnie zgadłeś, odpowiedzią są moduły.

Są różne metody pisania modułów, ale najprościej jest stworzyć plik z rozszerzeniem .py, który będzie zawierał funkcje i zmienne.

Moduły do Pythona można pisać nie tylko w Pythonie. Można, na przykład, napisać moduł do wydajnych obliczeń numerycznych w bardziej wydajnym języku C i po skompilowaniu używać go w swoim pythonowym kodzie.

Aby użyć zawartości modułu, należy go zaimportować. Dotyczy to również modułów ze standardowej biblioteki Pythona, od czego właśnie zaczniemy.

Przykład:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Nazwa pliku: uzycie_sys.py

import sys

print 'Polecenia linii komend to:'
for i in sys.argv:
    print i

print '\n\nPYTHONPATH to:',sys.path,'\n'
Rezultat:
$ python uzycie_sys.py my jestesmy argumentami
Polecenia linii komend to:
uzycie_sys.py
my
jestesmy
argumentami


PYTHONPATH to: ['/home/Swaroop', '/usr/lib/python25.zip', '/usr/lib/python2.5', 
'/usr/lib/python2.5/plat-linux2', '/usr/lib/python2.5/lib-tk', '/usr/lib/python2
.5/lib-dynload', '/usr/local/lib/python2.5/site-packages', '/usr/lib/python2.5/s
ite-packages', '/usr/lib/python2.5/site-packages/Numeric', '/usr/lib/python2.5/s
ite-packages/PIL', '/usr/lib/python2.5/site-packages/gst-0.10', '/var/lib/python
-support/python2.5', '/usr/lib/python2.5/site-packages/gtk-2.0', '/var/lib/pytho
n-support/python2.5/gtk-2.0']

PYTHONPATH to lista katalogów, w których Python poszukuje modułów. W IDLE listę taką można obejrzeć klikając: File->Path Browser.

Jak to działa?

Najpierw importujemy moduł sys używając polecenia import sys, czyli mówimy Pythonowi, że chcemy go używać. Moduł sys zawiera polecenia związane z Pythonem i jego środowiskiem, czyli systemem.

Podczas wykonywania polecenia import sys Python szuka pliku lub katalogu o odpowiedniej nazwie (m.in. zaczynającej się od sys). Aby operacja się powiodła, moduł musi być w jednym z katalogów wymienionych w zmiennej sys.path.

Po znalezieniu modułu treść jego głównego bloku zostaje wykonana, a funkcjonalność dostarczana przez moduł staje się dostępna w programie. Opisana inicjalizacja modułu następuje tylko raz, podczas jego pierwszego importu w danym programie.

Do zmiennych i funkcji zdefiniowanych w module dostajemy się korzystając z notacji z kropką. Na przykład, zmienna argv w module sys jest dostępna jako (sys.argv), dzięki czemu wiadomo, że ta nazwa jest częścią modułu sys. Zaletą tej notacji jest możliwość zdefiniowania zmiennych o tej samej nazwie w różnych modułach bez utraty jednoznaczności. Możemy mieć wiele zmiennych argv w różnych modułach, i jednoznacznie się do niech odwoływać.

Zmienna sys.argv jest listą ciągów znaków. (Listy poznamy dokładniej w następnym rozdziale.) Dokładniej mówiąc, zawiera listę argumentów, z którymi program został wywołany, czyli nazwę pliku zawierającego program oraz to, co użytkownik programu wpisał po nazwie w wywołaniu z wiersza poleceń.


Gdy wpisujemy python uzycie_sys.py my jestesmy argumentami, uruchamiamy moduł uzycie_sys.py za pomocą aplikacji python wraz z argumentami my jestesmy argumentami. Python przechowuje dla nas treść linii komend w zmiennej sys.argv.

Pamiętaj, że nazwa uruchamianego skryptu jest zawsze na początku listy sys.argv. Dlatego w omawianym przykładzie mamy następujące przypisania:

  • uzycie_sys.py jako sys.argv[0],
  • my jako sys.argv[1],
  • jestesmy jako sys.argv[2]
  • argumentami jako sys.argv[3].

Zauważ, że Python numeruje od 0, nie od 1.

sys.path zawiera listę katalogów, z których są importowane moduły. Zobacz, że pierwsza pozycja to folder, w którym został uruchomiony program. Folder, w którym został uruchomiony program zostaje automatycznie wstawiony na początek listy, natomiast pozostałe jej elementy są takie same niezależnie od miejsca wywołania. Oznacza to, że możesz łatwo importować moduły znajdujące się w katalogu bieżącym. Inne moduły są dostępne tylko w wypadku gdy znajdują się w którymś z katalogów wymienionych w ścieżce.

Przy okazji, możesz w każdej chwili podejrzeć katalog, w którym jesteś wpisując w Pythonie print os.getcwd() (oczywiście najpierw musisz zaimportować moduł os).

Pliki pośrednie .pyc

Importowanie modułu to dosyć czasochłonna operacja, więc Python używa pewnych sztuczek, aby ją przyspieszyć. Jedną z nich jest tworzenie plików skompilowanych do kodu bajtowego — pewnej formy pośredniej z rozszerzeniem .pyc. Pliki .pyc są niezależne od platformy. Plik .pyc jest przydatny, gdy masz zamiar zaimportować dany moduł po raz kolejny, używając innego programu - o wiele szybciej, gdyż część pracy potrzebnej do zaimportowania została już wykonana.

Uwaga
Pliki .pyc są zazwyczaj tworzone w tym samym folderze, co odpowiednie pliki .py. Jeżeli Python nie ma pozwolenia na zapis w tym folderze, pliki .pyc nie zostaną utworzone.

Konstrukcja from ... import ...

Jeżeli chcesz bezpośrednio zaimportować zmienną argv do swojego programu (aby nie pisać ciągle sys.), możesz użyć wyrażenia from sys import argv. Jeżeli chcesz zaimportować wszystko, co znajduje się w module sys, możesz użyć wyrażenia from sys import *. To działa z każdym modułem.

Tak naprawdę, powinieneś normalnie używać formy import ..., i odwoływać się do zmiennych z innych modułów przez nazwę ich modułu. Formy from...import... powinieneś używać wtedy, gdy dana nazwa będzie używana bardzo często i kłopotliwe byłoby używanie pełnej ścieżki. Formy z gwiazdką nie powinieneś używać w programach, bo powoduje zaśmiecenie przestrzeni nazw. Niemniej jest ona użyteczna w trybie interaktywnym, gdzie naprawdę nie chcemy pisać długich nazw.

Konstrukcja import ... as ...

Istnieje też możliwość zaimportowania modułu pod inną nazwę. Konstrukcja import nazwa_modulu as inna_nazwa spowoduje zaimportowanie modułu, z tym, że dostęp do jego zmiennych i funkcji będzie się odbywał przez inna_nazwa. . Może to być przydatne jeśli chcemy sobie oszczędzić pisania (na ogół inna_nazwa wybierana jest jako znacznie krótsza niż oryginalna nazwa_modulu)

Samoidentyfikacja modułu

Każdy moduł posiada zmienną zawierającą jego nazwę (zazwyczaj). Najczęściej używa się tej zmiennej wtedy, gdy chcemy się dowiedzieć, czy moduł został zaimportowany, czy uruchomiony jako program. Jak już wcześniej wspomniano, gdy moduł zostaje zaimportowany po raz pierwszy, jego kod zostaje wykonany. W przypadku definicji funkcji i klas ich wykonanie oznacza po prostu zdefiniowanie tych funkcji i klas. Polecenia zapisane w treści funkcji nie zostają wykonane w momencie definicji funkcji, lecz dopiero później, w momencie wywołania funkcji. Podobnie jest dla klas, czyli definicja klasy nie powoduje stworzenia obiektu. Natomiast wyrażenia znajdujące się poza definicjami funkcji i klasy zostają wykonane od razu. Często chcemy, by zostałe one wykonane tylko wtedy, gdy uruchamiamy moduł jako program.

Zmienna __name__ zawiera nazwę modułu. Wyjątkiem jest sytuacja gdy został on uruchomiony samodzielnie, jako program. Wówczas zawiera napis '__main__'. Dzięki temu możemy rozróżnić dwa sposoby wywołania modułu i podjąć odpowiednie decyzje.

Przykład:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Nazwa pliku: nazwa.py

if __name__ == '__main__':
    print 'Ten program jest uruchomiony samodzielnie.'
else:
    print 'Zostałem zaimportowany z innego modułu.'
Rezultat:
$ python nazwa.py
Ten program jest uruchomiony samodzielnie.

$ python
>>> import nazwa
Zostałem zaimportowany z innego modułu.
>>>

Jak to działa?

Każdy moduł Pythona ma zdefiniowaną własną nazwę. Jeżeli jest nią '__main__', oznacza to, że moduł działa samodzielnie, a wtedy możemy podjąć odpowiednie działania.

Tworzenie własnych modułów

Tworzenie własnych modułów jest proste, robisz to cały czas! A to dlatego, że każdy program w Pythonie jest także modułem. Ty musisz tylko zadbać, żeby miał rozszerzenie .py. Ten przykład powinien wszystko wyjaśnić.

Przykład:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Nazwa pliku: mojmodul.py

def mowczesc():
    print 'Cześć, tu mówi mojmodul.'

__version__ = '0.1'

# Koniec modułu mojmodul.py.

Oto przykładowy moduł. Jak widać, nie ma tu nic szczególnie różniącego go od zwykłego programu w Pythonie. Następnie zobaczymy, jak go użyć w innych naszych programach.

Pamiętaj, że moduł powinien być umieszczony w tym samym katalogu co program, który z niego korzysta, lub też w jednym z katalogów wpisanych w sys.path.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Nazwa pliku: mojmodul_demo.py

import mojmodul

mojmodul.mowczesc()
print 'Wersja', mojmodul.__version__

Rezultat:

$ python mojmodul_demo.py
Cześć, tu mówi mojmodul.
Wersja 0.1

Jak to działa?

Zauważ, że używamy tego samego zapisu z kropkami przy uzyskiwaniu dostępu do elementów modułu. Python robi dobry użytek z tej samej notacji nadając temu swoisty „pythonowy” styl, dzięki czemu nie musimy wciąż poznawać coraz to nowych metod pracy.

Oto wersja z użyciem from...import...:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Nazwa pliku: mojmodul_demo2.py

from mojmodul import mowczesc, __version__

mowczesc()
print 'Wersja', __version__

Rezultat mojmodul_demo2.py jest taki sam jak mojmodul_demo.py.

Ta forma jest przykładem bardzo złego stylu. Zauważ, że jeżeli nazwa __version__ już istniała wcześniej w module, który importuje mojmodul, powstanie konflikt nazw. Na dodatek jest to bardzo prawdopodobne, ponieważ zmienna o nazwie __version__ jest standardowo używana po przechowywania wersji modułu. W momencie kiedy plik mojmodul_demo zostanie wydłużony, łatwo o pomyłkę, bo normalnie __version__ to wersja bieżącego modułu, a tutaj to wersja modułu mojmodul. Stąd zawsze lepiej użyć wyrażenia import w taki sposób, by ograniczyć liczbę importowanych nazw.

Możesz także użyć:

from mojmodul import *

To spowoduje zaimportowanie prawie wszystkich nazw, jak na przykład mowczesc, ale ominie __version__, gdyż zaczyna się ona od podwójnego podkreślenia. Ta wersja jest jeszcze brzydsza.

Funkcja dir

Możesz użyć wbudowanej funkcji dir, aby wypisać nazwy zdefiniowane w pewnym obiekcie. Na przykład w module takie nazwy wskazują na funkcje, klasy i zmienne w nim zadeklarowane.

Gdy dir() zostaje wywołane z argumentem, to działa na nim. W wypadku wywołania dir() bez argumentu, działa ono na przestrzeni nazw, z której została wywołana.

Przykład:

$ python

>>> import sys # Zdobądź listę atrybutów, w tym wypadku dla modułu sys.
>>> dir(sys)
['__displayhook__', '__doc__', '__excepthook__', '__name__', '__package__', '__s
tderr__', '__stdin__', '__stdout__', '_clear_type_cache', '_current_frames', '_g
etframe', 'api_version', 'argv', 'builtin_module_names', 'byteorder', 'call_trac
ing', 'callstats', 'copyright', 'displayhook', 'dont_write_bytecode', 'exc_clear
', 'exc_info', 'exc_type', 'excepthook', 'exec_prefix', 'executable', 'exit', 'f
lags', 'float_info', 'getcheckinterval', 'getdefaultencoding', 'getdlopenflags',
 'getfilesystemencoding', 'getprofile', 'getrecursionlimit', 'getrefcount', 'get
sizeof', 'gettrace', 'hexversion', 'maxint', 'maxsize', 'maxunicode', 'meta_path
', 'modules', 'path', 'path_hooks', 'path_importer_cache', 'platform', 'prefix',
 'ps1', 'ps2', 'py3kwarning', 'pydebug', 'setcheckinterval', 'setdlopenflags', '
setprofile', 'setrecursionlimit', 'settrace', 'stderr', 'stdin', 'stdout', 'subv
ersion', 'version', 'version_info', 'warnoptions']
>>> dir() # Zdobądź listę atrybutów dla aktualnego modułu.
['__builtins__', '__doc__', '__name__', '__package__', 'sys']
>>> a = 5 # Stwórz nową zmienną "a".
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'a', 'sys']
>>> del a # Usuń nazwę.
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'sys']
>>>

Jak to działa?

Na początku sprawdzamy działanie dir na zaimportowanym module sys. Widać długą listę jego atrybutów.

Następnie używamy funkcji dir bez parametrów. Domyślnie zwraca ona listę atrybutów aktualnego modułu. Zauważ, że lista zaimportowanych modułów jest też częścią wyniku.

W celu ujrzenia dir w akcji, deklarujmy nową zmienną a, przypisujemy jej wartość, a następnie sprawdzamy, że na liście pojawiła nazwa naszej nowej zmiennej. Usuwamy ją poleceniem del, czego efekt widać po kolejnym użyciu dir.

Uwaga do del — to polecenie usuwa zmienną/nazwę (w tym wypadku del a), w efekcie później nie da się odnieść do tej nazwy, tak jakby nigdy wcześniej nie istniała.

Pamiętaj, że funkcja dir() działa z każdym obiektem. Na przykład możesz napisać dir(max), aby poznać atrybuty funkcji max, albo dir(str), aby poznać atrybuty klasy str.

Paczki

Właśnie zacząłeś dogłębnie poznawać hierarchię elementów twoich programów. Zmienne zazwyczaj znajdują się w funkcjach. Funkcje oraz zmienne globalne — w modułach. A co gdy chcesz zarządzać modułami? W tym miejscu na scenę wkraczają paczki.

Paczki to katalogi z modułami oraz ze specjalnym plikiem __init__.py, który informuje Pythona, że ten katalog jest specjalnie przeznaczony właśnie do przechowywania modułów.

Powiedzmy, że chcesz stworzyć paczkę o nazwie swiat zawierającą paczki azja, afryka itd., zaś w nich na przykład indie czy madagaskar.

Oto, jak powinna wyglądać twoja struktura katalogów:

 jakiś katalog wymieniony w sys.path
 └── swiat/
     ├── __init__.py
     ├── azja/
     │   ├── __init__.py
     │   ├── indie.py
     │   └── chiny.py
     └── afryka/
         ├── __init__.py
         └── madagaskar.py

Paczki są wygodnym sposobem segregacji modułów. Zobaczysz wiele przykładów ich użycia w [[../Biblioteka standardowa/|bibliotece standardowej]].

Podsumowanie

Tak jak funkcje są częściami programu wielokrotnego użytku, tak moduły to programy wielokrotnego użytku. Paczki są odrębną hierarchią organizacji modułów. Standardowa biblioteka Pythona jest przykładem zestawu paczek i modułów.

Zobaczyliśmy jak użyć tych modułów i utworzyć swoje własne.

Następnie poznamy pewne interesujące koncepty zwane strukturami danych.

Zadania

Liczby Fibonacciego

Stwórz moduł fib3 zawierający trzy funkcje zwracające [math]n[/math]-tą liczbę Fibonacciego — obliczoną rekurencyjnie z definicji, w pętli, oraz ze wzoru Bineta. Sam moduł oraz funkcje muszą mieć docstringi.

Moduł ma też zawierać bezargumentową funkcję test, której zadaniem jest sprawdzenie poprawności działania wszystkich trzech funkcji i wypisanie odpowiedniego komunikatu. Test powinien być wykonany dla pewnych ustalonych wartości, w szczególności tych granicznych, czyli 0, 1, 2 i innych. Przydatna może być ich lista na http://pl.wikisource.org/wiki/Ciąg_Fibonacciego.

Następnie napisz moduł fib3-czas, który wykorzystuje moduł fib3 i moduł time aby porównać szybkość wykonywania wszystkich trzech algorytmów. Wynik powienien zostać wypisany w postaci tabelki

 n    T1/ms    T2/ms    T3/ms
 1     0.5     0.3      0.7
 2     0.5     0.5      1.0
 ...   ...     ...      ...