Ennik (tuple)

Ennik (ingl tuple), vahel ka nimetusega korteež, rida või järjend, on andmetüüp, mis on jada Pythoni objekte. Ennik on väga sarnane järjendile (list), kuid erinevalt järjendist on ta muutumatu (immutable).

Loomine:

Enniku loomiseks tuleb elemendid eraldada komadega ja panna ümarsulgude vahele. Elementide arv pole piiratud ja elemendid võivad olla erinevat andmetüüpi - näiteks täisarv (int), ujukomaarv (float), sõne (str), järjend (list) jne.

Programmis kasutatakse enniku kirjeldamiseks ümarsulge "(..., ...)" (erinevalt listist, kus oli kandilised sulud "[..., ...]"). Elementide indekseerimiseks aga kasutatakse siiski nurksulge.

Enniku asemel saaks alati kasutada järjendit, aga heaks tavaks on kasutada järjendeid vaid neil juhtudel, kus kogumi elemendid on kõik ühte tüüpi: näiteks [1, 2, 3, ...] või ["hello", "world!"]. Ennikuid võiks eelistada siis, kui meil on mingi kindel komplekt elemente, mida me tahame koos käsitleda, kusjuures iga element võib olla erinevat tüüpi.

tup0 = ()  # empty tuple
tup1 = (1,)  # need to use a comma with one element
tup2 = (1, 2)
tup3 = ("hello", 123, "abc")  # different types
tup4 = ("wasd", tup3, 777.2)  # -> ('wasd', ('hello', 123, 'abc'), 777.2)

# parentheses can be omitted, but it's safer to use them
tup5 = 1,  # use of comma!
tup6 = 1, 3, 5

# tuple of list
tup7 = tuple([4])  # -> (4,)
tup8 = tuple(["Boeing", 737, "Cessna", 210])  # -> ("Boeing", 737, "Cessna", 210)

# list of tuple
list1 = list(tup7)  # -> [4]
list2 = list(tup8)  # -> ["Boeing", 737, "Cessna", 210]
empty = ()
singleton = 'hello',    # <-- note trailing comma
print(len(empty))  #0
print(len(singleton))  #1
print(singleton)  #('hello',)

Võrdlus järjendiga:

Ennik erineb järjendist (list) peamiselt selle poolest, et see on muutumatu (immutable), mis tähendab, et ennikule ei saa lisada, üle kirjutada või eemaldada elemente. Seetõttu puuduvad paljud meetodid, mis on olemas järjendil (näiteks elementide lisamise meetodid append ja extend või elementide eemaldamise meetodid remove ja pop).

Ennikud on pisut kiiremad kui järjendid ning nende kasutamine aitab muuta koodi selgemaks ja arusaadavamaks tagades selle, et andmeid ei saa muuta. Kui kasutad konstantseid väärtuseid, siis eelista võimalusel ennikut järjendile.

aList = [4, 2]
aList[0] = 1
print(aList)  # -> [1, 2]

aTuple = (4, 2)
aTuple[0] = 1  # elemente ei saa üle kirjutada -> TypeError: 'tuple' object does not support item assignment

Ennikut saab kasutada sõnastiku (dictionary) võtmena (key), kuid järjendit ei saa, sest järjend on muutuv (mutable).

dict1 = {(2, 3): "abc"}  # ennik sobib sõnastiku võtmeks
dict2 = {[2, 3]: "abc"}  # -> TypeError: unhashable type: 'list'

Nii loendit kui ka ennikut saab määrata vastavalt omanimelise funktsiooniga.

arvud = tuple(range(10))
tekst = tuple('python')
print(arvud)                        # (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
print(tekst)                        # ('p', 'y', 't', 'h', 'o', 'n')

Juurdepääs elementidele ja operatsioonid ennikutel:

Ennikus olevaid elemente saab kätte indeksi järgi [] operaatoriga. Indekseerimine algab 0-st.

a_tuple = ("January", "February", "March")
# index        0          1          2
print(a_tuple[0])  # -> January
print(a_tuple[2])  # -> March

# ennik sõnest töötab sarnaselt järjendile, lahutades sõne tähtedeks
b_tuple = tuple("Technology")  # -> ('T', 'e', 'c', 'h', 'n', 'o', 'l', 'o', 'g', 'y')
print(b_tuple[0])  # -> T
print(b_tuple[4])  # -> n

# indeks peab olema int tüüpi ja vahemikus 0 kuni enniku pikkus - 1, viimane element on indeksil len(b_tuple) - 1
# print(b_tuple[10])  # -> IndexError: tuple index out of range

