Python - wprowadzenie

11 - Złożone struktury danych

Poznane dotychczas struktury danych takie jak listy, krotki, słowniki i zbiory mogą zawierać nie tylko łańcuchy znaków czy liczby, ale także te wymienione struktury.

Tworzenie bardziej złożonej struktury danych

Utwórzmy zatem taką złożoną strukturę danych, zawierającą dane zasad azotowych. Umieścimy w niej, dla każdej zasady występującej w kwasach nukleinowych, informacje o jej pełnej, trójliterowej i jednoliterowej nazwie, typie zasady oraz jednoliterowych oznaczeń zasad, z którymi się dana zasada paruje (zasady komplementarne). Dane dla każdej zasady będą przechowywane w słownikach, w których kluczem będzie nazwa rodzaju informacji (pola) zapisana jako łańcuch znaków, a wartością będzie łańcuch znaku poza informacją dotyczącą komplementarnych zasad. Ponieważ w przypadku adeniny, w zależności od tego, czy mamy do czynienia z DNA lub RNA, są możliwe dwie zasady komplementarne, w tym ostatnim przypadku dane będziemy przechowywać w krotce. Wszystkie słowniki z danymi dla poszczególnych zasad, umieścimy w liście. Zatem otrzymamy listę, zawierającą słowniki, które zawierają łańcuchy znaków i krotki.

# Tworzymy kolekcję: lista zawierająca słowniki
# w słowniki znajdują się pary klucz-wartość
# Kluczami są łańcuchy znaków, 
# wartościami są łańcuchy znaków lub krotki 
zasady = [
    {'pelna': 'adenina', '3_literowa': 'Ade', 
    '1_literowa': 'A', 'typ': 'purynowa', 'para': ('T', 'U')},
    {'pelna': 'guanina', '3_literowa': 'Gua', 
    '1_literowa': 'G', 'typ': 'purynowa', 'para': ('C')},
    {'pelna': 'cytozyna', '3_literowa': 'Cyt', 
    '1_literowa': 'C', 'typ': 'pirymidynowa', 'para': ('G')},
    {'pelna': 'tymina', '3_literowa': 'Thy', 
    '1_literowa': 'T', 'typ': 'pirymidynowa', 'para': ('A')},
    {'pelna': 'uracyl', '3_literowa': 'Ura', 
    '1_literowa': 'U', 'typ': 'pirymidynowa', 'para': ('A')},
]

Budowę struktury danych, którą utworzyliśmy, można przedstawić na schemacie: Budowa struktury

Pobieranie danych ze złożonej struktury

Skoro skonstruowaliśmy tak złożoną strukturę danych, to czas ją wykorzystać. Dopiszmy kod, który pozwoli wypisać informację o każdej zasadzie z wprowadzonej przez użytkownika sekwencji.

# Pobranie sekwencji od użytkownika
sekwencja_1 = input("Podaj sekwencję: ")
# Pobieranie kolejnych liter z sekwencji
for litera in sekwencja_1:
    # Pobieranie kolejnych słowników z danymi
    for zasada in zasady:
        # Jeśli wartość pola z nazwą 1-literową jest taka jak litera
        if zasada['1_literowa'] == litera:
            # Wydrukuj informacje
            print(f"{zasada['1_literowa']} {zasada['3_literowa']} "+
            f"{zasada['pelna']} zasada {zasada['typ']}")
Podaj sekwencję:  GAACT

G Gua guanina zasada purynowa
A Ade adenina zasada purynowa
A Ade adenina zasada purynowa
C Cyt cytozyna zasada pirymidynowa
T Thy tymina zasada pirymidynowa

Za razie nie została użyta informacja o zasadach komplementarnych. Uzupełnijmy nasz program o sprawdzenie, czy druga, wpisana przez użytkownika sekwencja zawiera pasujące zasady. Program będzie wypisywał w kolejnych wierszach kolejne zasady, jeśli będą komplementarne, to umieści między nimi znak -, jeśli nie, to między nimi pojawi się spacja. Jeśli sekwencje będą się różniły długością, to będą one porównywane od pierwszej zasady aż do ostatniej w krótszej sekwencji. Pozostałe zasady w sekwencji dłuższej nie będą drukowane.

# Pobranie sekwencji od użytkownika
sekwencja_1 = input("Podaj pierwszą sekwencję: ")
# Pobranie drugiej sekwencji od użytkownika
sekwencja_2 = input("Podaj drugą sekwencję:    ")
# Pobieramy długość obu sekwencji
dlugosc_1 = len(sekwencja_1)
dlugosc_2 = len(sekwencja_2)
# "licznik"
i = 0
# Pętla porównuje sekwencje do ostatniej zasady
# w krótszej sekwencji
while i < dlugosc_1 and i < dlugosc_2:
    # Pobranie kolejnych liter z sekwencji
    litera_1 = sekwencja_1[i]
    litera_2 = sekwencja_2[i]
    # Pobieranie kolejnych słowników z danymi
    for zasada in zasady:
        # Jeżeli 1-lierowa nazwa jest taka jak litera
        if zasada['1_literowa'] == litera_1:
            # Pobranie słownika z pasującymi danymi
            dane = zasada
    #Jeżeli litera z drugiej sekwencji znajduje się 
    # w krotce z zasadami komplementarnymi
    if litera_2 in dane['para']:
        # znak łączący zasady to -
        komplementarne = '-'
    else:
        # znak łączący zasady to spacja
        komplementarne = ' '
    # Wydrukowanie zasad i łacznika
    print(f"{litera_1}{komplementarne}{litera_2}")
    # inkrementacja "licznika"
    i += 1
