![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Entice (tuple
) je neměnitelný indexovaný seznam hodnot, oddělených čárkou:
>>> tuple = 'a', 'b', 'c', 'd', 'e'
Zápis na pravé straně vyhodnotí interpret jako entici (tuple) a jako takovou ji vrátí:
>>> tuple ('a', 'b', 'c', 'd', 'e')
Při tvorbě entice s jedinou položkou musíme připojit závěrečnou čárku:
>>> t1 = 'a',lépe t1 =("a",) >>> t1; type(t1) ('a',) <class 'tuple'>
Bez čárky by byl výraz 'a' považován překladačem Pythonu za řetězec.
>>> t2 = ('a') >>> t2; type(t2) 'a' <class 'str'>
Odhlédneme-li od skladby, jsou operace s enticemi stejné jako operace se seznamy. Indexový operátor vybere položku z entice:
>>> tuple = ('a', 'b', 'c', 'd', 'e') >>> tuple[0] 'a'
Úsekový operátor vybere rozsah položek:
>>> tuple[1:3] ('b', 'c')
Pokusíme-li se změnit některou z indexem vybraných položek, dostaneme chybové hlášení:
>>> tuple[0] = 'A' TypeError: 'tuple' object doesn't support item assignment
Neměnitelnost entice znamená, že ji nelze změnit beze změny identity (ID). Pokud nám nevadí změna ID, můžeme entici - jako přiřazenou hodnotu k proměnné, upravit pomocí součtu entic a úsekového operátoru:
>>> tuple = ('A',) + tuple[1:] >>> tuple 'A', 'b', 'c', 'd', 'e') >>> ruple = ('a', 'b', 'c', 'd', 'e'); id(ruple) 56396448 >>> ruple = ruple[:2] + ("C",) + ruple[3:] >>> ruple; id(ruple) ('a', 'b', 'C', 'd', 'e') 63672720
Pokud entice obsahuje vložený měnitelný element typu list a dict (nikoliv tuple a set), lze tento prvek změnit obvyklým způsobem a kopírovat metodou copy a deepcopy z modulu copy:
>>> import copy >>> lt = ("a", [2.5, "b"]) # vložený seznam# id(lt) --> 60715336 >>> slt = copy.copy(lt) # závislá kopie >>> slt[1][1] = "joj"; lt, slt (('a', [2.5, 'joj']), ('a', [2.5, 'joj']))# id(lt), id(slt) --> (60715336, 60715336) >>> lt = ("a", [2.5, "b"])# id(lt) --> 67797224 >>> dlt = copy.deepcopy(lt) # nezávislá kopie >>> dlt[1][1] = "hoj"; lt, dlt (('a', [2.5, 'b']), ('a', [2.5, 'hoj']))# id(lt), id(dlt) --> (67797224, 67797384)
Při úpravě vloženého slovníku musíme zadávat nikoliv index, nýbrž klíč:
>>> import copy >>> dt = ("a", {2: "b"}) # vložený slovník# id(dt) --> 54882600 >>> sdt = copy.copy(dt) # závislá kopie >>> sdt[1][2] = "číč"; dt, sdt (('a', {2: 'číč'}), ('a', {2: 'číč'}))# id(dt), id(sdt) --> (54882600 54882600) >>> dt = ("a", {2: "b"})# id(dt) --> 58810760 ) >>> ddt = copy.deepcopy(dt) # nezávislá kopie >>> ddt[1][2] = "rýč"; dt, ddt >>> (('a', {2: 'b'}), ('a', {2: 'rýč'}))# id(dt), id(ddt) --> (58810760 58810920)
Závislou kopii jak prosté, tak složené entice lze rovněž vytvořit úsekovým operátorem [:]:
>>> tup = ('a', 'b', 'c') >>> rup = tup[:]; rup ('a', 'b', 'c') >>> id(tup), id(rup) (46444232, 46444232) dt = ("a", {2: "b"}) >>> sdt = dt[:]; sdt ('a', {2: 'b'}) >>> id(dt), id(sdt) (46497384, 46497384)
Vestavěná metoda tpl.copy() pro entice nechodí.
Pojmenovaná entice (named tuple) je normální entice, opatřená jménem a názvy položek. Prvky entice jsou tedy kromě indexů přístupné také přes slovní označení.
Práce s pojmenovanou enticí připomíná práci s deklarovanou třídou a jejími instancemi - viz Kap.12. Třídou je zde "matriční" entice, vytvořená pomocí importované funkce namedtuple() z modulu collections:
Název_typu = namedtuple('Název_typu', 'výpis polí' [, rename=False] [, defaults=None] [, module=None])
Z takto obecně deklarované entice (třídy) vytvoříme jednotlivé entice (instance) s konkrétními hodnotami příslušných položek. Užitečnost pojmenované entice spočívá v tom, že si nemusíme pamatovat význam jednotlivých polí.
Práci s pojmenovanou enticí si nejlépe ukážeme na konkrétním příkladě, ve kterém si vytvoříme kartotéku se jmény zaměstnanců, jejich obory a platy (převzato ze stránky DaniWeb s laskavým svolením autora):
# ntEmpRec.py from collections import namedtupleVytvoření vzorové entice s určením polí (atributů):<název_NT> = namedtuple(<název_NT>, <názvy polí>)jen dva argumenty EmpRec = namedtuple('EmpRec', 'name department salary') Vytvoření konkrétních entic(lze zadat poziční i pojmenované argumenty) <jméno_pole> = <název_NT>(hodnoty jednotlivých atributů) bob = EmpRec('Bob Zimmer', 'finance', salary = 77123) tim = EmpRec('Tim Bauer', 'shipping', 34231)Jiný způsob s použitím seznamu a metody _make:fred_list = ['Fred Flint', 'purchasing', 42350] fred = EmpRec._make(fred_list)
Jiný způsob s použitím slovníku a dvojhvězdičkového operátoru:fred_dict = {'name': 'Fred Flint', 'department': 'purchasing', 'salary': 42350} fred = EmpRec(**fred_dict)--> EmpRec{'name': 'Fred Flint', 'department': 'purchasing', 'salary': 42350} Vytvoření entice z existující entice metodou _replace:john = fred._replace(name='John Ward', salary=49200) Vytvoření implicitní entice pro dělníky s hodinovou mzdou default = EmpRec('addname', 'manufacturing', 26000) a její aplikace pro tvorbu konkretních entic mike = default._replace(name='Mike Holz') gary = default._replace(name='Gary Wood') carl = default._replace(name='Carl Boor')Převedení entice na slovník metodou _asdict():print(bob._asdict())Výpis polí entice přes název entice či vzorové entice:print(bob._fields) print(EmpRec._fields)
=== RESTART: F:\Codetest\HowTo\ch-10\in-text\ntEmpRec.py === {'name': 'Bob Zimmer', 'department': 'finance', 'salary': 77123} ('name', 'department', 'salary') ('name', 'department', 'salary') Přístup k polím přes jméno entice a atributu >>> print(bob.name, bob.salary) Bob Zimmer 77123 Přístup k polím přes jméno entice a index atributu >>> print(tim[0], tim[2]) Tim Bauer 34231
Vytvoření seznamu pojmenovaných entic:
# ntEmpRec.py selektivně upravené ... print('-'*40) emp_list = [bob, fred, tim, john, mike, gary, carl] for emp in emp_list: print( "%-15s works in %s" % (emp.name, emp.department) ) print('-'*40)
== RESTART: F:/Codetest/HowTo/ch-10/in-text/listEmpRec.py == ---------------------------------------- Bob Zimmer works in finance Fred Flint works in purchasing Tim Bauer works in shipping John Ward works in purchasing Mike Holz works in manufacturing Gary Wood works in manufacturing Carl Boor works in manufacturing ----------------------------------------
Pro výpis polí používá Python interně metodu split():
>>> "name department salary".split() ['name', 'department', 'salary']
Objekt EmpRec lze tedy alternativně deklarovat takto:
>>> EmpRec = namedtuple('EmpRec', ['name', 'department', 'salary'])Případně: >>> EmpRec = namedtuple('EmpRec', ('name department salary')) >>> type(EmpRec) <class 'type'># vida, ona je to třída
Kromě prezentovaných metod _replace(), _make(), _asdict(), _fields podporuje named tuple stejné metody jako regulérní typ tuple - min(), max(), len(), in, not in, concatenation, index, slice, atd.
Aktuálně platné parametry funkce namedtuple - rename, defaults a module viz namedtuple() v anglické dokumentaci.
Hromadné přiřazení (multiple assignment) umožňuje přiřadit několika proměnným příslušné hodnoty na jediném řádku.
Víme již, že entici bez závorek akceptuje interpret jako entici se závorkami:
>>> x, y = 10, 20# --> (x, y) = (10, 20) >>> x, y (10, 20)
Popsaný způsob přiřazení lze použít nejen u entic ale i u dalších 'iteráblů' - slovníků [], setů {} a řetězců " ".
>>> a, b = [12, 22] >>> c, d = {14, 24} >>> e, f = "Hi" >>> a,b, c,d, e,f (12, 22, 24, 14, 'H', 'i')
Rozbalení entice použijeme výhodně při záměně přiřazení:
>>> x, y = 10, 20 >>> x, y = y, x >>> x, y (20, 10)
Při rozbalování entice lze použít pomocnou proměnnou:
>>> ent = "m", 2, False >>> x,y,z = ent >>> z,y,x (False, 2, 'm')Počet prvků po obou stranách přiřazení musí být shodný.
Funkce mohou vracet entici jako výstupní hodnotu. Můžeme například napsat funkci, která zamění dva parametry:
def swap(x, y): return y, x
Při používání tété funkce je zapotřebí jisté ostražitosti:
>>> a, b = 10,5 >>> swap(a, b) (5,10) # dostali jsme co jsme chtěli >>> a,b (10,5) # vstupní hodnoty jsme ale nezměnili >>> a,b = swap(a, b) # musíme o to výslovně požádat >>> a,b (5,10)
Enticové přiřazení uvnitř funce swap se v prostoru __main__ neprojeví.
Tato funkce případně neprovede to, co jsme si přáli. To je příklad sémantické (významové) chyby.
Přehlednější ukázkou použití entice pro výstupní hodnoty funkce je tento příklad:
from math import pi def kruh(r): c = 2 * pi * r# obvod kruhu a = pi * r * r# plocha kruhu return (r, c, a)
V kapitole 3.5 a 3.7 jsme si povídali o čistých funkcích a modifikátorech v souvislosti se seznamy. Pro entice modifikátory psát nemůžeme, protože entice jsou samy o sobě neměnitelné.
Zde máme modifikátor, který vloží novou hodnotu val do středu seznamu lst. Text kódu si uložíme do souboru
# pureTriTools.py def insert_in_middle_lst(val, lst): middle = int(len(lst)/2) lst[middle:middle] = [val]
>>> my_list = ['a', 'b', 'd', 'e'] >>> insert_in_middle_lst('c', my_list) >>> my_list ['a', 'b', 'c', 'd', 'e']
Zkusíme-li to s enticí, dostaneme chybu. Problém je ten, že entice jsou neměnitelné a nepodporují úsekové přiřazení. Jednoduchým řešením je udělat z insert_in_middle_lst
čistou funkci:
def insert_in_middle_tup(val, tup): middle = int(len(tup)/2) return tup[:middle] + (val,) + tup[middle:]
Tato verze nyní chodí pro entice, ne však pro seznamy a řetězce.
>>> my_tuple = ('a', 'b', 'd', 'e') >>> insert_in_middle_tup('c', my_tuple) ('a', 'b', 'c', 'd', 'e')
Chceme-li verzi pro všechny sekvenční typy, potřebujeme zapouzdřit naši hodnotu do správného sekvenčního typu. Malá pomocná funkce vykoná div:
def encapsulate(val, seq): if type(seq) == type(""): return str(val) if type(seq) == type([]): return [val] return (val,)
Nyní můžeme napsat insert_in_middle tak, aby pracovala s každým vestavěným typem sekvence:
def insert_in_middle(val, seq): middle = int(len(seq)/2) return seq[:middle] + encapsulate(val, seq) + seq[middle:]
Poslední dvě verze insert_in_middle jsou čisté funkce. Nemají žádné postranní účinky. Přidáme-li encapsulate a poslední verzi insert_in_middle do modulu pureTriTools.py, můžeme to vyzkoušet:
>>> from pureTriTools import * >>> my_string = 'abde' >>> insert_in_middle('c', my_string) 'abcde' >>> my_list = ['a', 'b', 'd', 'e'] >>> insert_in_middle('c', my_list) ['a', 'b', 'c', 'd', 'e'] >>> my_tuple = ('a', 'b', 'd', 'e') >>> insert_in_middle('c', my_tuple) ('a', 'b', 'c', 'd', 'e') >>> my_string 'abde'
Hodnoty my_string, my_list, a my_tuple se nezměnily.
Set (množina) je neuspořádaná měnitelná kolekce odlišných prvků, frozenset je neměnitelná kolekce odlišných prvků. Používají se při testování "členství" a při eliminaci duplikátních zápisů.
Kolekce set a frozenset podporují množinové operace sjednocení
(union), průnik
(intersection), rozdíl
(difference) a doplněk
(symmetric_difference), funkci len(s)
a idiomy x in s, x not in s
.
Set vytvoříme buď výčtem nebo pomocí funkce set(), frozenset vytvoříme pouze funkcí frozenset(). Obě funkce přijímají pouze jeden argument, jímž může být seznam, řetězec, entice, částečně i slovník. Pozice jednotlivých prvků není indexována (
>>> basket ={
'apple','orange','apple','pear','orange','banana'}
>>> basket {'orange', 'apple', 'banana', 'pear'} # žádné duplikáty! >>> tup = (1,2,3,"alpha",2) >>> st = set(tup) >>> st {1, 2, 3, 'alpha'} # žádné duplikáty! >>> fr = frozenset('alcatraz') >>> fr frozenset({'a', 'c', 'l', 'r', 't', 'z'}) # žádné duplikáty!
Použijeme-li jako argument pro funkce set a frozenset slovník, použijí se pouze hodnoty jeho klíčů:
>>> dic = {"a": 1, "b": 2, "c": 3} >>> set_dic = set(dic) >>> set_dic {'b', 'c', 'a'}
Set i frozenset mají definovanou částečně společnou řadu metod:
set_meth = add, clear, copy, difference, difference_update, discard, intersection, intersection_update, isdisjoint, issubset, issuperset, pop, remove, symmetric_difference, union, update, symmetric_difference_update frozenset_meth = copy, difference, intersection, isdisjoint, issubset, issuperset, symmetric_difference, union
Objekt typu set nepodporuje změnu položky. Obsah setu lze nicméně měnit metodami add, clear, discard, pop, remove a update. Set i frozenset jsou rovněž vybaveny funkcí next(~) a metodou .__next__(), jsou to tedy iterábly.
Ukážeme si některé operace na setech:
>>> a = set('abracadabra') >>> b = set('alacazam') >>> a, b# zápis entice ({'a', 'c', 'b', 'r', 'd'}, {'a', 'c', 'z', 'm', 'l'}) >>> a - b# rozdíl {'r', 'd', 'b'}# b-a --> {'z', 'm', 'l'} >>> a | b# sjednocení {'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'} >>> a & b# průnik {'a', 'c'} >>> a ^ b# doplněk {'r', 'd', 'b', 'm', 'z', 'l'}
Stejně jako pro seznam lze i pro set definovat komprehenci setu:
>>> s = {v for v in "ABCDABCD" if v not in "CB"} >>> s >>> {'A', 'D'}
Zápisy (), [], {} označují práznou entici, seznam a slovník; zápisy set() a frozenset() (nikoliv {}) označují prázdný set a frozenset.
Funkce zip(*iterables)
přijímá jednu či více sekvencí a kombinuje jejich položky do řady entic uvnitř interního iterátorového objektu.
Tento výsledný seznam či entice je iterátorem, jenž je po použití prázdný a před případným opakovaným použitím se musí opětovně deklarovat.
>>> izip = zip([1, 2, 3, 4], "postel")# sekvencí může být i více >>> izip <zip object at 0x000002194D5E0B40># interní iterátorový objekt >>> list(izip)# použití iterátoru [(1, 'p'), (2, 'o'), (3, 's'), (4, 't')] >>> set(izip)# iterátor je prázdný! set() >>> set(zip([1, 2, 3, 4], "postel"))# použití s deklarací {(4, 't'), (3, 's'), (2, 'o'), (1, 'p')} >>> tuple(zip([1, 2, 3, 4], "postel"))# použití s deklarací ((1, 'p'), (2, 'o'), (3, 's'), (4, 't')) >>> dict(zip([1, 2, 3, 4], "postel"))# použití s deklarací {1: 'p', 2: 'o', 3: 's', 4: 't'}
Počet entic ve výsledném iterátoru je dán délkou nejkratšího argumentu funkce zip. Zazipované sekvence lze opět odzipovat:
nl = [1, 2, 3] sl = ['one', 'two'] nt = ('ONE', 'TWO', 'THREE', 'FOUR') izip = zip(nl, sl, nt) result_list = (list(izip)) print("result_list =",result_list) nl, sl, nt = zip(*result_list)# sběrný parametr print("nl =",nl, "sl =",sl, "nt =",nt)
========== RESTART: F:/Codetest/python/zip_test.py ========= result_list = [(1, 'one', 'ONE'), (2, 'two', 'TWO')] nl = (1, 2) sl = ('one', 'two') nt = ('ONE', 'TWO')
Proměnné nl, sl, nt změnily trvale svou hodnotu.
SeqTools je sada knihoven pro manipulaci se sekvenčními daty typu list, tuple, range, bytes a bytearray . Velice pěkné seznámení s tímto nástrojem nalezneme na stránce SeqTools.
Zde se seznámíme s řadou metod, které lze s příslušným importem využívat.
Předtím je nutné si tento nástroj instalovat:
pip install seqtoolsJak prosté, milý Watsone!
Oprašte svou angličtinu a naučte se pracovat s nástrojem SeqTools.
def make_empty(seq): """ >>> make_empty([1, 2, 3, 4]) [] >>> make_empty(('a', 'b', 'c')) () >>> make_empty("No, not me!") '' """ def insert_at_end(val, seq): """ >>> insert_at_end(5, [1, 3, 4, 6]) [1, 3, 4, 6, 5] >>> insert_at_end('x', 'abc') 'abcx' >>> insert_at_end(5, (1, 3, 4, 6)) (1, 3, 4, 6, 5) """ def insert_in_front(val, seq): """ >>> insert_in_front(5, [1, 3, 4, 6]) [5, 1, 3, 4, 6] >>> insert_in_front(5, (1, 3, 4, 6)) (5, 1, 3, 4, 6) >>> insert_in_front('x', 'abc') 'xabc' """def index_of(val, seq, start=0): """ >>> index_of(9, [1, 7, 11, 9, 10]) 3 >>> index_of(5, (1, 2, 4, 5, 6, 10, 5, 5)) 3 >>> index_of(5, (1, 2, 4, 5, 6, 10, 5, 5), 4) 6 >>> index_of('y', 'happy birthday') 4 >>> index_of('banana', ['apple', 'banana', 'cherry', 'date']) 1 >>> index_of(5, [2, 3, 4]) -1 >>> index_of('b', ['apple', 'banana', 'cherry', 'date']) -1 """def remove_at(index, seq): """ >>> remove_at(3, [1, 7, 11, 9, 10]) [1, 7, 11, 10] >>> remove_at(5, (1, 4, 6, 7, 0, 9, 3, 5)) (1, 4, 6, 7, 0, 3, 5) >>> remove_at(2, "Yomrktown") 'Yorktown' """def remove_val(val, seq): """ >>> remove_val(11, [1, 7, 11, 9, 10]) [1, 7, 9, 10] >>> remove_val(15, (1, 15, 11, 4, 9)) (1, 11, 4, 9) >>> remove_val('what', ('who', 'what', 'when', 'where', 'why', 'how')) ('who', 'when', 'where', 'why', 'how') """def remove_all(val, seq): """ >>> remove_all(11, [1, 7, 11, 9, 11, 10, 2, 11]) [1, 7, 9, 10, 2] >>> remove_all('i', 'Mississippi') 'Msssspp' """ def count(val, seq): """ >>> count(5, (1, 5, 3, 7, 5, 8, 5)) 3 >>> count('s', 'Mississippi') 4 >>> count((1, 2), [1, 5, (1, 2), 7, (1, 2), 8, 5]) 2 """ def reverse(seq): """ >>> reverse([1, 2, 3, 4, 5]) [5, 4, 3, 2, 1] >>> reverse(('shoe', 'my', 'buckle', 2, 1)) (1, 2, 'buckle', 'my', 'shoe') >>> reverse('Python') 'nohtyP' """ def sort_sequence(seq): """ >>> sort_sequence([3, 4, 6, 7, 8, 2]) [2, 3, 4, 6, 7, 8] >>> sort_sequence((3, 4, 6, 7, 8, 2)) (2, 3, 4, 6, 7, 8) >>> sort_sequence("nothappy") 'ahnoppty' """ if __name__ == "__main__": import doctest doctest.testmod()
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |