Lotimo se nečesa podjetnejšega: radi bi izpisali ime Qwertyjevega gena, ki vsebuje največ A-jev.
No, za začetek pohlevnejši cilj: koliko A-jev ima gen, ki ima največ A-jev? (Če je vprašanje bolj zapleteno, to ne pomeni nujno, da je nanj težje odgovoriti. V tem primeru že ni tako.)
najvec = 0
for vrstica in open("datoteke/qwerty-dna.txt"):
ime, zaporedje = vrstica.split()
if zaporedje.count("a") > najvec:
najvec = zaporedje.count("a")
print(najvec)
32
Ideja programa je takšna: v spremenljivko najvec
bomo shranili največje število a-jev, kar smo jih kdaj videli. V začetku je to 0
. Nato za vsako vrstico pogledamo, ali je število a-jev, ki jih vsebuje, večje od največjega doslej; če je, je največje pač to.
Ker ni lepo, da računalnik silimo delati več, kot je treba (sploh, kadar to delo ne bo tako hitro in preprosto, kot je štetje a-jev v besedilu), popravimo program tako, da bo štel le enkrat.
najvec = 0
for vrstica in open("datoteke/qwerty-dna.txt"):
ime, zaporedje = vrstica.split()
ajev = zaporedje.count("a")
if ajev > najvec:
najvec = ajev
print(najvec)
32
Zdaj pa k prvotni nalogi: izpisali bi radi ime gena, ne števila a-jev. Za to si bo potrebno poleg največjega števila genov (ki ga potrebujemo zato, da jih lahko primerjamo) pač zapomniti še ime gena.
najvec = 0
for vrstica in open("datoteke/qwerty-dna.txt"):
ime, zaporedje = vrstica.split()
ajev = zaporedje.count("a")
if ajev > najvec:
najvec = ajev
naj_gen = ime
print(naj_gen)
GHJK17
No, saj ni bilo tako težko.
Zdaj pa si zapletimo življenje. Radi bi izpisali imena vseh genov, urejena po abecedi.
Z datotekami bo to težko. Datoteke so okorne. Za urejanje potrebujemo nekaj fleksibilnejšega: sezname.
Seznam je ... pač seznam. Seznam nekih stvari. Opišemo ga tako, da med oglatimi oklepaji naštejemo njegove elemente, ločene z vejicami, na primer
imena = ["Ana", "Berta", "Cilka", "Dani"]
teze = [72, 85, 70, 82]
Prek seznamov lahko gremo kar z zanko for
, čisto tako kot prek datoteke.
for ime in imena:
print(ime)
Ana Berta Cilka Dani
Seznam lahko izpišemo tudi s print
: izpiše se z oglatimi oklepaji in vejicami. Ni estetsko, dela pa.
print(imena)
print(teze)
['Ana', 'Berta', 'Cilka', 'Dani'] [72, 85, 70, 82]
Če hočemo seznam vseh genov, bomo sestavili prazen seznam, šli s for
prek datoteke in zlagali vsa imena v seznam.
geni = []
for vrstica in open("datoteke/qwerty-dna.txt"):
ime, zaporedje = vrstica.split()
geni.append(ime)
print(geni)
['ASDF13', 'SDFG14', 'DFGH15', 'FGHJ16', 'GHJK17', 'HJKL18', 'ZXCV19', 'XCVB20', 'CVBN21', 'VBNM22']
Kaj je append
, najbrž lahko uganemo: metoda seznamov, ki na konec seznam dopiše nov element.
Zaželeli smo si izpisati vse gene, urejene po abecedi, ne? Ni problema: seznam ima metodo sort
, ki uredi njegove elemente. Primerja jih tako, kot je za tip elementa smiselno - številke po velikosti, nize po abecedi.
geni.sort()
print(geni)
['ASDF13', 'CVBN21', 'DFGH15', 'FGHJ16', 'GHJK17', 'HJKL18', 'SDFG14', 'VBNM22', 'XCVB20', 'ZXCV19']
Seznami imajo v resnici še goro metod. Kot smo zamolčali metode nizov, bomo tudi metode seznamov. Vsak pa naj preleti seznam, da bo vedel, kaj obstaja. Uradni seznam je v Pythonovi dokumentaciji, gostobesednejši opis pa je v zapiskih predmeta Programiranje 1.
Seznami so prikladnejši od datotek (tudi) zato, ker so shranjeni v pomnilniku in jih, za razliko od datotek (ki se vedejo, kot da bi bile shranjene na magnetnih trakovih, ki jih lahko vidimo na filmih iz šestdesetih), ne beremo le po vrsti po vrsticah, temveč lahko dostopamo do poljubnega elementa. Zaporedno številko elementa, ki nas zanima, zapišemo med oglate oklepaje. Tako, recimo geni[2]
pomeni drugi element seznama geni
.
geni[2]
'DFGH15'
Drugi? I, pač. Računalnikarji štejejo od 0, ne od 1.
geni[0]
'ASDF13'
geni[1]
'CVBN21'
Poleg tega znajo računalnikarji šteti tudi nazaj. Element -1 (zakaj ne -0? no, no, razmisli!) je zadnji element, -2 je predzadnji...
geni[-1]
'ZXCV19'
geni[-2]
'XCVB20'
in tako nazaj.
Zahtevamo lahko tudi "rezino", na primer vse elemente od drugega do petega: zapišemo oba indeksa, vmes pa damo dvopičje.
geni[2:5]
['DFGH15', 'FGHJ16', 'GHJK17']
Kot je pričakovati, so geni od drugega do petega trije. Torej drugi, tretji in četrti. Ja, spodnja meja je vključena, gornja ne. Ker je tako praktično.
Če hočemo vse elemente do petega ali od drugega, preprosto izpustimo spodnjo oziroma zgornjo mejo.
geni[:5]
['ASDF13', 'CVBN21', 'DFGH15', 'FGHJ16', 'GHJK17']
geni[2:]
['DFGH15', 'FGHJ16', 'GHJK17', 'HJKL18', 'SDFG14', 'VBNM22', 'XCVB20', 'ZXCV19']
Genov do petega je pet, torej je [:5]
isto, kot če bi rekli "prvih pet". Če hočemo vse gene od drugega naprej, pa je isto, kot če bi rekli "brez prvih dveh". (To seveda deluje predvsem in samo zato, ker štejemo od 0!)
Ker znamo šteti tudi od zadaj, znamo zahtevati vse gene razen zadnjih štirih (torej vse do minus četrtega; to deluje, ker gornja meja intervala ni vključena), ali pa prav zadnje štiri (torej od minus četrtega naprej; to deluje, ker ima zadnji indeks -1 in ne -0, kar bi bilo tako ali tako nemogoče, saj je -0 isto kot 0).
geni[:-4]
['ASDF13', 'CVBN21', 'DFGH15', 'FGHJ16', 'GHJK17', 'HJKL18']
geni[-4:]
['SDFG14', 'VBNM22', 'XCVB20', 'ZXCV19']
Z indeksiranjem lahko sezname tudi spreminjamo. Če bi bila Ana raje Ančka, lahko seznam
imena
['Ana', 'Berta', 'Cilka', 'Dani']
spremenimo z
imena[0] = "Ančka"
pa imamo
imena
['Ančka', 'Berta', 'Cilka', 'Dani']
Metoda split
v resnici vrne seznam.
vrstica = "Ana 72 1.70"
vrstica.split()
['Ana', '72', '1.70']
Seznam pa lahko priredimo bodisi eni spremenljivki
podatki = ["Ana", "72", "1.70"]
in v tem primeru se bo spremenljivka podatki
nanašala na ta seznam, bodisi trem,
ime, teza, visina = ["Ana", "72", "1.70"]
pa bomo dobili tri spremenljivke.
Če ne vemo, koliko jih bo, lahko preverimo dolžino seznama. Dobimo jo s funkcijo (ne metodo!) len
.
len(podatki)
3
Če imamo, recimo, seznam, ki se vedno začne z imenom in težo, naprej pa ne vemo, kaj bi utegnilo slediti, lahko napišemo
ime = podatki[0]
teza = podatki[1]
Izpisati želimo tri gene, katerih sekvence vsebuejo z največ a-jev.
Očitno bi lahko razširili program, ki išče gen z največ a-ji - vendar bi bilo kar zoprno. Druga pot je, da sestavimo, hm, seznam števil a-jev.
ajev = []
for vrstica in open("datoteke/qwerty-dna.txt"):
ime, zaporedje = vrstica.split()
ajev.append(zaporedje.count("a"))
ajev.sort()
print(ajev[-3:])
[29, 31, 32]
Metoda sort
uredi po velikosti, mi pa hočemo največje tri in ti so na koncu, torej bomo vzeli zadnje tri, [-3:]
, se pravi, vse od minus tretjega do konca.
Eh, no, ker velikokrat želimo urejati v rikverc, ima sort
še argument, ki ga moramo (za razliko od tega, kar smo počeli doslej), podati z imenom, takole.
ajev.sort(reverse=True)
ajev[:3]
[32, 31, 29]
Vse lepo in prav, ampak nas ne zanimajo števila a-jev, temveč za katere tri gene gre. Zoprna reč. Urejati moramo po številu a-jev, obenem pa ne izgubiti imen.
Pa naredimo nekaj hecnega: namesto seznama števil a-jev, sestavimo seznam, ki bo vseboval sezname-pare, s številom a-jev in imenom gena.
ajev = []
for vrstica in open("datoteke/qwerty-dna.txt"):
ime, zaporedje = vrstica.split()
ajev.append([zaporedje.count("a"), ime])
ajev
[[26, 'ASDF13'], [31, 'SDFG14'], [26, 'DFGH15'], [19, 'FGHJ16'], [32, 'GHJK17'], [29, 'HJKL18'], [24, 'ZXCV19'], [21, 'XCVB20'], [24, 'CVBN21'], [27, 'VBNM22']]
Pozorno poglejte, kaj smo dali kot argument append
-u: podali smo mu seznam, [zaporedje.count("a"), ime]
.
Zdaj to uredimo s sort
, tako kot prej. Kako pa sort
primerja par seznamov? Najprej pogleda prva elementa. Če sta različna, je "manjši" tisti seznam, ki ima "manjši" prvi element. Če sta enaka, pogleda drugi element. In tako naprej.
ajev.sort(reverse=True)
ajev
[[32, 'GHJK17'], [31, 'SDFG14'], [29, 'HJKL18'], [27, 'VBNM22'], [26, 'DFGH15'], [26, 'ASDF13'], [24, 'ZXCV19'], [24, 'CVBN21'], [21, 'XCVB20'], [19, 'FGHJ16']]
Zdaj vzamemo prve tri elemente. Prehodimo jih z zanko for
in izpišemo njihova imena.
naj_tri = ajev[:3]
for par in naj_tri:
a, ime = par
print(ime)
GHJK17 SDFG14 HJKL18
Gre tudi hitreje. Najprej: seznama naj_tri
niti ne potrebujemo. Prvi tri elemente lahko pograbimo kar mimogrede.
for par in ajev[:3]:
a, ime = par
print(ime)
GHJK17 SDFG14 HJKL18
Pa tudi spremenljivke par
ne potrebujemo: v a
in ime
jo lahko razbijemo kar v vrstici s for
:
for a, ime in ajev[:3]:
print(ime)
GHJK17 SDFG14 HJKL18
Da bo pregledneje, zberimo vse skupaj.
ajev = []
for vrstica in open("datoteke/qwerty-dna.txt"):
ime, zaporedje = vrstica.split()
ajev.append([zaporedje.count("a"), ime])
ajev.sort(reverse=True)
for a, ime in ajev[:3]:
print(ime)
GHJK17 SDFG14 HJKL18
Za konec omenimo še žlahtnika seznamov, terko (angl. tuple). Terke se od seznamov razlikujejo po tem, da jih ne pišemo z oglatimi oklepaji temveč z okroglimi.
podatki = ("Ana", 72, 1.70)
Seznami običajno vsebujejo same enake stvari - imeli smo seznam imen, seznam tež, seznam genov. Terke bodo navadno vsebovale različne stvari, kot recimo gornja, ki ima ime (niz) ter težo in višino (števili).
Ta razlika je bolj kozmetična in ne bi bila vredna svojega podatkovnega tipa. Pomembnejša lastnost terk je, da jih ne moremo spreminjati. Terke imajo vse metode seznamov, ki ničesar ne spremenijo; manjka jim, recimo append
. Poleg tega terke ne moremo spreminjati z indeksiranjem.
podatki[0] = "Ančka"
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-47-421cb9f87815> in <module>() ----> 1 podatki[0] = "Ančka" TypeError: 'tuple' object does not support item assignment