wyklad5 Python


Programowanie Fizyka Medyczna
Wykład V
3 kwietnia 2012
Przypomnienie i rozszerzenie wiadomości z poprzednich wykładów

Operacje na typach złożonych,

Standardowe moduły: sys, os

Podstawy definicji klasy,
Dziedziczenie:

Dziedziczenie wielokrotne,

super

MRO  Method Resolution Order,

Kiedy __init__(), kiedy __new__()?
Moduł PyDicom:

Podstawowe operacje: wczytywanie i zapisywanie plików,

Elementy Dicom Tag,

Anonimizacja,

Zmiana, usuwanie i wyszukiwanie elementów,
Typy złożone mutable - ściąga
Dodawanie elementu Usuwanie elementu Czyszczenie
Lista L.append(elem) del L[indeks] del L[:]
(list) L.insert(indeks,elem) L.pop(indeks)
L.extend(iterable) L.remove(wart)
Zbiór S.update(iterable) S.discard(wart) S.clear()
(set) S.add(hashable) S.remove(wart)
S.pop()
Słownik D.update() D.pop(key,default) D.clear()
(dictionary) D[klucz] = wart D.popitem(key,default)
Ciąg bajtów B.append(liczba_calk) del B[indeks] del B[:]
(bytearray) B.insert(indeks,licz_cal) B.pop(indeks)
B.extend(iterable_int) B.remove(wart)
del Z  usuwa dowolną zmienną Z
Programowanie FizMed, wykład 5 2
Moduł sys
Moduł ten daje nam dostęp do podstawowych informacji o systemie
operacyjnym i interpreterze python
import sys
Niektóre częściej używane funkcje:
sys.hexversion Wersja interpretera Python zapisana jako liczba szestnastkowa
sys.platform Windows czy Linux, 64bit czy 32bit
sys.executable scieżka do interpretera Python
sys.argv argumenty wywołania skryptu
sys.float_info słowniki przechowujące informacje o szczegółach
sys.int_info implementacji typów int i float
sys.path lista zawierająca katalogi przeszukiwane w poszukiwaniu
plików.
sys.modules lista załadowanych modułów
Programowanie FizMed, wykład 5 3
Moduł os
Moduł do podstawowych operacji w systemie
import os
os.linesep  znak końca linii ('\r' lub '\n') w *nix lub MacOS, '\r\n' w Windows
os.path.abspath(s) absolutna ścieżka dla nazwy względnej s
os.path.split(s) dzieli ścieżkę s na listę nazw [dysk, katalogi, plik]
os.path.join(s,p) łączy dwie części ścieżki w jedną używając poprawnego
łącznika
os.path.basename(s) zwraca nazwę pliku lub ostatniego katalogu
os.path.dirname(s) zwraca wszystko przed basename
os.path.exists(s) sprawdza czy dana ścieżka/plik istnieje
os.stat(s) informacja o statusie obiektu na dysku (poniżej konkretne
pola)
os.path.getatime(s) czas ostatniego dostępu do obiektu na dysku
getctime(s) utworzenia
getmtime(s) modyfikacji
os.path.getsize(s) rozmiar pliku
os.path.isdir(s) czy jest katalogiem
os.path.isfile(s) czy jest plikiem
Programowanie FizMed, wykład 5 4
Obiekt jako przedstawiciel klasy
class mojaklasa(object):
def __init__(self, *args, **kwargs):
print("Tu jest inicjalizacja obiektu")
def __repr__(self):
return "Reprezentacja tekstowa obiektu"
def __str__(self):
return "Konwersja na string"
Mając zdefiniowaną klasę, możemy już utworzyć jej przedstawiciela, czyli
obiekt.
a = mojaklasa()
a jest obiektem klasy mojaklasa. Klasa ta może mieć kilku przedstawicieli.
b = mojaklasa()
b też jest obiektem klasy mojaklasa.
Mówiliśmy już też o metodachnp. __call__() i __getitem__(), __add__() i
__bool__().
Programowanie FizMed, wykład 5 5
Dziedziczenie
Dziedziczenie (inheritance) jest mechanizmem współdzielenia
funkcjonalności między klasami.
W Pythonie klasa może dziedziczyć po jednej lub kilku klasach, co
oznacza, że oprócz swoich własnych atrybutów oraz zachowań, uzyskuje
także te pochodzące z klasy dziedziczonej.
Klasa dziedzicząca jest nazywana klasą pochodną lub potomną (w j.
angielskim: subclass lub derived class).
Klasa, z której następuje dziedziczenie  klasą bazową (w ang.
superclass).
Z jednej klasy bazowej można uzyskać dowolną liczbę klas pochodnych.
Klasy pochodne posiadają obok swoich własnych metod i pól, również
kompletny interfejs (metody i pola) klasy bazowej.
Wszystkie klasy w Python 3 dziedziczą po pierwotnej klasie o nazwie object,
nawet jeżeli nie jest to jawnie napisane w definicji klasy.
Programowanie FizMed, wykład 5 6
http://pl.wikipedia.org/wiki/Dziedziczenie_(programowanie)
Dziedziczenie i hermetyzacja
Dziedziczenie pozwala na budowanie
skomplikowanych funkcjonalnie klas z
prostszych elementów.
Klasy bazowe mogą stanowić niezależną,
zamkniętą całość, która istnieje bez klasy
pochodnej.
Klasy pochodne mają dostęp tylko do
określonych elementów klas bazowych.
Takie podejście nazywamy hermetyzacją
lub enkapsulacją.
class Pierwsza(): class Druga(object):
pass pass
Obie klasy Pierwsza i Druga dziedziczą po object, nawet jeżeli w
pierwszym przypadku nie jest to jawnie napisane.
Programowanie FizMed, wykład 5 7
http://pl.wikipedia.org/wiki/Hermetyzacja_(informatyka)
Drzewo klas
class Druga(object):
class Pierwsza():
def __init__(self):
def __init__(self):
self.wart = 2
self.wart = 1
pass
pass
Klasa bazowa
object
Pierwsza Druga
wart = 1 wart = 2
Klasy pochodne
Programowanie FizMed, wykład 5 8
Drzewo klas  dziedziczenie po klasach pochodnych
Klasa bazowa
object
Pierwsza Druga
wart = 1 wart = 2
Klasy pochodne do object
class Trzecia(Pierwsza):
def __init__(self):
Trzecia
pass
Klasa pochodna do Pierwsza
Programowanie FizMed, wykład 5 9
Propagacja inicjalizacji
object
Pierwsza Druga
wart = 1 wart = 2
p = Pierwsza()
print(p.wart) # OK wart jest 1
t = Trzecia()
print(t.wart) # << błąd !!!
Trzecia
Czy klasy bazowe są inicjalizowane?
Programowanie FizMed, wykład 5 10
"__init__" wywoływany jest tylko raz!
class Pierwsza(): p = Pierwsza()
def __init__(self): print(p.wart) # OK wart jest 1
self.wart = 1 # bo jest ustawiona w __init__
pass
t = Trzecia()
class Trzecia(Pierwsza):
print(t.wart) # << błąd !!!
def __init__(self):
pass
W klasie głównej znaleziona jest metoda __init__ i to ona jest wykonana
p = Pierwsza()
class Pierwsza():
print(p.wart) # OK wart jest 1
def __init__(self):
# bo jest ustawiona w __init__
self.wart = 1
pass
t = Trzecia()
class Trzecia(Pierwsza):
print(t.wart) # OK jest wart 1
pass
# bo __init__ jest z klasy Pierwsza
Jeżeli nie ma __init__ w klasie głownej to wykonana jest pierwsza znaleziona.
Programowanie FizMed, wykład 5 11
Co zrobić, gdy __init__ musimy mieć również w klasie pochodnej?
"Klasa" super
class Pierwsza():
def __init__(self):
object
self.wart = 1
pass
super
class Trzecia(Pierwsza):
def __init__(self):
# inicjalizacja klasy Trzecia
Pierwsza
super(Trzecia, self).__init__()
wart = 1
p = Pierwsza()
super
print(p.wart) # OK wart jest 1
# bo jest ustawiona w __init__
Trzecia
t = Trzecia()
print(t.wart) # OK
Klasa super jest uniwersalnym mechanizmem pozwalającym na inicjalizację
klas bazowych z klasy pochodnej.
Programowanie FizMed, wykład 5 12
"Diamond inheritance"  czyli kłopoty
object
Pierwsza Druga
wart = 1 wart = 2
Czwarta
class Czwarta(Pierwsza, Druga):
def __init__(self):
# inicjalizacja klasy Czwarta
# inicjalizacja klas bazowych
super(Czwarta,self).__init__()
"diamond"
# ale w jakiej kolejności???
Programowanie FizMed, wykład 5 13
http://pl.wikipedia.org/wiki/Hermetyzacja_(informatyka)
MRO = Method Resultion Order
object
Pierwsza Druga
wart = 1 wart = 2
Czwarta
Każda klasa przechowuje kolejność klas używająca mechanizmu
super(klasa,self).metoda()
Jest to krotka Klasa.__mro__ klas informującą w jakiej kolejności
będzie przebiegać wywołanie poprzez super.
Programowanie FizMed, wykład 5 14
Aańcuch MRO
object
Pierwsza Druga
wart = 1 wart = 2
Czwarta
Pierwsza.__mro__
(, )
Czwarta.__mro__
(, , '__main__.Druga'>, )
Programowanie FizMed, wykład 5 15
Ciągłość łancucha
Czwarta.__mro__
(, , '__main__.Druga'>, )
class Pierwsza(): class Druga(object):
def __init__(self): def __init__(self):
print("In Pier") print("In Dru")
self.wart = 1 self.wart = 2
class Czwarta(Pierwsza, Druga):
def __init__(self):
print("In 4")
# inicjalizacja klasy Czwarta
# inicjalizacja klas bazowych
super(Czwarta,self).__init__()
c = Czwarta()
In 4
In Pier
#A co z resztą MRO? Nie jest ona automatycznie przekazywana dalej
Programowanie FizMed, wykład 5 16
Poprawna inicjalizacja wymaga kontynuacji wywołania
super
Czwarta.__mro__
(, , '__main__.Druga'>, )
class Pierwsza(): class Druga(object):
def __init__(self): def __init__(self):
print("In Pier") print("In Dru")
self.wart = 1 self.wart = 2
super(Pierwsza,self).__init__() super(Druga,self).__init__()
class Czwarta(Pierwsza, Druga):
def __init__(self):
print("In 4")
# inicjalizacja klasy Czwarta
# inicjalizacja klas bazowych
super(Czwarta,self).__init__()
c = Czwarta()
In 4
In Pier
In Dru
c.wart
Programowanie FizMed, wykład 5 17
2
Podsumownie MRO
object
Pierwsza Druga
wart = 1 wart = 2
Czwarta
1. Prędzej czy pozniej "diamond inheritance" pojawi się w programie.
2. Jeżeli nie zrobimy tego my, może to zrobić nieświadomie ktoś, kto
korzysta z naszej klasy.
3. Mechanizm MRO zapewni jednoznaczną kolejność klas pochodnych.
4. Do nas należy poprawne przekazanie wywołań super(Klasa,
self).metoda() w górę łańcucha.
Programowanie FizMed, wykład 5 18
Przykładowe i bezpieczne definicje klas
Dodatkowym mechanizmem zabezpieczającym przed problemami z
dziedziczeniem jest obsługa parametrów inicjalizacji.
Proszę unikać parametrów pozycyjnych, a zmienne przekazywać poprzez
named arguments.
class Pierwsza():
def __init__(self, *args, **kwargs ):
print("In Pier")
self.wart = 1
super(Pierwsza,self).__init__(*args, **kwargs )
class Druga(object):
def __init__(self, *args, **kwargs):
print("In Dru")
self.wart = 2
super(Druga,self).__init__(*args, **kwargs )
class Czwarta(Pierwsza, Druga):
def __init__(self, *args, **kwargs):
print("In 4")
# inicjalizacja klasy Czwarta
# inicjalizacja klas bazowych
super(Czwarta,self).__init__(*args, **kwargs )
Programowanie FizMed, wykład 5 19
#Przykład z http://fuhm.net/super-harmful/
class A(object):
def __init__(self, *args, **kwargs):
print ("A" )
super(A, self).__init__(*args, **kwargs)
class B(object):
object
def __init__(self, *args, **kwargs):
print ("B" )
super(B, self).__init__(*args, **kwargs)
class C(A):
def __init__(self, arg, *args, **kwargs):
print ("C","arg=",arg)
A B
super(C, self).__init__(arg, *args, **kwargs)
class D(B):
def __init__(self, arg, *args, **kwargs ):
print ("D", "arg=",arg)
super(D, self).__init__(arg, *args, **kwargs)
C D
class E(C,D):
def __init__(self, arg, *args, **kwargs):
print( "E", "arg=",arg)
super(E, self).__init__(arg, *args, **kwargs)
E
print (E.__mro__)
print( "MRO:", [x.__name__ for x in E.__mro__])
E(10)
#E()
"""MRO: ['E', 'C', 'A', 'D', 'B', 'object']
Programowanie FizMed, wykład 5 20
Metoda __new__
Metoda __init__() nadaje się dobrze do inicjalizacji klas opartych na typach
mutable, ponieważ można je zmieniać.
Problem pojawia się, gdy nasza klasa ma być oparta na typie immutable na
przykład str lub bytes.
Jeżeli zmienna takiego typu jest już utworzona, nie można jej zmienić w
metodzie __init__ bez zniszczenia całej oryginalnej zawartości.
W Pythonie w pojawia się metoda __new__(), która jest odpowiednikiem
konstruktora z innych języków obiektowych.
Jest ona wywoływana przed metodą __init__().
Podlega takim samym regułom MRO jako __init__().
class Baza1(object):
def __new__(klasa, wart):
print("In Baza1 __new__,klasa = ", klasa)
return super(Baza1,klasa).__new__(klasa, wart)
def __init__(self,value):
print("In baza 1 __init__")
Przykładem są klasy PersonName i PersonNameUnicode z Pydicom
Programowanie FizMed, wykład 5 21
Wracając do PyDicom
Programowanie FizMed, wykład 5 22
Przypomnienie podstawowych informacji o PyDicom
import dicom
Podstawowym obiektem jest dataset, który można uważać za specjalny słownik.
dicom.read_file(s) wczytuje plik o ścieżce s i zwraca dataset
ds = dicom.read_file("MR_small.dcm")
Wyświetlenie elementów słownika możliwe jest przez wpisanie jego nazwy w
sesji interaktywnej lub polecenie
dir(ds)
które zwraca listę kluczy.
Obraz jest dostępny poprzez pole pixel_array, w postaci macierzy numpy.array,
którą można wyświetlić używając biblioteli matplotlib.
Pakiet PyDICOM automatycznie wykonuje konwersję.
import pylab
pylab.imshow(ds.pixel_array, cmap=pylab.cm.bone)
pylab.show()
Programowanie FizMed, wykład 5 23
Przypomnienie podstawowych informacji o PyDicom
cd.
Informacje wpliku dicom zorganizowane są w postaci tablicy.
Indeks tej tablicy to para liczb szestnastkowych tzw DICOM Tag, która określa
charakter pola.
(0008, 0008) Image Type CS: [b'DERIVED', b'SECONDARY', b'OTHER']
(0008, 0012) Instance Creation Date DA: b'20040826'
(0008, 0013) Instance Creation Time TM: b'185434'
Lista znaczników (tagów) rozpoznawanych przez pakiet PyDicom znajduje się w
pliku
_dicom_dict.py
0x00080008: ('CS', '2-n', "Image Type", '', 'ImageType'),
DICOM Tag Description  opis typu pola wynikająca z jego znacznika/tagu.
DICOM Tag Name  opis typu pola wynikająca z jego znacznika/tagu.
VR  Value Record  jakiego typu jest wartość pola (CS-string, DA - Date)
2-n oznacza, ze wartość ma długość od 2 do n znaków, czyli więcej niż jeden.
Programowanie FizMed, wykład 5 24
PyDicom cd.
Pola, które nie zostaną znalezione w ogólnym słowniku traktowane są jako
prywatne sekcje:
Private creator
(0009, 0000) Private Creator UL: 160
(0009, 0010) Private tag data LO: b'GEMS_IDEN_01'
(0021, 0000) Private Creator UL: 298
(0021, 0010) Private tag data LO: 'GEMS_RELA_01'
Na podstawie pola Private tag data można zidentyfikować wartości
specyficzne dla producenta urządzenia.
Lista znaczników zarezerwowanych dla producentów znajduje się w pliku:
_private_dict.py
Programowanie FizMed, wykład 5 25
Anonimizacja
Anonimizacja pliku dicom polegała będzie na usunięciu z niego danych,
które nie są potrzebne. Np. w celach badawczych nie są potrzebne dane
personalne pajenta, czy też nazwisko lekarza lub nazwa przychodni.
Aby to zrobić musimy:
1) Znalezć konkretne pole.
2) Usunąć go
3) Zapisać tak powstały plik.
Obiekt dataset posiada wbudowane 2 metody ułatwiające to zadanie.
ds.remove_private_tags() - usuwa sekcje Private Creator i ich zawartość.
ds.walk(callback_func)
callback_func  jest nazwą funkcji, którą musimy sami napisać. Będzie ona
wywołana dla każdego znacznika (tagu) obecnego w pliku dicom
Programowanie FizMed, wykład 5 26
Anonimizacja
Jeżeli znamy "tag" znacznika, możemy bezpośrednio zmienić jesgo wartość.
Na przykład z dataset o nazwie ds wyciągamy znacznik (data element) o tagu
(0028,0102).
de = ds[(0x0028,0x0102)]
de.name # nazwa znacznika
'High Bit'
de.value # jego wartość
15
de.VR # jego typ
'US'
de.value = 0 # zmieni wartość w obiekcie de a ponieważ jest on referencją do
oryginalnego pola, zostanie zmieniona wartość w datasecie.
Programowanie FizMed, wykład 5 27
Anonimizacja c.d.
Pole "Patients Name"
Z dataset o nazwie ds wyciągamy znacznik o tagu (0010,0010).
de = ds[(0x0010,0x0010)]
de.value = "" # Kasuje nazwisko pacjenta
Tak zmieniony plik możemy zapisać pod nową nazwą:
ds.save_as(output_filename)
Programowanie FizMed, wykład 5 28
Koniec na dzisiaj!
Programowanie FizMed, wykład 5 29


Wyszukiwarka