Chapter 2: The Python language

Jazyk Python

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)

API
pro mnoho funkcí hostujícího operačního systému (OS). Python kód, ačkoli minimalistický, definuje výkonné vestavěné struktury, jako jsou seznamy (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

shell

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

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

type

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

str
ASCII
UTF8
Unicode
encode

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

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

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

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

for

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

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

if
elif
else
Použití podmíněného větvení je v Pythonu intuitivní:

>>> 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
except
finally
Exception
Python může vyvolat výjimku:

>>> 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

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

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 nebo lambda umožňují re-factoring existující funkce pro jinou množinu argumentů. cache.ram a cache.disk jsou cachovací funkce, které jsou k dipozici ve Web2py.

class

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

file.read
file.write

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.

file.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() a write_file() v namespace gluon.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

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

import
random
Skutečná síla Pythonu je v jeho knihovních modulech. Poskytují rozsáhlou a konzistentní sadu API pro použití mnoha systémových knihoven (často způsobem, nezávislým na konkrétním operačním systému).

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

os
os.path.join
os.unlink

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
sys.path

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

date
datetime
time

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

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

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'))
 top