Chapter 2: The Python language
Jazyk Python
O Pythonu
Python je vyšší programovací jazyk všeobecného určení. Filozofie jeho stavby je zaměřena na programátorskou produktivitu a čitelnost kódu. Má minimalistickou základní syntaxi s jen několika základními příkazy a snadnou sémantikou, ale současně má rozsáhlou a výkonnou standardní knihovnu (standard library), včetně Aplikačního Programového Rozhraní (Application Programming Interface, API)
list
), vektory (tuple
), hash tabulky (slovníky, dict
), nebo třeba long integers (long
).Python má prvky pro podporu různých programovacích paradigmat, včetně objektově orientovaného (class
), imperativního (def
) a funkcionálního (lambda
) programování. Python má dynamické typování a automatickou správu paměti pomocí sledování počtu referencí (podobnou Perlu, Ruby, nebo Scheme).
Python byl prvně publikován Guido van Rossumem v roce 1991. Jazyk má otevřený vývojový model, založený na komunitě a spravovaný neziskovou Python Software Foundation. Je k dispozici mnoho interpreterů a překladačů, které implementují jazyk Python, včetně např. implementace v Javě (Jython), ale, v tomto rychlém náhledu se budeme držet implementace v C, kterou původně začal vytvářet Guido.
Na oficiální stránce python.org můžete najít mnohé tutoriály, oficiální dokumentaci a dokumentaci knihoven.[python]
Pro další informace o Pythonu můžeme doporučit knihy v ref.[guido] a ref.[lutz] .
Znáte-li už Python, můžete tuto kapitolu přeskočit.
Starting up
Binární distribuce Web2py pro Microsoft Windows nebo pro Apple OS X jsou baleny s Python interpreterem přímo v distribučních souborech.
Ve Windows jej můžete spustit následujícím příkazem (zapište do DOS prompt, který získáte příkazem cmd, přičemž je třeba změnit pracovní adresář na ten, v němž se nachází Web2py: cd .....\web2py):
web2py.exe -S welcome
Na Apple OS X zadejte do Terminálového okna následující příkaz (opět předpokládáme, že jste ve stejném adresáři jako web2py.app):
./web2py.app/Contents/MacOS/web2py -S welcome
V Linuxu nebo Unixu je pravděpodobné, že Python máte nezávisle nainstalovaný. Je-li tomu tak, napište na výzvu shellu:
python web2py.py -S welcome
Jestliže Python 2.5 (nebo pozdější 2.x) nemáte nainstalovaný, budete potřebovat jej stáhnout a instalovat před spuštěním Web2py.
-S welcome
fráze příkazu říká Web2py, aby spustilo interaktivní shell stejně, jako kdyby příkazy byly vykonávány v controlleru welcome aplikace, což je Web2py vzorová aplikace (zahrnutá v instalaci a sloužící pro počáteční naklonování nových aplikací). Tím jsou vám zpřístupněny téměř všechny Web2py třídy, objekty a funkce. To je jediný rozdíl mezi Web2py interaktivní příkazovou řádkou a normální příkazovou řádkou Pythonu (případně nějakým sofistikovanějším prostředím pro spouštění příkazů Pythonu, jako je např. IDLE).
Rovněž admin rozhraní Web2py dává k dispozici webový shell pro kteroukoli aplikaci. (Po spuštění frameworku, např. ve Windows příkazem: web2py.exe nebo ze zdrojového kódu příkazem: python web2py.py) můžete např. shell pro "welcome" aplikaci spustit v prohlížeči zadáním adresy:
http://127.0.0.1:8000/admin/shell/index/welcome
Můžete zkoušet všechny příklady z této kapitoly pomocí normálního shellu nebo pomocí webového shellu (a protože většina příkladů této kapitoly není závislá na Web2py, tak i z normálního shellu Pythonu, z IDLE, apod.).
help, dir
Jazyk Python obsahuje dva příkazy k získání dokumentace o objektech, definovaných v aktuálním kontextu, a sice o vestavěných i uživatelsky definovaných.
Můžeme se ptát na help
o objektu, např. o objektu "1":
>>> help(1)
Help on int object:
class int(object)
| int(x[, base]) -> integer
|
| Convert a string or number to an integer, if possible. A floating point
| argument will be truncated towards zero (this does not include a string
| representation of a floating point number!) When converting a string, use
| the optional base. It is an error to supply a base when converting a
| non-string. If the argument is outside the integer range a long object
| will be returned instead.
|
| Methods defined here:
|
| __abs__(...)
| x.__abs__() <==> abs(x)
...
protože "1" je integer, dostali jsme popis int
třídy a jejích method. Zde jsme výpis zkrátili, protože je příliš dlouhý a podrobný.
Podobně můžeme pomocí příkazu dir
získat seznam metod objektu "1":
>>> dir(1)
['__abs__', '__add__', '__and__', '__class__', '__cmp__', '__coerce__',
'__delattr__', '__div__', '__divmod__', '__doc__', '__float__',
'__floordiv__', '__getattribute__', '__getnewargs__', '__hash__', '__hex__',
'__index__', '__init__', '__int__', '__invert__', '__long__', '__lshift__',
'__mod__', '__mul__', '__neg__', '__new__', '__nonzero__', '__oct__',
'__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdiv__',
'__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__',
'__rlshift__', '__rmod__', '__rmul__', '__ror__', '__rpow__', '__rrshift__',
'__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__',
'__str__', '__sub__', '__truediv__', '__xor__']
Typy
Python je dynamicky typovaný jazyk, což znamená, že Proměnné (Variables) nemají typ (Type) a proto je není potřeba předem deklarovat. Na druhou stranu Hodnoty (Values) typ mají. Můžete se dotázat proměnné na typ hodnoty, kterou právě obsahuje:
>>> a = 3
>>> print type(a)
<type 'int'>
>>> a = 3.14
>>> print type(a)
<type 'float'>
>>> a = 'hello python'
>>> print type(a)
<type 'str'>
Nativní součástí Pythonu jsou také datové structury jako jsou seznamy (lists) a slovníky (dictionaries).
str
Python používá dva odlišné typy řetězců: ASCII řetězce a Unicode řetězce. ASCII řetězce ohraničujeme pomocí '...', "..." nebo pomocí '..' nebo """...""". Trojité uvozovky jsou určeny pro ohraničení víceřádkových řetězců. Unicode řetězce začínají u
před normálním ohraničením řetězce (před uvozovkami). Unicode řetězec lze konvertovat na ASCII řetězec, když zadáme kódování. Např.:
>>> a = 'this is an ASCII string'
>>> b = u'This is a Unicode string'
>>> a = b.encode('utf8')
Po vykonání těchto tří příkazů je výsledné a
ASCII řetězec s UTF8 kódovanými znaky. Web2py používá interně právě UTF8 kódované řetězce.
Je možné různými způsoby vkládat hodnoty do řetězců:
>>> print 'number is ' + str(3)
number is 3
>>> print 'number is %s' % (3)
number is 3
>>> print 'number is %(number)s' % dict(number=3)
number is 3
Poslední notace je nejvíce explicitní a nejméně náchylná k chybám, proto ji lze doporučit.
Mnohé objekty Pythonu, např. čísla, můžeme serializovat do řetězců za pomoci str
nebo repr
. Tyto dvě funkce jsou velmi podobné, ale dávají nepatrně odlišný výstup, jak je patrné z příkladu:
>>> for i in [3, 'hello']:
print str(i), repr(i)
3 3
hello 'hello'
Pro uživatelem definované třídy str
a repr
může být definováno/redefinováno pomocí speciálních operátorů __str__
a __repr__
. Ty budou stručně popsány později; pro podrobnosti se podívejte do oficiální dokumentace Pythonu [pydocs] . repr
má vždy defaultní hodnotu.
Jiným důležitým rysem řetězců v Pythonu je, že podobně jako list (seznam) jsou to iterovatelné objekty, neboli lze v cyklu procházet jejich jednotlivé dílčí prvky - v případě řetězce tedy znaky:
>>> for i in 'hello':
print i
h
e
l
l
o
list
Nejdůležitějšími metodami seznamu (list) v Pythonu jsou append, insert, a delete:
>>> a = [1, 2, 3]
>>> print type(a)
<type 'list'>
>>> a.append(8)
>>> a.insert(2, 7)
>>> del a[0]
>>> print a
[2, 7, 3, 8]
>>> print len(a)
4
Seznamy mohou být ořezávány (sliced):
>>> print a[:3]
[2, 7, 3]
>>> print a[1:]
[7, 3, 8]
>>> print a[-2:]
[3, 8]
a vzájemně spojovány:
>>> a = [2, 3]
>>> b = [5, 6]
>>> print a + b
[2, 3, 5, 6]
Seznam je (jak již bylo řečeno) iterovatelný; můžete jej procházet ve smyčce:
>>> a = [1, 2, 3]
>>> for i in a:
print i
1
2
3
Prvky seznamu nemusí být stejného typu. Prvkem může být kterýkoli Python objekt.
Existuje častý postup, který lze zkrátit pomocí list comprehension. Podívejme se na následující kód (seznam b naplní trojnásobky sudých čísel zze seznamu a):
>>> a = [1,2,3,4,5]
>>> b = []
>>> for x in a:
if x % 2 == 0:
b.append(x * 3)
>>> b
[6, 12]
To lze nahradit pomocí list comprehension:
>>> a = [1,2,3,4,5]
>>> b = [x * 3 for x in a if x % 2 == 0]
>>> b
[6, 12]
tuple
Pozn.překl.: Používám pojem Vektor, protože přesně vystihuje význam této datové struktury. Obvykle se ale pojem Tuple do češtiny nepřekládá.
Vektor (tuple, n-tice) je podobný seznamu (list), jenže počet jeho prvků a jeho jednotlivé prvky nelze změnit (size and elements are immutable), zatímco v seznamu je měnit lze (jsou mutable). Ačkoli prvek jako takový je ve vektoru (v tuple) neměnitelný (immutable), když se jedná o objekt, jeho atributy měnitelné (mutable) jsou. Tuple je ohraničen kulatými závorkami:
>>> a = (1, 2, 3)
Takže přiřazení do prvku sice funguje pro seznam (list):
>>> a = [1, 2, 3]
>>> a[1] = 5
>>> print a
[1, 5, 3]
ale už ne pro vektor (tuple):
>>> a = (1, 2, 3)
>>> print a[1]
2
>>> a[1] = 5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
Tuple, podobně jako seznam (list), je iterovatelný objekt. Důležitá drobnost: tuple s jediným prvkem musíme zapsat s čárkou za tímto prvkem, jak ukazuje příklad:
>>> a = (1)
>>> print type(a)
<type 'int'>
>>> a = (1,)
>>> print type(a)
<type 'tuple'>
Vektory (tuples) jsou hodně užitečné pro sbalení (packing) několika objektů. Závorky jsou přitom v zápisu často nepovinné:
>>> a = 2, 3, 'hello'
>>> x, y, z = a
>>> print x
2
>>> print z
hello
dict
Slovník (dict
-ionary) je hash tabulka, která ke klíči (key) mapuje hodnotu (value). Např.:
>>> a = {'k':'v', 'k2':3}
>>> a['k']
v
>>> a['k2']
3
>>> a.has_key('k')
True
>>> a.has_key('v')
False
Klíče (keys) mohou být jakéhokoli hashovatelného typu (int, string, nebo kterýkoli objekt, jehož třída implementuje __hash__
metodu). Hodnoty (values) mohou býýt naprosto libovolného typu. Jednotlivé klíče ani jednotlivé hodnoty v dictionary nemusí být stejného typu (ačkoli v případě klíčů je obvyklé používat jen jeden typ). Jsou-li klíče řetězce, může být dictionary deklarována pomocí alternativní syntaxe:
>>> a = dict(k='v', h2=3)
>>> a['k']
v
>>> print a
{'k':'v', 'h2':3}
Užitečné metody jsou has_key
, keys
, values
a items
:
>>> a = dict(k='v', k2=3)
>>> print a.keys()
['k', 'k2']
>>> print a.values()
['v', 3]
>>> print a.items()
[('k', 'v'), ('k2', 3)]
Metoda items
sestaví seznam vektorů (tuples), z nichž každý obsahuje klíč a jemu příslušnou hodnotu.
Prvky slovníku (dictionary) a seznamu (list) lze rušit identickým příkazem - del
:
>>> a = [1, 2, 3]
>>> del a[1]
>>> print a
[1, 3]
>>> a = dict(k='v', h2=3)
>>> del a['h2']
>>> print a
{'k':'v'}
Vnitřně Python používá hash
funkci k převodu klíče na integer, a tento integer použije ke stanovení, kam má uložit hodnotu.
>>> hash("hello world")
-1500746465
O odsazení (indentation)
Python používá odsazení (indentation) k omezení bloků kódu. Blok začíná řádkem, na jehož konci je dvojtečka a pokračuje přes všechny následující řádky, které mají stejné (nebo větší) odsazení. Např.:
>>> i = 0
>>> while i < 3:
>>> print i
>>> i = i + 1
>>>
0
1
2
Nejběžnější je používat 4 mezery pro každou úroveň odsazení. Je důležité mít pořádek a nemíchat pro odsazení tabelátory a mezery - což by mohlo vést k chybám bez viditelné příčiny.
for...in
V Pythonu můžete iterovatelné objekty procházet ve smyčce:
>>> a = [0, 1, 'hello', 'python']
>>> for i in a:
print i
0
1
hello
python
Běžnou zkratkou je funkce xrange
, která generuje iterovatelný rozsah, aniž by ukládala seznam prvků (list).
>>> for i in xrange(0, 4):
print i
0
1
2
3
Je to ekvivalent C/C++/C#/Java syntaxe:
for(int i=0; i<4; i=i+1) { print(i); }
K dispozici je ještě funkce range(a, b, c)
, která fyzicky vrátí seznam integer hodnot, které začínají a
, zvětšují se o c
, a končí poslední hodnotou menší než b
. Defaultní hodnotou pro a
je 0 a pro c
(krok) je to 1. xrange
je podobné - ale seznam fyzicky nesestavuje. Jen dává k dispozici iterátor přes tento (fyzicky neexistující) seznam - je proto lepší pro konstrukci smyček.
Jinou užitečnou funkcí je enumerate
, která počítá průchody smyčkou:
>>> a = [0, 1, 'hello', 'python']
>>> for i, j in enumerate(a):
print i, j
0 0
1 1
2 hello
3 python
Ze smyčky můžete mimořádně vyskočit příkazem break
>>> for i in [1, 2, 3]:
print i
break
1
Můžete také přeskočit zbytek kódu smyčky a pokračovat další iterací, a sice pomocí příkazu continue
>>> for i in [1, 2, 3]:
print i
continue
print 'test'
1
2
3
while
while
smyčka v Pythonu pracuje jako v jiných jazycích - prochází cyklus, přičemž počet průchodů není předem dán, ale trvá dokud testovaná podmínka vrací True
.
>>> i = 0
>>> while i < 10:
i = i + 1
>>> print i
10
Python nemá konstrukci typu loop...until
.
if...elif...else
>>> for i in range(3):
>>> if i == 0:
>>> print 'zero'
>>> elif i == 1:
>>> print 'one'
>>> else:
>>> print 'other'
zero
one
other
"elif" znamená "else if". elif
i else
fráze jsou nepovinné. Můžeme zapsat více elif
, ale pouze jediné else
(pokrývající všechny zbývající případy). Lze samozřejmě vytvářet komplexní podmínky pomocí not
, and
a or
.
>>> for i in range(3):
>>> if i == 0 or (i == 1 and i + 1 == 2):
>>> print '0 or 1'
try...except...else...finally
>>> try:
>>> a = 1 / 0
>>> except Exception, e:
>>> print 'oops: %s' % e
>>> else:
>>> print 'no problem here'
>>> finally:
>>> print 'done'
oops: integer division or modulo by zero
done
Když k výjimce dojde, zachytí ji except
fráze, která se provede, kdežto else
fráze se neprovede. Když nedojde k výjimce, except
fráze se neprovede, naopak else
fráze ano. finally
fráze se provede vždy.
Může být definováno více except
frází pro různé možné výjimky:
>>> try:
>>> raise SyntaxError
>>> except ValueError:
>>> print 'value error'
>>> except SyntaxError:
>>> print 'syntax error'
syntax error
else
a finally
jsou nepovinné.
Zde je seznam vestavěných (built-in) Python výjimek, a navíc HTTP výjimka, kterou definuje Web2py:
BaseException
+-- HTTP (definuje ji Web2py)
+-- SystemExit
+-- KeyboardInterrupt
+-- Exception
+-- GeneratorExit
+-- StopIteration
+-- StandardError
| +-- ArithmeticError
| | +-- FloatingPointError
| | +-- OverflowError
| | +-- ZeroDivisionError
| +-- AssertionError
| +-- AttributeError
| +-- EnvironmentError
| | +-- IOError
| | +-- OSError
| | +-- WindowsError (Windows)
| | +-- VMSError (VMS)
| +-- EOFError
| +-- ImportError
| +-- LookupError
| | +-- IndexError
| | +-- KeyError
| +-- MemoryError
| +-- NameError
| | +-- UnboundLocalError
| +-- ReferenceError
| +-- RuntimeError
| | +-- NotImplementedError
| +-- SyntaxError
| | +-- IndentationError
| | +-- TabError
| +-- SystemError
| +-- TypeError
| +-- ValueError
| | +-- UnicodeError
| | +-- UnicodeDecodeError
| | +-- UnicodeEncodeError
| | +-- UnicodeTranslateError
+-- Warning
+-- DeprecationWarning
+-- PendingDeprecationWarning
+-- RuntimeWarning
+-- SyntaxWarning
+-- UserWarning
+-- FutureWarning
+-- ImportWarning
+-- UnicodeWarning
Pro podrobný popis každé z nich nahlédněte do oficiální dokumentace Pythonu.
Web2py zveřejňuje jen jedinou novou výjimku, nazvanou HTTP
. Když je vyvolána, způsobí, že program vrátí HTTP error page (více o tom píšeme v kapitole 4).
Principiálně lze jako výjimku vracet libovolný objekt, ale je rozumnou praxí použít jen objekty, vytvořené pomocí vestavěných tříd pro výjimky (exception classes).
def...return
Funkce se deklarují příkazem def
:
>>> def f(a, b):
return a + b
>>> print f(4, 2)
6
Není potřebné (a ani nelze) specifikovat typy argumentů a vrácené hodnoty(hodnot). V našem příkladu jsme definovali funkci f
, která může mít dva argumenty.
Funkce jsou prvním prvkem syntaxe v této kapitole, v souvislosti s nimiž zavedeme pojem rozsah/kontext (scope nebo namespace). V předchozím příkladu proměnné a
a b
nejsou definovány vně kontextu funkce f
:
>>> def f(a):
return a + 1
>>> print f(1)
2
>>> print a
Traceback (most recent call last):
File "<pyshell#22>", line 1, in <module>
print a
NameError: name 'a' is not defined
Proměnné, definované vně kontextu funkce jsou uvnitř funkce přístupné; podívejte se, jak se s identifikátorem a
pracuje v následujícím kódu:
>>> a = 1
>>> def f(b):
return a + b
>>> print f(1)
2
>>> a = 2
>>> print f(1) # new value of a is used
3
>>> a = 1 # reset a
>>> def g(b):
a = 2 # creates a new local a
return a + b
>>> print g(2)
4
>>> print a # global a is unchanged
1
Jestliže je a
modifikováno, pozdější volání funkce použije novou hodnotu globálního a
, a to proto, že funkce má přístup k paměťovému místu s označením a
, nikoli ke konkrétní hodnotě a
v okamžiku, kdy je funkce deklarována. Ale naopak, když do a
přiřadíme uvnitř g
, globální a
není ovlivněno, protože nové lokální a
překryje globální hodnotu.
External-scope reference může být použita na vytvoření closures:
>>> def f(x):
def g(y):
return x * y
return g
>>> doubler = f(2) # doubler is a new function
>>> tripler = f(3) # tripler is a new function
>>> quadrupler = f(4) # quadrupler is a new function
>>> print doubler(5)
10
>>> print tripler(5)
15
>>> print quadrupler(5)
20
Voláním funkce f
je vrácena funkce. Funkce f
tedy vytváří nové funkce. Poznamenejme, že kontext (scope) jména g
je zcela interní uvnitř f
. Closures jsou velmi mocným nástrojem.
Argumenty funkcí mohou mít defaultní hodnoty. Funkce mohou vracet více výsledků (ve skutečnosti vrací vektor (tuple), který je v následujícím příkladu zpětně rozbalen do proměnných x,y):
>>> def f(a, b=2):
return a + b, a - b
>>> x, y = f(5)
>>> print x
7
>>> print y
3
Argumenty funkcí mohou být předány explicitně pomocí jmen, a to tedy znamená, že pak pořadí parametrů při volání může být i jiné než pořadí v definici funkce:
>>> def f(a, b=2):
return a + b, a - b
>>> x, y = f(b=5, a=2)
>>> print x
7
>>> print y
-3
Funkce mohou dokonce mít proměnný počet parametrů při jejich volání:
>>> def f(*a, **b):
return a, b
>>> x, y = f(3, 'hello', c=4, test='world')
>>> print x
(3, 'hello')
>>> print y
{'c':4, 'test':'world'}
Zde argumenty nepředané pomocí jmen (3, 'hello') se nastřádají do vektoru (tuple) a
, kdežto argumenty předané pomocí jmen (c
a test
) se nastřádají do dictionary b
(tato jména se použijí jako klíče v dictionary).
Ale i opačně: Seznam (list) nebo tuple mohou být ve volání předány funkci, která v definici vyžaduje individuální poziční parametry. Při volání podle níže uvedeného příkladu se list nebo tuple na jednotlivé poziční parametry rozbalí:
>>> def f(a, b):
return a + b
>>> c = (1, 2)
>>> print f(*c)
3
Podobně může být ve volání použita dictionary a rozbalena na jednotlivé argumenty v definici podle jejich jména:
>>> def f(a, b):
return a + b
>>> c = {'a':1, 'b':2}
>>> print f(**c)
3
lambda
lambda
představuje způsob, jak velmi jednoduše vytvořit krátké nepojmenované funkce:
>>> a = lambda b: b + 2
>>> print a(3)
5
Výraz "lambda
[a]:[b]" doslovně čteme "funkce s argumenty [a], která vrací [b]". Samotný lambda
výraz sice není pojmenovaný, ale funkce získá jméno přiřazením do identifikátoru a
. Pravidla o kontextu (scoping rules) pro def
platí identicky pro lambda
funkci a ve skutečnosti výše popsaný kód je identický s deklarací funkce pomocí def
:
>>> def a(b):
return b + 2
>>> print a(3)
5
Jedinou výhodou lambda
funkce je stručnost; ale stručnost může být v některých situacích zásadním přínosem. Např. funkce Pythonu map
má jako první parametr funkci, druhý seznam. Na všechny prvky seznamu se aplikuje funkce z prvního parametru a tak se vytvoří nový seznam:
>>> a = [1, 7, 2, 5, 4, 8]
>>> map(lambda x: x + 2, a)
[3, 9, 4, 7, 6, 10]
Tento kód by byl 2x delší, kdybychom použili def
místo lambda
. Hlavním omezením lambda
funkce (v Python implementaci) je, že její syntaxe stačí jen na jednotlivé výrazy v návratových hodnotách; ale zase když ve složitějších případech použijeme def
, tak extra prodloužení, způsobené pojmenováním bude stále méně významné, jak složitost funkce poroste.
Stejně jako def
, i lambda
může být použita pro curry funkce: nové funkce lze vytvářet obalením (wrapping) stávajících funkcí a dát jim jiné argumenty:
>>> def f(a, b): return a + b
>>> g = lambda a: f(a, 3)
>>> g(2)
5
Je hodně situací, kde jsou curry funkce užitečné, ale v jedné z nich jsou velmi užitečné právě ve Web2py: jde o caching. Předpokládejme, že máte často používanou funkci, která zjišťuje, jestli její argument je prvočíslo:
def isprime(number):
for p in range(2, number):
if (number % p) == 0:
return False
return True
Taková funkce spotřebuje hodně času.
Předpokládejte, že budete mít caching funkci cache.ram
, která bude mít 3 argumenty: klíč, funkci a počet vteřin.
value = cache.ram('key', f, 60)
Když je volána poprvé, zavolá funkci f()
, uloží výstup do dictionary v paměti (řekněme, že se bude jmenovat "d"), a vrátí jej, takže hodnota bude:
value = d['key']=f()
Když je volána podruhé, a klíč se už nachází v dictionary d a zároveň není starší než zadaný počet vteřin (60), vrátí caching funkce hodnotu z dictionary, aniž by volala časově náročnou funkci f().
value = d['key']
Jak to konkrétně provedete s funkcí isprime? Takto:
>>> number = 7
>>> seconds = 60
>>> print cache.ram(str(number), lambda: isprime(number), seconds)
True
>>> print cache.ram(str(number), lambda: isprime(number), seconds)
True
Výsledek je vždy stejný. Ale při prvním volání cache.ram
se skutečně volá isprime
; kdežto při druhém volání ne (a výsledek je vrácen z interní dictionary).
Viděli jsme, že funkce Pythonu, ať už vytvořené pomocí
def
nebolambda
umožňují re-factoring existující funkce pro jinou množinu argumentů.cache.ram
acache.disk
jsou cachovací funkce, které jsou k dipozici ve Web2py.
class
Python je dynamicky typovaný jazyk, proto je definice tříd velmi stručná. Ve skutečnosti není třeba ani definovat členské proměnné (atributy) při deklarování třídy, a rozdílné instance jedné třídy mohou mít odlišné atributy. Atributy jsou obecně svázány s instancí a ne třídou, pokud ovšem nedefinujete "atributy třídy (class attributes)", což je totéž jako "static member variables" v C++/Java.
Zde je příklad:
>>> class MyClass(object): pass
>>> myinstance = MyClass()
>>> myinstance.myvariable = 3
>>> print myinstance.myvariable
3
Všimněme si, že pass
je příkaz Pythonu, který nedělá vůbec nic. V tomto případě jsme jej použili na definici třídy MyClass
, která nic neobsahuje. MyClass()
zavolá konstruktor třídy (v tomto případě defaultní konstruktor) a vrátí objekt, který je instancí třídy. Slovo (object)
v definici třídy znamená, že naši třídu dědíme z vestavěné (built-in) třídy object
. Není to nutné, ale je to dobrá technika (v opačném případě se použije starší typ tříd).
Zde je příklad složitější třídy:
>>> class MyClass(object):
>>> z = 2
>>> def __init__(self, a, b):
>>> self.x = a, self.y = b
>>> def add(self):
>>> return self.x + self.y + self.z
>>> myinstance = MyClass(3, 4)
>>> print myinstance.add()
9
Funkce, deklarované uvnitř třídy, jsou metody. Některé metody mají speciální rezervovaná jména. Např. __init__
je konstruktor. Všechny proměnné jsou lokální proměnné metod, s výjimkou proměnných, deklarovaných mimo metody. Např. z
je proměnná třídy (class variable), ekvivalent C++ static member variable, která obsahuje stejnou hodnotu ve všech instancích třídy.
Všimněme si, že __init__
má 3 argumenty a add
jediný, ale přesto je voláme se třemi resp. žádným argumentem. První argument totiž podle konvence představuje lokální jméno, použité uvnitř metody pro odkazování na aktuální objekt. Tady jsme použili self
, ale bylo by možné použít jakékoli jméno. self
má stejnou úlohu jako *this
v C++ nebo this
v Javě, ale self
není rezervovaným slovem.
Tato syntaxe je potřebná pro zamezení nejasností, když definujeme vzájemně vnořené třídy, např. třídu, která je lokální uvnitř metody jiné třídy.
Speciální atributy, metody a operátory
Atributy tříd, metody a operátory, jejichž jméno začíná dvojitým podtržítkem jsou obvykle chápány jako privátní (tj. používané interně, ale nezveřejňované mimo třídu), ačkoli to je konvence a není to vynucováno interpreterem.
Některé z nich jsou rezervovaná slova a mají speciální význam. Jako příklad zde máme tři z nich:
__len__
__getitem__
__setitem__
Mohou být použity např. k vytvoření containerového objektu, který se bude chovat jako seznam (list):
>>> class MyList(object):
>>> def __init__(self, *a): self.a = list(a)
>>> def __len__(self): return len(self.a)
>>> def __getitem__(self, i): return self.a[i]
>>> def __setitem__(self, i, j): self.a[i] = j
>>> b = MyList(3, 4, 5)
>>> print b[1]
4
>>> b.a[1] = 7
>>> print b.a
[3, 7, 5]
Dalšími speciálními operátory jsou __getattr__
a __setattr__
, které definují činnost při zjištění (get) a nastavení (set) hodnoty atributu třídy, a __sum__
a __sub__
, které definují, jak se se třídou bude zacházet při použití aritmetických operátorů. Čtenáře, který by chtěl tyto speciální operátory používat, odkazujeme na podrobnější dokumentaci k tomuto tématu. Už jsme zmínili speciální operátory __str__
a __repr__
.
Vstup ze souboru / Výstup do souboru
V Pythonu můžete soubor otevřít a zapisovat do něj takto:
>>> file = open('myfile.txt', 'w')
>>> file.write('hello world')
>>> file.close()
Obdobně můžete ze souboru číst:
>>> file = open('myfile.txt', 'r')
>>> print file.read()
hello world
Případně můžete číst v binárním módu pomocí "rb", zapisovat v binárním módu pomocí "wb" (textový mód se od binárního liší automatickým převodem znaků pro konce řádků - které jsou v Linuxu, na Macu a ve Windows odlišné), a otevírat soubor pro přidávání na konec (append mode) pomocí "a"; používá se standardní C notace.
read
metoda má volitelný argument, kterým je počet bytů, které chcete přečíst. Můžete také přeskočit na libovolnou pozici v souboru pomocí seek
.
Takhle přečteme text od určité pozice:
>>> print file.seek(6)
>>> print file.read()
world
Nakonec je třeba soubor, se kterým jsme pracovali, uzavírat:
>>> file.close()
Ve standardní distribuci Pythonu, tedy v CPython, je počítán počet odkazů na proměnné, včetně těch, které obsahují souborové handly, takže CPython ví, že když se sníží počet referencí na otevřený file handle na nulu, je třeba soubor uzavřít a proměnnou lze zrušit. Ale v jiných implementacích Pythonu, jako je např. PyPy, se používá garbage collection místo počítání referencí a mohlo by se stát, že by bylo příliš mnoho otevřených souborových handlů ještě dříve, než by garbage collector zasáhl, zavřel je a odstranit. Proto vždy doporučujeme handly explicitně zavírat. Web2py nabízí dvě pomocné funkce,
read_file()
awrite_file()
v namespacegluon.fileutils
, které obalují celý přístup k souboru a zajišťují automatické otevření a uzavření souboru.
Když používáte Web2py, nevíte, jaký je aktuální adresář, protože to závisí na konfiguraci Web2py. Proměnná
request.folder
obsahuje cestu k aktuální aplikaci. Cesty můžete nezávisle na operačním systému skládat pomocíos.path.join
, jak je popsáno níže.
exec
, eval
Na rozdíl od Javy, Python je opravdu interpretovaný jazyk. Znamená to, že může vykonávat příkazy Pythonu, uložené ne ve zdrojovém kódu, ale v řetězcích. Např.:
>>> a = "print 'hello world'"
>>> exec(a)
'hello world'
Co se tady stalo? Funkce exec
řekla interpreteru, aby zavolal sám sebe a vykonal obsah řetězce, předaného jako argument. Je také možné vykonat obsah řetězce v contextu, definovaném symboly (proměnnými,..) ze slovníku (dictionary):
>>> a = "print b"
>>> c = dict(b=3)
>>> exec(a, {}, c)
3
Zde interpreter, když vykonává řetězec a
, vidí symboly, definované ve slovníku c
(b
v našem příkladu), ale nevidí symboly c
ani a
. To je odlišné od práce s omezeným prostředím (restricted environment), protože exec
nijak neomezuje, co může prováděný kód dělat; jen definuje sadu proměnných, na které kód vidí.
Podobná funkce je eval
, která očekává, že její argument bude vyčíslen jako hodnota a tuto hodnotu vrátí.
>>> a = "3*4"
>>> b = eval(a)
>>> print b
12
import
Např. když chcete generovat náhodná čísla, uděláte to takto:
>>> import random
>>> print random.randint(0, 9)
5
To napíše náhodné integer číslo od 0 do 9 (včetně), 5 v našem příkladu. Fuunkce randint
je definována v modulu random
. Místo do namespace random
můžete také importovat objekt z modulu přímo do aktuálního namespace:
>>> from random import randint
>>> print randint(0, 9)
nebo importovat do aktuálního namespace najednou všechny objekty z modulu:
>>> from random import *
>>> print randint(0, 9)
případně importovat vše do nově vytvořeného namespace:
>>> import random as myrand
>>> print myrand.randint(0, 9)
Ve zbytku knihy budeme především používat objekty, definované v modulech os
, sys
, datetime
, time
a cPickle
.
Všechny prvky Web2py jsou dostupné pomocí modulu
gluon
, což je obsahem dalších kapitol. Vnitřně Web2py používá mnoho Python modulů (např.thread
), ale málokdy byste potřebovali k nim přistupovat přímo.
V následujících bodech probereme moduly, které jsou nejužitečnější.
os
Tento modul poskytuje rozhraní k API operačního systému. Např.:
>>> import os
>>> os.chdir('..')
>>> os.unlink('filename_to_be_deleted')
Některé z funkcí
os
modulu ale NESMĚJÍ BÝT ve Web2py POUŽÍVÁNY, např.chdir
, protože nejsou threadově bezpečné.
os.path.join
je velmi užitečná funkce; umožňuje spojovat cesty způsobem nezávislým na konkrétním operačním systému:
>>> import os
>>> a = os.path.join('path', 'sub_path')
>>> print a
path/sub_path
Proměnné operačního systému jsou dostupné pomocí:
>>> print os.environ
jedná se o read-only slovník (dictionary).
sys
sys
modul obsahuje mnoho proměnných a funkcí, jedna z nejužitečnějších je sys.path
. Obsahuje seznam (list) cest, kde Python hledá moduly. Když se pokoušíme importovat modul, Python ho hledá postupně ve všech adresářích, uvedených v sys.path
. Instalujete-li přídavné moduly na určité místo a chcete-li, aby je Python nalezl, potřebujete přidat (append) dotyčnou cestu do sys.path
.
>>> import sys
>>> sys.path.append('path/to/my/modules')
Když provozujete Web2py, Python zůstává rezidentní v paměti, a je pouze jediný seznam sys.path
, zatímco je více vláken (threads), která obsluhují HTTP požadavky. Abychom zabránili zbytečnému opakování a spotřebě času a paměti, je nejlepší testovat, zda cesta už v seznamu není přidána:
>>> path = 'path/to/my/modules'
>>> if not path in sys.path:
sys.path.append(path)
datetime
Použití datetime
modulu demonstrujeme na několika příkladech:
>>> import datetime
>>> print datetime.datetime.today()
2008-07-04 14:03:90
>>> print datetime.date.today()
2008-07-04
Někdy můžete potřebovat připsat k datům časovou informaci v UTC (světovém čase) místo v lokálním čase - to vyřešíte následující funkcí:
>>> import datetime
>>> print datetime.datetime.utcnow()
2008-07-04 14:03:90
datetime
modul obsahuje různé třídy: date, datetime, time a timedelta. K vyjádření rozdílu mezi dvěma daty, dvěma datetime údaji nebo dvěma time objekty slouží třída (datový typ) timedelta:
>>> a = datetime.datetime(2008, 1, 1, 20, 30)
>>> b = datetime.datetime(2008, 1, 2, 20, 30)
>>> c = b - a
>>> print c.days
1
Ve Web2py date a datetime používáme k uložení odpovídajících hodnot, když ukládáme časové údaje do databáze nebo je z ní získáváme..
time
time
modul se liší od date
a datetime
tím, že reprezentuje čas jako vteřiny od začátku epochy (od počátku roku 1970).
>>> import time
>>> t = time.time()
1215138737.571
Nahlédněte do Python dokumentace pro konverzní funkce mezi časem ve vteřinách (time) a časem jako datetime
.
cPickle
Toto je velmi potřebný modul. Poskytuje funkce, které serializují do řetězce téměř jakýkoli Python objekt, dokonce včetně objektů, které odkazují samy na sebe. Např. vytvořme nějaký objekt:
>>> class MyClass(object): pass
>>> myinstance = MyClass()
>>> myinstance.x = 'something'
>>> a = [1 ,2, {'hello':'world'}, [3, 4, [myinstance]]]
a nyní:
>>> import cPickle
>>> b = cPickle.dumps(a)
>>> c = cPickle.loads(b)
V tomto příkladu b
je řetězcová reprezentace hodnoty a
, a c
je kopie a
, která vznikla de-serializací řetězce b
.
cPickle může také serializovat do souboru (a de-serializovat ze souboru):
>>> cPickle.dump(a, open('myfile.pickle', 'wb'))
>>> c = cPickle.load(open('myfile.pickle', 'rb'))