Biblioteka standardowa¶
Operacje plikowe¶
Otwieranie plików¶
- open(path[, mode[, buffersize]])¶
otwieranie pliku zarówno do odczytu, jak i zapisywania
path
– łańcuch znaków określający ścieżkę, wskazujący na dany plikmode
– tryb otwarcia plikubuffersize
– argument opcjonalny, określa, jaki tryb buforowania powinien być stosowany, kiedy uzyskuje się dostęp do pliku
file = open(inPath, 'rU')
file = open(outPath, 'wb')
Tryby plików¶
r
Otwiera istniejący plik do odczytu
w
Otwiera plik do zapisu. Jeśli plik już istnieje, jego zawartość jest kasowana. Jeśli plik jeszcze nie istnieje, tworzony jest nowy.
a
Otwiera istniejący plik do uaktualnienia, zachowując istniejącą część bez zmian i dodając do niej nową treść.
r+
Otwiera plik zarówno do odczytu, jak i zapisu. Istniejąca treść pozostaje bez zmian.
w+
Otwiera plik zarówno do zapisu, jak i odczytu. Istniejąca treść jest kasowana.
a+
Otwiera plik zarówno do odczytu, jak i zapisu. Istniejąca treść pozostaje bez zmian, a nowa treść jest dodawana do istniejącej.
b
Stosowany w dodatku do jednego z ww. trybów odczytu, zapisu lub dodawania. Otwiera plik w trybie binarnym.
Tryb buforowania¶
0
Plik nie powinien być buforowany
1
Buforowanie wierszy (line-buffering)
- inna liczba dodatnia
Wskazuje konkretny rozmiar bufora, który ma być wykorzystywany w dostępie
- brak argumentu lub liczba ujemna
Wykorzystywany jest domyślny rozmiar bufora systemu
Otwieranie i zamykanie plików¶
- close()¶
metoda zamykająca plik
Przykład:
inPath = "input.txt"
outPath = "output.txt"
# Otwarcie pliku do odczytu
file = open(inPath, 'r')
if file:
# Tutaj następuje odczytywanie zawartości pliku
file.close()
else:
print("Przy otwieraniu pliku nastąpił błąd.")
# Otwarcie pliku do zapisu
file = open(outPath, 'wb')
if file:
# Tutaj następuje zapisywanie czegoś do pliku
file.close()
else:
print("Przy otwieraniu pliku nastąpił błąd.")
Od Pythona w wersji 2.5 można używać wyrażenia with
, tzw. menedżera kontekstu.
Gwarantuje on poprawne zamknięcie pliku nawet w sytuacji wystąpienia wyjątku.
Przykład:
with open("input.txt", 'r') as f:
# Operacje na obiekcie pliku
f.read()
# zamknięcie pliku
Menedżer kontekstu odpowiada następującej obsłudze wyjątków:
try:
f = open("input.txt", 'r')
f.read()
finally:
f.close()
Wyrażenie with
upraszcza pisanie kodu odpornego na wyjątki.
Odczyt¶
Odczytywanie całej zawartości pliku¶
- read()¶
wczytuje pełną zawartość pliku aż do napotkania znacznika EOF, a następnie zwraca zawartość pliku w formie łańcucha znaków.
- readlines()¶
wczytuje całą zawartość pliku, oddzielając każdy wiersz jako osobny łańcuch znaków, aż do napotkania znacznika EOF i zwraca listę łańcuchów znaków reprezentujących pojedyncze wiersze.
- read(bytes)¶
wczytuje określoną liczbę bajtów z pliku i zwraca je w postaci łańcucha znaków. Jeśli pierwszy odczytany znak jest znacznikiem EOF, zwracany jest null.
Przykład:
filePath = "input.txt"
# Wczytanie całego pliku do bufora
buffer = "Bufor całego pliku:\n"
buffer += open(filePath, 'r').read()
print(buffer)
# Wczytanie wierszy do bufora
buffer = "Bufor wszystkich wierszy z pliku:\n"
lines = open(filePath, 'r').readlines()
print(lines)
for line in lines:
buffer += line
print(buffer)
# Wczytanie bajtów do bufora
buffer = "Bufor odczytu:\n"
file = open(filePath, 'r')
while True:
bytes = file.read(5)
if bytes:
buffer += bytes
else:
break
print(buffer)
Bufor całego pliku:
Wiersz 1
Wiersz 2
Wiersz 3
Wiersz 4
['Wiersz 1\n', 'Wiersz 2\n', 'Wiersz 3\n', 'Wiersz 4\n']
Bufor wszystkich wierszy z pliku:
Wiersz 1
Wiersz 2
Wiersz 3
Wiersz 4
Bufor odczytu:
Wiersz 1
Wiersz 2
Wiersz 3
Wiersz 4
Dostęp do każdego słowa w pliku¶
- Słowa mogą być przetwarzane pojedynczo poprzez:
otwarcie pliku
wczytanie każdego wiersza do łańcucha znaków
podzielenie łańcuchów na znaki za pomocą funkcji
split()
Przykład:
filePath = "input.txt"
wordList = []
wordCount = 0
# Wczytanie wierszy do pliku
file = open(filePath, 'r')
for line in file:
for word in line.split():
wordList.append(word)
wordCount += 1
print(wordList)
print("Całkowita liczba słów {}".format(wordCount))
['Wiersz', '1', 'Wiersz', '2', 'Wiersz', '3', 'Wiersz', '4']
Całkowita liczba słów 8
Ustalenie liczby wierszy w pliku¶
- readlines()¶
generowanie listy wierszy. Dla dużych plików używanie readlines() może być niepraktyczne ze względu na ilość pamięci i czas niezbędny do przetwarzania.
- len()
ustalenie liczby wierszy w liście.
Przykład:
filePath = "input.txt"
lineCount = len(open(filePath, 'r').readlines())
print("Liczba wierszy w {} wynosi {}".format(filePath, lineCount))
Liczba wierszy w pliku input.txt wynosi 4
Zapisywanie do pliku¶
Metody zapisywania danych do pliku:
- write(string)¶
zapisuje argument string do pliku w miejscu, w którym aktualnie znajduje się wskaźnik pliku
- writelines(sequence)¶
funkcja zazwyczaj przyjmuje listę łańcuchów znaków sequence i zapisuje te łańcuchy do pliku
Można również przekierować funkcję print
do pliku za pomocą argumentu file
Przykład:
wordList = ["Czerwony", "Niebieski", "Zielony"]
filePath = "output.txt"
# Zapisanie listy do pliku
fileOut = open(filePath, 'w')
fileOut.writelines(wordList)
# Zapisanie łańcucha znaków do pliku
fileOut.write("\n\nSformatowany tekst:\n")
# Zapisanie listy do pliku
fileOut = open(filePath, 'w')
fileOut.writelines(wordList)
# Zapisanie łańcucha znaków do pliku
fileOut.write("\n\nSformatowany tekst:\n")
# Drukowanie bezpośrednio do pliku
for word in wordList:
print("\tKontrola koloru: {}".format(word), file=fileOut)
fileOut.close()
CzerwonyNiebieskiZielony
Sformatowany tekst:
Kontrola koloru: Czerwony
Kontrola koloru: Niebieski
Kontrola koloru: Zielony
Przechodzenie drzewa katalogów¶
- os.walk(path)¶
przechodzi drzewo katalogów i tworzy dla każdego z nich krotkę składającą się ze: ścieżki katalogu, listy nazw katalogów oraz listy nazw plików.
Po utworzeniu krotek mogą być one pojedynczo przetwarzane jako elementy listy z użyciem indeksów
0 – dostęp do reprezentowanej bezpośrednio ścieżki katalogu
1 – dostęp do listy podkatalogów
2 – dostęp do plików zawartych w katalogu
Przykład:
import os
path = "/books/python"
def printFiles(dirList, spaceCount):
for file in dirList:
print("/".rjust(spaceCount+1) + file)
def printDirectory(dirEntry):
print(dirEntry[0] + "/")
printFiles(dirEntry[2], len(dirEntry[0]))
tree = os.walk(path)
for directory in tree:
printDirectory(directory)
/books/python/
/Python Proposal.doc
/Python_Phrasebook_TOC.doc
/python_schedule.xls
/template.doc
/TOC_Notes.doc
/books/python/CH2/
/CH2.doc
/books/python/CH2/code/
/comp_str.py
/end_str.py
/eval_str.py
/format_str.py
/join_str.py
/books/python/CH3/
/CH3.doc
Zmiana nazwy pliku¶
- os.remove(file)¶
wykrycie, czy plik o danej nazwie już istnieje, a następnie ewentualne usunięcie
- os.rename(oldFile, newFile)¶
zmiana nazwy pliku
os.remove(newFileName)
os.rename(oldFileName, newFileName)
Przykład:
import os
oldFileName = "/books/python/CH4/code/output.txt"
newFileName = "/books/python/CH4/code/output.old"
# Nowy listing
for filename in os.listdir("/books/python/CH4/code/"):
if filename.startswith("output"):
print(filename)
# Usunięcie pliku, jeśli nowa nazwa już istnieje
if os.access(newFileName, os.X_OK):
print("Usuwanie " + newFileName)
os.remove(newFileName)
# Zmiana nazwy pliku
os.rename(oldFileName, newFileName)
# Stary listing
for filename in os.listdir("/books/python/CH4/code/"):
if filename.startswith("output"):
print(filename)
output.old
output.txt
Usuwanie /books/python/CH4/code/output.old
output.old
Rekurencyjne kasowanie plików i podkatalogów¶
Funkcja os.walk(path)
automatycznie tworzy listę krotek, reprezentujących katalogi, które mają być usunięte.
Aby rekurencyjnie skasować drzewo, należy przejść przez listę katalogów i usunąć każdy plik zawarty w liście plików (trzeci element krotki).
Najpierw należy usunąć pliki, a następnie same katalogi w odwrotnej kolejności, rozpoczynając od najgłębiej zagnieżdżonych.
Przykład:
import os
emptyDirs = []
path = "/trash/deleted_files"
def deleteFiles(dirList, dirPath):
for filename in dirList:
print("Usuwanie " + filename)
os.remove(dirPath + "/" + filename)
def removeDirectory(dirEntry):
print("Usuwanie plików z " + dirEntry[0])
deleteFiles(dirEntry[2], dirEntry[0])
emptyDirs.insert(0, dirEntry[0])
# Sporządzenie listy wpisów w drzewie katalogów
tree = os.walk(path)
for directory in tree:
removeDirectory(directory)
# Usunięcie pustych katalogów
for dir in emptyDirs:
print("Usuwanie " + dir)
os.rmdir(dir)
Usuwanie plików z /trash/deleted_files
Usuwanie 102.ini
Usuwanie 103.ini
Usuwanie 104.ini
Usuwanie 105.ini
Usuwanie 106.ini
Usuwanie plików z /trash/deleted_files\Test
Usuwanie 111.ini
Usuwanie 114.ini
Usuwanie 115.ini
Usuwanie plików z /trash/deleted_files\Test\Test2
Usuwanie 112.ini
Usuwanie 113.ini
Usuwanie plików z /trash/deleted_files\Test\Test2
Usuwanie plików z /trash/deleted_files\Test
Usuwanie plików z /trash/deleted_files
Wyszukiwanie plików w oparciu o rozszerzenie¶
Tworzymy listę rozszerzeń plików poprzez podzielenie łańcucha znaków wzorca za pomocą funkcji split()
.
Sprawdzamy, czy rozszerzenie pliku odpowiada rozszerzeniu znajdującemu się na liście używając funkcji endswith(string)
na nazwie pliku.
Przykład:
import os
path = "/books/python"
pattern = "*.py;*.doc"
# Wydrukowanie plików, które odpowiadają wybranym rozszerzeniom
def printFiles(dirList, spaceCount, extension):
for file in dirList:
for ext in extension:
if file.endswith(ext):
print("/".rjust(spaceCount+1) + file)
break
# Wydrukowanie każdego podkatalogu
def printDirectory(dirEntry, extension):
print(dirEntry[0] + "/")
printFiles(dirEntry[2], len(dirEntry[0]), extension)
# Konwersja łańcucha znaków wzorca na listę rozszerzeń plików
extensions = []
for extension in pattern.split(";"):
extensions.append(extension.lstrip("*"))
# Przejście drzewa katalogów w celu wydrukowania plików
for directory in os.walk(path):
printDirectory(directory, extensions)
/books/python/
/Python Proposal.doc
/Python_Phrasebook_TOC.doc
/python_schedule.xls
/template.doc
/TOC_Notes.doc
/books/python/CH2/
/CH2.doc
/books/python/CH2/code/
/comp_str.py
/end_str.py
/eval_str.py
/format_str.py
/join_str.py
/books/python/CH3/
/CH3.doc
Współpraca z systemem operacyjnym¶
Moduł os
¶
Moduł os
udostępnia przenośny, niezależny od platformy interfejs dla dostępu do usług operacyjnych.
Pozwala to na dodanie do programów obsługi na poziomie systemu operacyjnego.
- os.path.abspath(path)¶
zwraca bezwzględną ścieżkę w formie łańcucha znaków.
>>> import os
>>> print(os.path.abspath("."))
/home/my_user/code/Python
>>> print(os.path.abspath(".."))
/home/my_user/code
- os.exists(path)¶
- os.isdir(path)¶
- os.isfile(path)¶
funkcje sprawdzają istnienie plików i katalogów.
>>> os.path.exists("/home/InfoTraining")
True
>>> os.path.isdir("/home/InfoTrainingSzkolenia")
True
>>> os.path.isfile("/home/InfoTrainingSzkolenia/doc1.txt")
True
- os.chdir(path)¶
umożliwia zmianę bieżącego katalogu roboczego dla programu.
>>> os.chdir("/home/user/books/python/ch1/code")
>>> os.path.abspath(".")
/home/user/books/python/ch1/code
os.environ
słownik zmiennych środowiskowych.
>>> os.environ['PATH']
'/usr/sbin:/sbin:/usr/local/bin:/usr/bin'
Moduł subprocess
¶
- subprocess.Popen(command, stdin=None, stdout=None)¶
wykona funkcję systemową
command
tak, jakby znajdowała się ona w podpowłoce.
import subprocess
popen = subprocess.Popen("make",stdout=subprocess.PIPE)
result = popen.communicate()
print(result)
Moduł sys
¶
Moduł sys
udostępnia interfejs dostępu do środowiska interpretera Pythona.
>>> import sys
>>> sys.argv
['/home/my_user/code/print_it.py', 'text']
>>> sys.argv[1]
'text'
Atrybut argv
modułu sys
jest listą.
Pierwszy element na liście argv
jest ścieżką do modułu.
Reszta listy składa się z argumentów, które zostały przekazane do modułu na początku wykonania.
Atrybut stdin
modułu sys
jest obiektem pliku, który zostaje utworzony na początku wykonywania kodu.
>>> text = sys.stdin.readline()
Dane wejściowe
>>> print(text)
Dane wejściowe
Atrybuty stdout
i stderr
wskazują na pliki używane jako standardowe wyjście oraz standardowe wyjście błędów.
Pliki te domyślnie są związane z ekranem.
>>> old_stdout = sys.stdout
>>> old_stderr = sys.stderr
>>> sys.stdout = open("output.txt", "w")
>>> sys.stderr = sys.stdout
>>> sys.stdout = old_stdout
>>> sys.stderr = old_stderr
Moduł platform
¶
Moduł platform
udostępnia przenośny interfejs do informacji o platformie, na której uruchamiany jest program.
- platform.architecture()¶
zwraca krotkę (bits, linkage)
bits – liczba bitów wielkości słowa w systemie
linkage – powiązane informacje o pliku wykonywalnym Pythona
>>> import platform
>>> platform.architecture()
('64bit', 'ELF')
- platform.python_version()¶
zwraca wersję pliku wykonywalnego Pythona dla celów zgodności.
>>> platform.python_version()
'3.3.2'
- platform.uname()¶
zwraca obiekt zawierający informację w formie (system, node, release, version, machine, processor)
system – aktualnie działający system operacyjny
node – nazwa hosta danej maszyny
release – główna wersja systemu operacyjnego
version – informacja o wydaniu systemu operacyjnego w formie łańcucha znaków
machine, processor – informacje sprzętowe danej platformy
>>> platform.uname()
uname_result(system='Linux', node='tpad', release='3.2.0-4-amd64',
version='#1 SMP Debian 3.2.46-1+deb7u1', machine='x86_64', processor='')
Moduł time
¶
Moduł time
udostępnia przenośny interfejs do funkcji związanych z czasem w systemie, na którym program jest wykonywany.
time.time()
Zwraca bieżący czas systemowy jako liczbę sekund, które upłynęły od 1.01.1970 zgodnie z UTC (Coordinated Universal Time)
time.localtime(secs)
Zwraca czas w sekundach od 1.01.1970 w formie krotki (year, month, day, hour, second, day of week, day of year, daylight savings)
time.ctime(secs)
Zwraca czas, określony w sekundach od 1.01.1970, w formie sformatowanego łańcucha znaków, nadającego się do wydruku
time.clock()
Zwraca aktualny czas procesora w postaci liczby zmiennoprzecinkowej, która może być używana w funkcjach mierzących czas
time.sleep(secs)
Zmusza bieżący proces do uśpienia przez liczbę sekund określoną przez liczbę zmiennoprzecinkową secs
Utrwalanie stanu: moduł pickle¶
Serializacja i trwałość obiektów może zostać zrealizowana przy pomocy wbudowanych narzędzi Pythona.
Moduły pickle
i cPickle
serializują obiekty do i z plików.
import pickle
pickler = pickle.Pickler(file) # file to otwarty obiekt pliku
pickler.dump(obj) # Zrzut obiektu
Aby odserializować obiekt, używamy funkcji unpickle.
unpickler = pickle.Unpickler(file) # file to otwarty obiekt pliku
obj = unpickler.load() # Ładowanie obiektu
Serializacja - wskazówki¶
Większość typów wbudowanych można poddać serializacji.
Można dokonać serializacji obiektu klasy. Klasa musi być dostępna w momencie odtwarzania obiektu. Sam kod klasy nie jest serializowany. Python automatycznie importuje moduł zawierający odtwarzaną klasę.
Pewne typy obiektów nie mogą być serializowane np. sockety, pliki.
Dowolny obiekt zapewniający metody write(), read() i readline() może być używany jako plik.
Obiekty rekursywne można poddać serializacji.
Dane po serializacji można przenosić między różnymi systemami operacyjnymi i architekturami sprzętowymi.
Serializacja obiektów zastosowana w Pythonie nie jest dostępna z poziomu programów napisanych w innych językach programowania.
Wyrażenia regularne¶
Wyrażenie regularne (regex
) definiuje prosty analizator, który sprawdza dopasowanie tekstu do danego wzorca.
- Powody używania wyrażeń regularnych:
Przetwarzanie: identyfikacja i wyodrębniane fragmentów tekstu, które spełniają określone kryteria. Wyrażenia regularne są używane w celu tworzenia doraźnych analizatorów składni oraz przez zwykłe narzędzia analizatorów składni.
Wyszukiwanie: odnajdywanie łańcuchów znaków, które mogą mieć więcej niż jedną formę, na przykład znalezienie dowolnego z plików auto.png, auto.jpg lub auto.svg, ale unikanie pliku autobus.png.
Wyszukiwanie i zastępowanie: zastępowanie każdego łańcucha znaków dopasowanego przez wyrażenie regularne, na przykład wyszukanie „motor” lub „pojazd jednośladowy” i zastąpienie słowem „motocykl”.
Dzielenie łańcuchów znaków: podzielenie łańcucha znaków w każdym miejscu dopasowanym przez wyrażenie regularne, na przykład podział w każdym miejscu występowania dwukropka lub znaku równości.
Sprawdzanie poprawności: weryfikacja, czy dany fragment tekstu spełnia pewne kryteria, na przykład zawiera cyfry, a po nich symbol waluty.
foo.* # Dopasowuje string rozpoczynający od foo
\d* # Dopasowuje dowolny ciąg znaków złożony z cyfr
[a-zA-Z]+ # Dopasowuje sekwencję jednej lub więcej liter
Moduł re¶
Moduł re
umożliwia korzystanie z wyrażeń regularnych w Pythonie.
Znaki specjalne¶
Większość znaków może być używana jako literały.
Istnieją jednak znaki specjalne, które muszą być poprzedzone ukośnikiem (), aby można ich było używać jako literałów.
Wspomniane znaki specjalne to \ . ^ $ ? + * {} [] () |
.
Klasy znaków¶
Jeśli zamiast jednego określonego znaku chcemy dopasować jeden z zestawów znaków, używamy klasy znaków. Klasa znaków to jeden lub większa liczba znaków ujętych w nawiasy kwadratowe. Klasa znaków jest wyrażeniem, więc jeśli nie jest wyraźnie podany kwantyfikator, to zostanie dopasowany dokładnie jeden znak (dowolny ze znaków zdefiniowanych w klasie znaków).
Dla często używanych klas znaków dostępne są formy skrótowe.
|
Dopasowuje dowolny znak oprócz znaku nowej linii |
|
Dopasowuje dowolną cyfrę |
|
Dopasowuje dowolny znak nie będący cyfrą |
|
Dopasowuje dowolny biały znak |
|
Dopasowuje dowolny znak nie będący białym znakiem |
|
Dopasowuje dowolny znak alfanumeryczny |
|
Dopasowuje znaki nie będące znakami alfanumerycznymi |
Kwantyfikatory wyrażeń regularnych¶
Kwantyfikatory służą do określania powtórzeń znaków lub sekwencji we wzorcach.
Domyślnie wyrażenia regularne realizują tzw. zachłanne dopasowanie. Każdy podciąg we wzorcu wyrażenia regularnego jest dopasowywany do największej możliwej liczby znaków w ciągu. Dopiero potem następuje przejście do kolejnej części wyrażenia.
|
Dopasowuje 0 lub więcej wystąpień |
|
Dopasowuje 1 lub więcej wystąpień |
|
Dopasowuje 0 lub 1 wystąpienie |
|
Dopasowuje m do n wystąpień |
|
Dopasowuje zbiór znaków |
|
Dopasowuje znaki, które nie występują w zbiorze |
|
Dopasowuje A lub B |
|
Dopasowuje wyrażenie podane w nawiasie jako grupę |
Dodanie znaku zapytania po kwantyfikatorze powoduje przekształcenie go w tzw. kwantyfikator leniwy. Kwantyfikator leniwy pobiera znak po znaku szukając dopasowania. Ewentualna próba dopasowanie całego tekstu następuje na samym końcu.
|
Dopasowuje 0 lub 1 wystąpienie, wersja leniwa |
|
Dopasowuje 0 lub więcej, wersja leniwa |
|
Dopasowuje 1 lub więcej, wersja leniwa |
|
Dopasowuje m do n wystąpień, wersja leniwa |
Asercje wyrażeń regularnych¶
Asercja pozwala wyznaczyć miejsce w tekście, w którym musi pojawić się dopasowanie.
|
Dopasowuje początek stringa, także po każdym znaku nowego wiersza, jeśli włączono opcję re.MULTILINE |
|
Dopasowuje koniec stringa, , także przed każdym znakiem nowego wiersza, jeśli włączono opcję re.MULTILINE |
|
Dopasowuje początek stringa |
|
Dopasowuje pusty string na początku lub na końcu słowa |
|
Dopasowuje pusty string, lecz nie na początku lub na końcu słowa |
|
Dopasowuje na końcu stringa |
Surowe ciągi znaków¶
Surowe ciągi znaków (raw strings) ułatwiają pisanie wyrażeń regularnych w Pythonie.
Ciąg poprzedza się literą r
, która wyłącza specjalne znaczenie backslash’a w kolejnym ciągu znaków.
number_pattern = r'(\d+)\.(\d*)' # Dopasowuje liczby jak 3.4772
opis modułu re
¶
Wyrażenia regularne są definiowane z użyciem opisanej składni
Kompilowany do wyrażenia regularnego „obiekt”
Używany do wykonywania operacji dopasowywania i zastępowania
import re
text = '12.34'
number_pattern = r'(\d+)\.(\d*)' # Mój wzór
compiled_pattern = re.compile(number_pattern) # Kompilacja
is_matched = compiled_pattern.match(text) # Sprawdzam
if is_matched:
print("pasuje")
else:
print("nie pasuje")
Obiekty wyrażeń regularnych¶
Obiekty wyrażeń regularnych stworzone przez re.compile()
posiadają następujące metody:
r.search(s [,pos [,endpos]])
Szuka dopasowania
r.match(s [,pos [,endpos]])
Sprawdza dopasowanie stringu
r.split(s)
Dzieli według dopasowania regex
r.findall(s)
Znajduje wszystkie dopasowania
r.sub(repl,s)
Zamienia wszystkie dopasowania na repl
Jeśli znaleziono dopasowanie, zwracany jest obiekt dopasowania ’MatchObject’, który zawiera informację, gdzie znaleziono dopasowanie oraz o grupie
Metoda search szuka dopasowania w dowolnym miejscu stringa
Metoda match szuka dopasowania zaczynając od pierwszego znaku
Parametry pos i endpos określają początkową i końcową pozycję do wyszukania/dopasowania
Obiekty dopasowania¶
Obiekty dopasowania zawierają informację o samym dopasowaniu. Oparte są na pojęciu grup i stosują reguły grupowania.
Wyrażenie regularne (\d+)\.(\d*)
ma 3 grupy:
* Grupa 0 : całe wyrażenie regularne
* Grupa 1 : część (\d+)
* Grupa 2 : część (\d*)
Numerowanie grup we wzorcu przebiega od lewej do prawej.
Uzyskiwanie informacji o dopasowaniu:
m.group(n) # Zwraca tekst dopasowany dla grupy n
m.start(n) # Zwraca indeks początkowy dla grupy n
m.end(n) # Zwraca indeks końcowy dla grupy n
Przykłady dopasowania¶
import re
compiled_pattern = re.compile(r'(\d+)\.(\d*)')
match = compiled_pattern.match("42.37")
matched_part = match.group(0) # Zwraca ’42.37’
first_group = match.group(1) # Zwraca ’42’
second_group = match.group(2) # Zwraca ’37’
print(match.start(2)) # Drukuje 3
# Zastępowanie URL (np.: http://www.python.org) hiperłączem
url_pattern = r'(http://[\w-]+(\.[\w-]+)*((/[\w-~]*)?))'
compiled_pattern = re.compile(url_pattern)
compiled_pattern.sub('<a href="\\1">\\1</a>', text) # Zastępowanie w stringu
Testowanie kodu - moduł unittest¶
Wprowadzenie¶
Testowanie programu pozwala sprawdzić, czy program zachowuje się w pożądany sposób. Manualne testowanie programu jest czasochłonne. Dlatego dobrą praktyką jest pisanie automatycznych testów, które mogą zostać szybko wykonane przez komputer. Testuje się nie tylko całe programy, ale też pojedyncze funkcje czy klasy.
Standardowa biblioteka Pythona posiada moduł unittest
ułatwiający pisanie takich testów.
unittest
jest najpopularniejszą biblioteką stosowaną do pisania testów jednostkowych. Wynika to jej następujących cech:
unittest
jest częścią standardowej biblioteki Pythona, co oznacza, że jest dostępna wszędzie, gdzie jest zainstalowany Python.unittest
jest inspirowana bibliotekąJUnit
z Javy. Osoby, które pisały wcześniej testy wJUnit
bardzo szybko nauczą się pisać testy w Pythonie. Ponadto, wykorzystano sprawdzony interfejs. Z drugiej strony, wzorowanie się na bibliotece Javy powoduje, że testy są dosyć rozwlekłe i „rozgadane”.unittest
potrafi automatycznie znaleźć wszystkie testy i wykonać je.
Przykład¶
Testy grupuje się w klasy dziedziczące po unittest.TestCase
.
Każda metoda zaczynająca się od test_
to jeden test.
Wywołanie unittest.main()
powoduje uruchomienie wszystkich testów (nie jest potrzebne ręczne wymienianie nazw wszystkich testów).
Powszechnie przyjętą konwencją jest separacja testów i testowanego kodu, poprzez umieszczenie ich w osobnych plikach. Dodatkowo, testy umieszcza się w osobnym katalogu, a nie w tym samym katalogu co testowany moduł. Dzięki temu możliwa jest instalacja pakietu bez instalowania bibliotek wykorzystywanych tylko przez testy.
# converters.py
import re
def url_converter(url):
if not url:
raise ValueError("Empty url")
url_pattern = r'(http://[\w-]+(\.[\w-]+)*((/[\w-]*)?))'
compiled_pattern = re.compile(url_pattern)
return compiled_pattern.sub('<a href="\\1">\\1</a>', url)
# test_converters.py
import unittest
from converters import url_converter
class UrlConverterTests(unittest.TestCase):
def test_converts_url_to_ahref(self):
url = "http://www.python.org"
expected_ahref = '<a href="http://www.python.org">http://www.python.org</a>'
result = url_converter(url)
self.assertEqual(result, expected_ahref)
if __name__ == "__main__":
unittest.main()
Ostatnie dwie linie powyższego kodu gwarantują uruchomienie testów, ale tylko gdy plik z kodem zostanie bezpośrednio uruchomiony, a nie zaimportowany.
$ python test_converters.py
setUp
i tearDown
¶
Jeżeli na początku lub na końcu każdego testu wykonujemy operacje, które powtarzają się w innych testach, wówczas można je umieścić w metodach setUp
i tearDown
.
setUp
jest metodą wykonywaną na początku każdego testu, natomiast tearDown
– po wykonaniu testu.
Jest to świetne miejsce na:
uzyskanie zasobów, które są potrzebne w każdym teście (np. połączenie z bazą danych),
skonfigurowanie środowiska w ten sam sposób dla każdego testu (np. w
setUp
– utworzenie przykładowych tabel i rekordów w bazie danych, a wtearDown
– „posprzątanie” po teście, tzn. wyczyszczenie testowej bazy danych przez usunięcie tych tabel).
class TestAdd(unittest.TestCase):
def setUp(self):
print("setUp")
def tearDown(self):
print("tearDown")
def test_one(self):
print("test one")
def test_two(self):
print("test two")
setUp
test one
tearDown
setUp
test two
tearDown
self.assert*
¶
W środku każdego testu możemy wykorzystać szereg metod zaczynających się od assert
, np. assertEqual
.
Test przechodzi, jeżeli wszystkie takie asercje są prawdziwe.
Jeżeli chociaż jedna taka asercja nie będzie spełniona, wówczas wykonywanie testu jest natychmiast przerywane i wykonywana jest metoda tearDown
.
Najczęściej wykorzystywane asercje to:
self.assertTrue(condition)
iself.assertFalse(condition)
,self.assertEqual(got, expected)
iself.assertNotEqual(got, expected)
,self.assertIn(element, collection)
iself.assertNotIn(element, collection)
,self.assertIsInstance(obj, class_)
iself.assertNotIsInstance(obj, class_)
.
Do sprawdzenia, czy dany blok kodu rzuca wyjątek, można użyć self.assertRaises(ExceptionType)
:
class UrlConverterTests(unittest.TestCase):
def test_raises_exception_for_empty_string(self):
url = ""
with self.assertRaises(ValueError):
url_converter(url)