Podaj pierwszą sekwencję:  CATTAG
Podaj drugą sekwencję:     GTATAC

C-G
A-T
T-A
T T
A A
G-C

Zobaczmy teraz jak można pobrać wybrane informacje z naszej struktury danych:

# Pobranie całego, trzeciego słownika
print(zasady[0])
# Pobranie wartości z pierwszego słownika dla klucza "pelna"
print(zasady[0]['pelna'])
# Pobranie drugiej wartości z krotki z pierwszego słownika dla klucza "para"
print(zasady[0]['para'][1])
{'pelna': 'adenina', '3_literowa': 'Ade', '1_literowa': 'A', 'typ': 'purynowa', 'para': ('T', 'U')}
adenina
U

Jak widać, zasada jest prosta: po nazwie struktury znajduje się jedna, lub więcej par nawiasów kwadratowych, w których odwołujemy się do kolejnych elementów zagnieżdżonych struktur.

Kopiowanie złożonych struktur danych

Utwórzmy teraz kolejną strukturę: w liście umieścimy słowniki z informacjami na temat kwasów nukleinowych:

kwasy = [
    {'kwas': 'DNA', 'zasady': ('T', 'A', 'G', 'C')},
    {'kwas': 'RNA', 'zasady': ('U', 'A', 'G', 'C')},
]

print(kwasy)
[{'kwas': 'DNA', 'zasady': ('T', 'A', 'G', 'C')}, {'kwas': 'RNA', 'zasady': ('U', 'A', 'G', 'C')}]

Spróbujmy teraz skopiować ją za pomocą poznanej wcześniej metody copy(), a następnie zmieńmy jedną wartość w kopii i sprawdźmy zawartość obu list:

# Próba kopiowania
kwasy_kopia = kwasy.copy()
print("Przed zmianą:")
print(f"oryginał: {kwasy[1]}")
print(f"oryginał: {kwasy_kopia[1]}")
# zmiana wartości
kwasy_kopia[1]['kwas'] = 'rna'
print("Po zmianie:")
print(f"oryginał: {kwasy[1]}")
print(f"oryginał: {kwasy_kopia[1]}")
Przed zmianą:
oryginał: {'kwas': 'RNA', 'zasady': ('U', 'A', 'G', 'C')}
oryginał: {'kwas': 'RNA', 'zasady': ('U', 'A', 'G', 'C')}
Po zmianie:
oryginał: {'kwas': 'rna', 'zasady': ('U', 'A', 'G', 'C')}
oryginał: {'kwas': 'rna', 'zasady': ('U', 'A', 'G', 'C')}

Jak widać zmiana w “kopii” spowodowała również zmianę wartości w “oryginalnej” strukturze. Metoda copy() nie sprawdza się w tak złożonych strukturach. W takich przypadkach najprościej jest zrobić to za pomocą metody deepcopy():

import copy

kwasy = [
    {'kwas': 'DNA', 'zasady': ('T', 'A', 'G', 'C')},
    {'kwas': 'RNA', 'zasady': ('U', 'A', 'G', 'C')},
]

# Próba kopiowania
kwasy_kopia = copy.deepcopy(kwasy)
print("Przed zmianą:")
print(f"oryginał: {kwasy[1]}")
print(f"oryginał: {kwasy_kopia[1]}")
# zmiana wartości
kwasy_kopia[1]['kwas'] = 'rna'
print("Po zmianie:")
print(f"oryginał: {kwasy[1]}")
print(f"oryginał: {kwasy_kopia[1]}")
Przed zmianą:
oryginał: {'kwas': 'RNA', 'zasady': ('U', 'A', 'G', 'C')}
oryginał: {'kwas': 'RNA', 'zasady': ('U', 'A', 'G', 'C')}
Po zmianie:
oryginał: {'kwas': 'RNA', 'zasady': ('U', 'A', 'G', 'C')}
oryginał: {'kwas': 'rna', 'zasady': ('U', 'A', 'G', 'C')}

Teraz mamy do czynienia z rzeczywistą kopią, zmiana w niej nie powoduje modyfikacji pierwotnej struktury. W powyższym kodzie zagadkowa pozostaje pierwsza linia zawierająca instrukcję import copy. Dokładniej wyjaśnię jej sens w kolejnej lekcji, na razie wystarczy wiedzieć, że dzięki niej możemy używać obiektu copy, który posiada wywoływaną metodę deepcopy().

Last updated on 25 Nov 2020
Published on 25 Nov 2020