Re: [python] Modifikace seznamu bez kopirovnani (byl o SQLite - forma selectovaných dat)

2007-01-08 Tema obsahu superman
 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)

2007-01-08 Tema obsahu superman
 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)

2007-01-08 Tema obsahu Jan Matejka
 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)

2007-01-08 Tema obsahu Pavel Kosina
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)

2007-01-08 Tema obsahu Petr Prikryl
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)

2007-01-08 Tema obsahu Petr Prikryl
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)

2007-01-08 Tema obsahu Jan Matejka
 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)

2007-01-08 Tema obsahu superman
 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)

2007-01-07 Tema obsahu Petr Prikryl
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