Python lubab kasutada negatiivset indekseerimist nii järjendites kui ka ennikutes. -1 viitab viimasele elemendile, -2 eelviimasele jne.

c_tuple = ("n", "i", "c", "e")
print(c_tuple[-1])  # -> e
print(c_tuple[-2])  # -> c

Tükeldamise (slice) abil saab ligi kindlale vahemikule ennikus, sarnaselt paljudele teistele andmetüüpidele. Tükeldamine toimub kasutades koolonit: [start:stop:step].

  • [start] indeks, kust alustada tükeldamist; selle ärajätmisel eeldab Python, et tükeldamine algab algusest

  • [stop] elemendi indeks, kus peatuda, kusjuures seda elementi ei arvata vahemikku; vaikimisi viimase elemendini

  • [step] sammu suurus itereerimisel, vaikimisi on see 1; -1 itereerib elemendid tagurpidi

c_tuple = ("a", "b", "c", "d")

# range of elements between index 0 and index 2, element at index 2 is excluded
print(c_tuple[0:2])  # -> ('a', 'b')

# [start] can be omitted
print(c_tuple[:2])  # -> ('a', 'b')

print(c_tuple[2:4])  # -> ('c', 'd')

# [stop] can be omitted
print(c_tuple[2:])  # -> ('c', 'd')

# slicing from start to end, easy way to make a copy
print(c_tuple[:])  # -> ("a", "b", "c", "d")

# by choosing [step] to be -1, we get the reverse of original
print(c_tuple[::-1])  # -> ('d', 'c', 'b', 'a')

Enniku puhul saab kasutada in operaatorit, et kontrollida elemendi olemasolu ennikus.

tup = (123, "hello", 999)
b = "hello" in tup
print(b)  # -> True

Enniku muutmine:

Kuna ennik on erinevalt järjendist muutumatu, siis pole võimalik enniku elemente pärast esialgset määramist muuta. Erandiks on olukord, kus ennik sisaldab järjendit, mis võib muutuda.

a_list = [0, 1, 2]
x_tuple = (3, a_list, 4)
a_list.append(9)
print(x_tuple)  # -> (3, [0, 1, 2, 9], 4)

Sama muutujat saab asendada, esialgne: y_tuple = (5, 2) asendamine: y_tuple = (3, 3)

Kuna elemente ei saa ennikus eemaldada, siis on enniku kustutamiseks olemas võtmesõna del.

my_tuple = (1, 2)
del my_tuple
print(my_tuple)  # -> NameError: name 'my_tuple' is not defined

Ennikute uuendamiseks võib osaliselt kasutada vanu väärtusi.

tup0 = ("a", "b")
tup1 = (1,)
tup3 = tup0 + tup1
print(tup3)  # -> ('a', 'b', 1)
tup4 = tup3 * 3
print(tup4)  # -> ('a', 'b', 1, 'a', 'b', 1, 'a', 'b', 1)

Meetodid:

count(x) - Tagastab arvu, mis näitab, mitu elementi on võrdsed x-ga.

index(x) - Tagastab esimese elemendi indeksi, mis on võrdne x-ga.

print(tuple("Tallinn University of Technology").count("l"))  # -> 3
print(tuple("Tallinn University of Technology").index("y"))  # -> 17

Ennikutega kasutatakse tihti mitmeid Pythoni sisse ehitatud funktsioone. Nende kohta saab lugeda siit: https://docs.python.org/3/library/functions.html.

Kasutamine:

Pythonis kasutatakse tihti ennikut muutujate pakkimiseks:

fruits = "apple", "orange", "pear"  # tuple packing
f0, f1, f2 = fruits                 # tuple unpacking
# f0 -> apple
# f1 -> orange
# f2 -> pear

cities = "London", "Ottawa", "Bristol", "New York", "Oslo"
uk, ca, *other_cities, nor = cities  # the * operator just before a variable takes logically rest of the values
# other_cities -> ['Bristol', 'New York']

uk2, *other_cities2 = cities
# other_cities2 -> ['Ottawa', 'Bristol', 'New York', 'Oslo']

Ennikut on hea kasutada, kui on vaja, et funktsioon tagastaks mitu väärtust.

def hello():
    return "Hi", "Tom"  # looks like several values are returned, actually it's just a tuple without parentheses

print(type(hello()))  # <class 'tuple'>

h = hello()[0]
t = hello()[1]
# one liner with tuple unpacking -> h, t = hello()
# h = "Hi", t = "Tom"