Python - wprowadzenie

02 - Listy składane (`list comprehension`)

Dotychczas, chcąc wypełnić listę zestawem kolejnych liczb, używaliśmy takiego kodu:

liczby = list(range(1,10))
print(liczby)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Listę liczb parzystych uzyskiwaliśmy np. tak:

liczby = []
for i in range(1,10):
    if i % 2 == 0:
        liczby.append(i)
print(liczby)
[2, 4, 6, 8]

Oczywiście można by to samo uzyskać w ten sposób:

liczby = list(range(2,10, 2))
print(liczby)

ale dla celów dydaktycznych będziemy się trzymać poprzedniej składni.

Chcąc stworzyć listę składająca się z kolejnych parzystych liczb, podniesionych do potęgi moglibyśmy napisać tak:

liczby = []
for i in range(1,10):
    if i % 2 == 0:
        liczby.append(i**2)
print(liczby)
[4, 16, 36, 64]

Taki kod dobrze się sprawdza, ale można go napisać krócej. Np. listę liczb parzystych uzyskamy używając kodu:

liczby = [i for i in range(1, 10) if i % 2 == 0]
print(liczby)
[2, 4, 6, 8]

Natomiast listę liczb parzystych podniesionych do kwadratu również można otrzymać, używając tak ,,skondensowanego’’ kodu:

liczby = [i**2 for i in range(1, 10) if i % 2 == 0]
print(liczby)
[4, 16, 36, 64]

Jak widać, to co wcześniej zajmowało cztery linie kodu, teraz można osiągnąć, używając jednej. Zastosowaliśmy właśnie listę składaną.

Można kod dalej rozbudowywać:

liczby = [i for i in range(1, 20) if i % 2 == 0 if i % 3 == 0]
print(liczby)
[6, 12, 18]

Także zastosować konstrukcję if ... else:

sekwencja = 'ACCGTA'
zasady = ['puryna' if z == 'A' or z == 'G' else 'pirymidyna' for z in sekwencja]
print(zasady)
['puryna', 'pirymidyna', 'pirymidyna', 'puryna', 'pirymidyna', 'puryna']

Być może zastanawiasz się, dlaczego kiedy używaliśmy wyrażenia if, bez else, umieszczaliśmy je po prawej stronie wyrażenia for ... in ..., natomiast używając konstrukcji if ... else ..., umieszczaliśmy ją po lewej stronie wyważenia for ... in .... Może to na początku wydawać się nieintuicyjne, ale tak naprawdę w tych dwu przypadkach robimy dwie różne rzeczy. Samo if powoduje, że tylko filtrujemy elementy (np. wybieramy liczby, które są parzyste), zatem wyrażenie umieszczamy po prawej stronie. Natomiast w przypadku if ... else ... w zależności od spełniania, lub nie określonych warunków, wybieramy taką lub inną zwracaną wartość, można powiedzieć, że dokonujemy transformacji elementu. Wtedy wyrażenie powinno znaleźć się po lewej stronie for ... in ....

Zatem:

  • Umieszczaj wyrażenie if po prawej stronie for ... in ..., kiedy tylko filtrujesz elementy uzyskiwane w wyniku iteracji.
  • Umieszczaj if ... else ... po lewej stronie for ... in ... kiedy zamierzasz dokonywać transformacji elementów w zależności od spełniania (lub nie) określonych warunków.

Poza wyrażeniami arytmetycznymi można też na generowanych elementach wywoływać funkcje, czy metody:

sekwencja = 'ACCGTA'
sekwencja_male = [z.lower() for z in sekwencja]
print(sekwencja_male)
['a', 'c', 'c', 'g', 't', 'a']

Listy składane mogą się przydać przy tworzeniu nieco bardziej złożonych struktur danych, tu listy krotek:

wartosci = [22, 12, 21, 18, 15, 13, 18]
obiekty =  ['A', 'B', 'C', 'D', 'E', 'F', 'G']
dane = [(o, w) for o, w in zip(obiekty, wartosci)]
print(dane)
[('A', 22), ('B', 12), ('C', 21), ('D', 18), ('E', 15), ('F', 13), ('G', 18)]

