Re: [python] Modifikace seznamu bez kopirovnani (byl o SQLite - forma selectovaných dat)
Iterace není read only. Read only jsou zpřístupňované objekty. Iterace je read only vzhledem k iterovanému objektu, což je občas dost nešikovné. Od Python 2.3 existuje standardní funkce enumerate(), která vrací iterátor. Jeho metoda next() vrací dvojici (index, element). Takže původní kód a = [(1,2),(3,4)] for i in range(len(a)): a[i] = list(a[i]) Můžu přepsat na a = [ (1, 2), (3, 4) ] for i, elem in enumerate(a): a[i] = list(elem) Obávám se, ale že ten můj kód, tedy první verze bude rychlejší. Jasně je druhou otázkou zda jde o high level konstrukce, nebo o rychlost. Tohle je problém v rozdílném pojetí homogenního pole (všechny prvky mají stejný typ) a pythonovského seznamu, který je svým způsobem sice taky homogenní, ale obsahuje jen reference, které se automaticky dereferencují (když už musím použít tak pěkně české slovo). Ano tohle je pro mě vždycky problém přepnout do myšlení dynamických jazyků, kde neexistují hodnoty, ale jen odkazy. V Pythonu nemohu měnit hodnotu prvku typu int, protože jde o objekt s konstantní hodnotou (immutable). Mohu jej pouze nahradit jiným objektem typu int, který je vypočtený z hodnoty původního a z další konstanty. V seznamu tedy musím opět použít obrat, kdy modifikuji samotný prvek seznamu. V tomto případě ale dojde k modifikaci každého prvku seznamu, takže nejefektivnější a nejstručnější způsob spočívá v konstrukci nového, upraveného seznamu: a = [ 1, 2, 3, 4, 5 ] a = [ e+3 for e in a ] print a V mém případě se ale nový seznam nekonstruoval, nedocházelo tedy k alokaci nového objektu. Navíc hodnoty typu int se nutně nemusejí znovu vytvářet, protože vnitřně je Python intepretr udělaný tak, že základní řada čísel je předalokovaná při startu interpretru a při použití běžných konstant se prostě jen předhodí odkaz na již uvnitř Python interpreteru existující objekt typu int. Kdyby se všechno mělo alokovat a znovu vytvářet, tak je Python slimák. Pokud bych definoval svou vlastní třídu objektů s celočíselnou hodnotou, které by mohly být modifikovány (mutable), bylo by možné modifikovat seznam bez vytváření nového. To jenom se snaží obejít read only vlastnost iterátoru. Jinak já jsem modifikoval seznam bez vytváření nového. Ing. Miloslav Ponkrác ___ Python mailing list Python@py.cz http://www.py.cz/mailman/listinfo/python
Re: [python] Modifikace seznamu bez kopirovnani (byl o SQLite - forma selectovaných dat)
V této souvislosti je zajímavé, že pro read-write je pomalejší varianta 1 než 2. Myslím, že je to způsobeno použitím dvou proměnných místo jedné, nebo malou optimalizací enumerate. Co je na tom divného? Pokud navíc budete vytvářet tuple pomocí enumerate, který je úplně zbytečný pro procházení polem, tak Vás to bude stát paměť i čas. Navíc musíte procházet tuple namísto toho abyste jednoduše přičítal jedničku k indexu, tedy další časová penalizace. Mě osobně by zajímal rychlostní rozdíl mezi použitím range a xrange. Ing. Miloslav Poknrác ___ Python mailing list Python@py.cz http://www.py.cz/mailman/listinfo/python
Re: [python] Modifikace seznamu bez kopirovnani (byl o SQLite - forma selectovaných dat)
Co je na tom divného? Pokud navíc budete vytvářet tuple pomocí enumerate, který je úplně zbytečný pro procházení polem, tak Vás to bude stát paměť i čas. Navíc musíte procházet tuple namísto toho abyste jednoduše přičítal jedničku k indexu, tedy další časová penalizace. Je to záležitost implementace iterovaného objektu. Pokud je realizován jako zřetězený seznam, tak nalezení prvku pomocí indexu (bez nějakých heuristických optimalizací) je časově mnohem náročnější než vrácení následujícího prvku. Jan Matějka ___ Python mailing list Python@py.cz http://www.py.cz/mailman/listinfo/python
Re: [python] Modifikace seznamu bez kopirovnani (byl o SQLite - forma selectovaných dat)
Petr Prikryl napsal(a): Čím je tato obava podložena? Tipnul bych, že k tomu není důvod. Naopak bych čekal, že ta druhá verze bude rychlejší. Dá se to změřit -- přenechávám iniciativu jiným. opak=10**6 import timeit def f(): a = [(1,2),(3,4)] for i in range(len(a)): a[i] = list(a[i]) t=timeit.Timer(f(),from __main__ import f) print f():, t.timeit(opak) def g(): a = [ (1, 2), (3, 4) ] for i, elem in enumerate(a): a[i] = list(elem) t=timeit.Timer(g(),from __main__ import g) print g():, t.timeit(opak) f(): 10.2249000921 g(): 9.58399237892 -- geon Pavel Kosina ___ Python mailing list Python@py.cz http://www.py.cz/mailman/listinfo/python
Re: [python] Modifikace seznamu bez kopirovnani (byl o SQLite - forma selectovaných dat)
Jan Matejka [...] 1) for v,i in enumerate(l1): l1[i]=v+1 6.1710381 Drobná chybička, která ale může ovlivňovat výsledek. Iterátor vracený funkcí enumerate() vrací dvojice (index, hodnota) a ne (hodnota, index). Je jasné, že použití enumerate bude pomalejší, než použití xrange(), protože se musí konstruovat navíc ta dvojice a navíc se pak musí rozdělávat na i, v. Taky je jasné, že varianta s xrange() bude rychlejší, než varianta s range(), protože se nemusí konstruovat pomocný seznam indexů. (Přestane to platit u Python 3000, kdy bude range() fungovat jako xrange(). pepr ___ Python mailing list Python@py.cz http://www.py.cz/mailman/listinfo/python
Re: [python] Modifikace seznamu bez kopirovnani (byl o SQLite - forma selectovaných dat)
superman [...] Mě osobně by zajímal rychlostní rozdíl mezi použitím range a xrange. Ten je asi takový jako rozdíl mezi zkonstruováním pomocného seznamu a následnou iterací přes jeho prvky (range) a přičítáním jedničky + testem na koncovou hodnotu (xrange). Časový rozdíl nemusí být výrazný, protože i velmi velké seznamy čísel typu integer asi Python vygeneruje rychle. Ale zbytečně se alokuje a dealokuje prostor pro pomocný seznam. Od Python 3000 se ale range stane xrange a xrange bude odstraněno. Předjímám námitku, že používám range, protože v budoucnu nebude nutné zasahovat do zdrojových textů a nahrazovat xrange slovem range, ale nesouhlasím s ní ;-) pepr ___ Python mailing list Python@py.cz http://www.py.cz/mailman/listinfo/python
Re: [python] Modifikace seznamu bez kopirovnani (byl o SQLite - forma selectovaných dat)
Drobná chybička, která ale může ovlivňovat výsledek. Iterátor vracený funkcí enumerate() vrací dvojice (index, hodnota) a ne (hodnota, index). děkuji for v,i in enumerate(l1): l1[i]=v+1 6.5163242 Je jasné, že použití enumerate bude pomalejší, než použití xrange(), protože se musí konstruovat navíc ta dvojice a navíc se pak musí rozdělávat na i, v. ano to vím Pokud by však platila teze, že čtení prvku seznamu pomocí indexu je pro velké seznamy pomalé, tak by varianta s enumerate mohla být rychlejší. Důvodem je to, že získává další hodnotu načtením následujícího prvku v seznamu (rychlá operace) namísto pomalého přístupu přes index. Test však ukázal, že uvedená tese neplatí, takže pythovský list() asi není obyčejný obousměrně svázaný seznam. Jan Matějka ___ Python mailing list Python@py.cz http://www.py.cz/mailman/listinfo/python
Re: [python] Modifikace seznamu bez kopirovnani (byl o SQLite - forma selectovaných dat)
Pokud by však platila teze, že čtení prvku seznamu pomocí indexu je pro velké seznamy pomalé, tak by varianta s enumerate mohla být rychlejší. Důvodem je to, že získává další hodnotu načtením následujícího prvku v seznamu (rychlá operace) namísto pomalého přístupu přes index. Test však ukázal, že uvedená tese neplatí, takže pythovský list() asi není obyčejný obousměrně svázaný seznam. A není ani důvod aby byl. Každý trochu zkušený programátor by určitě list neimplementoval vnitřně jako obousměrně vázaný seznam, protože tím získá prakticky jenom nevýhody. O vnitřní implementaci list napovídá to, že Python má tuple a list, tedy dva objekty dělající de facto to samé (pomiňte teď konstantnost tuple) a tipnul bych si tedy že spíše jde o to, že list alokuje paměť pro indexy dopředu, a že se jedná o obyčejné pole s empiricky vyladěnými algoritmy pro minimální počet alokací a trochu plýtvající pamětí, zatímco tuple bude vnitřně to samé co list, akorát alokuje paměť na indexy přesně. Jinak už jen úvaha, že přístup přes index je pomalejší, než cokoli jiného je úchylná, protože si fakt neumím představit, že by někdo implementoval pole (tj. něco s neměnnou lineární řadou celočíselných indexů) tak, aby přístup přes indexy nebyl superrychlý. Zvlášť když přístup k poli přes index je suverénně nejčastěji vyžadovaná operace v programu a tím pádem je logické, že je implmentována s ohledem na maximální rychlost. Ing. Miloslav Ponkrác ___ Python mailing list Python@py.cz http://www.py.cz/mailman/listinfo/python
[python] Modifikace seznamu bez kopirovnani (byl o SQLite - forma selectovaných dat)
superman JInak, proc pouzivate for i in range(len(a)): ? Uz jsem si toho vsimnul driv, u jinych prispevku. Preci, kdyz chci iterovat pres prvky, tak musi staci for item in a: [...] 2) Protože taková iterace je read only. Já můžu dát for item in a, ale už nezměním ten konkrétní prvek přímo v poli. [...] Existuje možnost jak to udělat bez indexování a bez toho, aby v paměti byly dočasně dvě pole? Iterace není read only. Read only jsou zpřístupňované objekty. Od Python 2.3 existuje standardní funkce enumerate(), která vrací iterátor. Jeho metoda next() vrací dvojici (index, element). Takže původní kód a = [(1,2),(3,4)] for i in range(len(a)): a[i] = list(a[i]) Můžu přepsat na a = [ (1, 2), (3, 4) ] for i, elem in enumerate(a): a[i] = list(elem) V původním kódu je navíc použito range(), které by mělo být alespoň nahrazeno xrange(). Další problém je v tom, že do původního řešení nemohu zasadit nekonečnou iterovatelnou strukturu. Podobný nedostatek má i řešení Lukáše Linharta data2 = [list(t) for t in data] které navíc provádí kopii celého seznamu. Na druhou stranu je velmi jednoduché a přehledné a záleží na tom, jaký problém chci ve skutečnosti řešit (jak velké jsou konvertované struktury a jak často chci konverzi provádět). Pokud bych chtěl být extrémista, pak lze při zpracování seznamu na místě samém (používá se pojem in situ) kombinovat enumerate() s testováním typu elementu a konverzi provádět jen pro elementy typu tuple: from types import * a = [ (1, 2), (3, 4) ] for i, elem in enumerate(a): if type(elem) is TupleType: a[i] = list(elem) V PHP existuje stejná iterace, dokonce si tam můžete udělat i vlastní iterátor, čehož jsem hojně využíval ve svých třídách a objektech. Ale v PHP je iterátor dvojí, jeden read only jako v Pythonu a druhý s možností změnit prvek pole, a ten mi v Pythonu chybí (a nebo o něm nevím). V Pythonu můžu rovněž definovat vlastní iterátor, který nejspíše nabude podobu generátoru. Pokud bych měl definován vlastní kontejner, pak od Python 2.2 mohu definovat i jeho vlastní iterátor dodefinováním metod __iter__() a next(). Tohle je problém v rozdílném pojetí homogenního pole (všechny prvky mají stejný typ) a pythonovského seznamu, který je svým způsobem sice taky homogenní, ale obsahuje jen reference, které se automaticky dereferencují (když už musím použít tak pěkně české slovo). Iterátor přes pythonovský seznam mě odkazuje na stejný objekt, na jaký odkazuje a[i], ale nemohu se přes něj dostat na samotný seznam a. Nemohu tedy změnit obsah seznamu. Nemohu tedy nahradit odkaz na n-tici odkazem na jiný objekt -- seznam. Následující kód v PHP docela elegantně iterací přičte ke každému prvku pole trojku, aniž by se musela vytvářet kopie pole: $a = array(1,2,3,4,5); foreach ($a as $value) $value += 3; V Pythonu nemohu měnit hodnotu prvku typu int, protože jde o objekt s konstantní hodnotou (immutable). Mohu jej pouze nahradit jiným objektem typu int, který je vypočtený z hodnoty původního a z další konstanty. V seznamu tedy musím opět použít obrat, kdy modifikuji samotný prvek seznamu. V tomto případě ale dojde k modifikaci každého prvku seznamu, takže nejefektivnější a nejstručnější způsob spočívá v konstrukci nového, upraveného seznamu: a = [ 1, 2, 3, 4, 5 ] a = [ e+3 for e in a ] print a Pokud bych definoval svou vlastní třídu objektů s celočíselnou hodnotou, které by mohly být modifikovány (mutable), bylo by možné modifikovat seznam bez vytváření nového. V C++ také existuje iterace, dokonce s možností projet jen část prvků daných iterátorem: Pro seznam lze v Pythonu pro tento účel využít slice, který vystupuje v roli části původního seznamu. pepr ___ Python mailing list Python@py.cz http://www.py.cz/mailman/listinfo/python