Teraz użyjmy nieco bardziej złożonego przykładu. Przypuśćmy, że mamy jedną listę dłuższych sekwencji (dlugie), oraz drugą, zawierającą krótsze sekwencje. Chcemy wyszukać wszystkie krótsze sekwencje we wszystkich dłuższych a rezultat tych porównań zachować w liście, zawierającej krótsze listy, które będą zawierać obie porównywane sekwencje i wynik porównania (True lub False). Używając ,,klasycznej’’ zagnieżdżonej konstrukcji for, można to zrobić tak:

dlugie = ['AGACAAGAA', 'TTTCAAGGG', 'GGAGACCTA', 'AAGGTTAAA']
krotkie = ['AGA', 'AGG']
wynik = []
for d in dlugie:
    for k in krotkie:
        wynik.append([d, k, k in d])
print(f'Lista: {wynik}')
#Estetyczny wydruk:
print()
for k, d, t in wynik:
    print(f'{k} {d} {t}')
Lista: [['AGACAAGAA', 'AGA', True], ['AGACAAGAA', 'AGG', False], ['TTTCAAGGG', 'AGA', False], ['TTTCAAGGG', 'AGG', True], ['GGAGACCTA', 'AGA', True], ['GGAGACCTA', 'AGG', False], ['AAGGTTAAA', 'AGA', False], ['AAGGTTAAA', 'AGG', True]]

AGACAAGAA AGA True
AGACAAGAA AGG False
TTTCAAGGG AGA False
TTTCAAGGG AGG True
GGAGACCTA AGA True
GGAGACCTA AGG False
AAGGTTAAA AGA False
AAGGTTAAA AGG True

Zastosujmy teraz listę składaną:

dlugie = ['AGACAAGAA', 'TTTCAAGGG', 'GGAGACCTA', 'AAGGTTAAA']
krotkie = ['AGA', 'AGG']
wynik = [[d, k, k in d] for d in dlugie for k in krotkie]
print(wynik)
#Estetyczny wydruk:
print()
for k, d, t in wynik:
    print(f'{k} {d} {t}')
[['AGACAAGAA', 'AGA', True], ['AGACAAGAA', 'AGG', False], ['TTTCAAGGG', 'AGA', False], ['TTTCAAGGG', 'AGG', True], ['GGAGACCTA', 'AGA', True], ['GGAGACCTA', 'AGG', False], ['AAGGTTAAA', 'AGA', False], ['AAGGTTAAA', 'AGG', True]]

AGACAAGAA AGA True
AGACAAGAA AGG False
TTTCAAGGG AGA False
TTTCAAGGG AGG True
GGAGACCTA AGA True
GGAGACCTA AGG False
AAGGTTAAA AGA False
AAGGTTAAA AGG True

Zadania

Zadanie 1

Napisz program, który sprawdzi, czy w długiej sekwencji (np. AAAAAGGAATTCCCCTAGAATTAAAAACCAGGTTACTG) znajdują się trójzasadowe odcinki, np: AAA, ATT, GGG, AGG.

Rozwiązanie

sekwencja = 'AAAAAGGAATTCCCCTAGAATTAAAAACCAGGTTACTG'
odcinki = ['AAA', 'ATT', 'GGG', 'AGG']
wynik = [[o, o in sekwencja] for o in odcinki]

for s, t in wynik:
    print(f'{s}: {t}')
AAA: True
ATT: True
GGG: False
AGG: True

Zadanie 2

Zmodyfikuj program tak, aby drukował znalezione fragmenty (!), wraz z ich pozycjami w długiej sekwencji.

import re
sekwencja = 'AAAAAGGAATTCCCCTAGAATTAAAAACCAGGTTACTG'
odcinki = ['AAA', 'ATT', 'GGG', 'AGG']
wynik = [[o, w.start()] for o in odcinki for w in re.finditer(o, sekwencja) ]

for s, m in wynik:
    print(f'{s}: {m}')
AAA: 0
AAA: 22
ATT: 8
ATT: 19
AGG: 4
AGG: 29
Last updated on 6 Mar 2024
Published on 6 Mar 2024