Python 2.7 se hodně používá a zřejmě nějaký ten pátek ještě bude. Přechod na Python 3 se ale nevyhnutelně blíží a při takovém přechodu se skočí rovnou na nějakou nejposlednější verzi. Momentálně to může být 3.4 či dokonce před pár dny vypuštěný Python 3.5. Říkal jsem si, co vlastně všechno takového hezkého bylo přidáno od verze 3.0 do 3.5 a nebylo backportováno? Prošel jsem si všechny změny a řekl si, že se podělím o to, co mne zaujalo.
Python 3.0: Print!
print()
je funkce. S tím bude těžké sžít se. První třetí verze se objevila v
roce 2009 a stále jsem se nenaučil psát závorky.
Další velká změna je, že text je opravdu text a nejde mixovat s binárními
daty. Takže žádné fungování při testech a poté v provozu neustálé ošetřování
výjimek UnicodeDecodeError
.
Všude jsou iterátory. Žádné range
vs. xrange
, ale pouze range
vracející
iterátor. Další funkce dříve vracející seznamy také jedou na iterátorech, jako
třeba map
, filter
, zip
, … Ale pozor, items
, keys
apod. nad slovníkem
nevrací iterátor, nýbrž
view. To je zase trochu něco jiného.
super()
bez parametrů!
A detail možnost rozbalit tuple
s hvězdičkou: (a, *rest, b) = range(5)
.
Python 3.1: Vůbec nic
Vše důležité bylo backportnuto i do Pythonu 2.7. Nový Python si holt teprve sedal. :-)
Python 3.2: První featury
První krůčky k asynchronnímu Pythonu, knihovna
concurrent.futures
.
Konečně si nebudeme muset dělat sami řešení na cachování výsledků funkcí, aneb
ať žije
lru_cache
!
@lru_cache(maxsize=None)
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
>>> [fib(n) for n in range(16)]
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]
>>> fib.cache_info()
CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)
Ve functools
přibyla ještě jedna zajímavost a to, že wraps
navíc přidává
__wrapped__
odkazující na původní funkci. Takže jde introspektnout, vrátit
atp. :-)
OrderedDict
dostal novou metodu
move_to_end
.
Jen škoda, že opak udělali ošklivě parametrem…
Je možné mít configuráky s proměnnými díky
ExtendedInterpolation
.
Mimochodem lze předat konfiguraci i jako
slovník, což
dodává proměnným ještě větší šťávu!
>>> parser = ConfigParser(interpolation=ExtendedInterpolation())
>>> parser.read_dict({'buildout': {'directory': '/home/ambv/zope9'}})
>>> parser.read_string("""
[buildout]
find-links = ${buildout:directory}/downloads/dist
""")
>>> parser['buildout']['find-links']
'/home/ambv/zope9/downloads/dist'
Vzniknul modul argparse
. Ten byl teda backportnut i do Pythonu 2.7. Ale chci
se pochlubit, že existuje i
argparsedialog
(jen pro
trojku).
Python 3.3: Rozjíždíme…
Je to tu. Tak kolik aplikací 3.3ka složí? :-) Slovník nemá definované pořadí. V dokumentaci bylo upozornění, že se nejedná o náhodné řazení, ale rozhodně se na to nemá spoléhat. Kvůli bezpečnostní chybě už nelze pořadí předvídat.
Nová syntaxe yield
from
pro delegování
generatorů. Především pro to, co přijde v Pythonu 3.4, aby se později stalo v
2.5 zbytečným. :-)) Zkratka pro:
>>> for item in iterable:
... yield item
>>> yield from iterable
Virtualenv už není třeba, součástí Pythonu je nový
pyvenv
, který řeší všechny
problémy, které nemohly tooly třetích stran vyřešit na 100 procent.
__init__.py
není povinný! Ano,
NE-NÍ povinný. Jen otázka, zda je to dobrý nápad. Protože pak je možné
naimportovat jakýkoliv adresář…
Pokud se odchytí výjimka a ta vyhodí novou výjimku, budou vidět obě. Často zbytečné, ale věřte, že v praxi se stávají i chyby při zpracování chyby, a pak je akorát původní chyba občas nedohledatelná.
>>> class C:
... def __init__(self, extra):
... self._extra_attributes = extra
... def __getattr__(self, attr):
... try:
... return self._extra_attributes[attr]
... except KeyError:
... raise AttributeError(attr)
...
>>> C({}).x
Traceback (most recent call last):
File "", line 6, in __getattr__
KeyError: 'x'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "", line 1, in
File "", line 8, in __getattr__
AttributeError: x
Přes atribut __qualname__
půjde nově zjistit přesně, kde byla funkce či
třída definována.
>>> class C:
... def meth(self):
... pass
>>> C.meth.__name__
'meth'
>>> C.meth.__qualname__
'C.meth'
Vytváření souborů a vyhodit výjimku, pokud už existuje, půjde s novým módem
x
.
>>> open('/tmp/somefile', 'x')
<_io.TextIOWrapper name='/tmp/aaa' mode='x' encoding='ANSI_X3.4-1968'>
>>> open('/tmp/somefile', 'x')
Traceback (most recent call last):
File "", line 1, in
FileExistsError: [Errno 17] File exists: '/tmp/somefile'
datetime
instance dostala novou metodu timestamp
. Ale že to trvalo, že?
:-)
>>> some_datetime.timestamp()
1442266005.577331
Nový hezký Pythonic modul
ipaddress
. Díky tomu lze
zapsat například: (IPv4Address('127.0.0.1') + 42).is_private
.
mock
je součástí
unittestu
!
Je zpět u''
pro lepší port z dvojky! Není to jediná drobnost, která ulehčuje
přechod na trojku. Takže pokud přechod, tak minimálně na… 3.4 (ne, to není
překlep, jen dnes dřívější nemá moc smysl).
Python 3.4: Balíky, balíky, balíky, …
ensurepip
je nový balík zajišťující přítomnost instalací Pythonu, i když se
jedná o projekt mimo samotný Python. Jinými slovy pip po ruce po instalaci
Pythonu!
Nový balík pro asynchronní aplikace AsyncIO. To je kde se využije delegování generátorů. AsyncIO je totiž na generátorech tak trochu postavené.
Vlastně až balík pathlib
mne nahlodal projít podrobně, co vše v Pythonu 3.x bylo přidáno a upraveno.
>>> p = Path('/etc')
>>> q = p / 'init.d' / 'reboot'
>>> q
PosixPath('/etc/init.d/reboot')
>>> q.resolve()
PosixPath('/etc/rc.d/init.d/halt')
Třeba takový enum
se bude
taky hodit…
>>> from enum import Enum
>>> class Color(Enum):
... red = 1
... green = 2
... blue = 3
>>> type(Color.red)
<enum 'Color'>
>>> isinstance(Color.green, Color)
True
>>> print(Color.red.name)
red
Nebo různé statistické metody v modulu
statistics
…
Modul functools
se rozrostl o další užitečnosti. První z nich je
partialmethod
,
což dodává super nové možnosti:
>>> class Cell(object):
... def __init__(self):
... self._alive = False
... @property
... def alive(self):
... return self._alive
... def set_state(self, state):
... self._alive = bool(state)
... set_alive = partialmethod(set_state, True)
... set_dead = partialmethod(set_state, False)
A pokud vám chybělo method overloading, pak to lze aspoň trochu dohnat se
singledispatch
.
I když nedoporučoval bych takový přístup.
>>> from functools import singledispatch
>>> @singledispatch
... def fun(arg, verbose=False):
... if verbose:
... print("Let me just say,", end=" ")
... print(arg)
...
>>> @fun.register(int)
... def _(arg, verbose=False):
... if verbose:
... print("Strength in numbers, eh?", end=" ")
... print(arg)
...
>>> fun("Hello, world.")
Hello, world.
>>> fun("test.", verbose=True)
Let me just say, test.
>>> fun(42, verbose=True)
Strength in numbers, eh? 42
Občas se může stát, že se do reguláru zapomene přidat dolar nebo se při
nějakých změnách vytratí atp., proto další metoda
fullmatch
.
Zrovna nedávno jsme řešili, že naše aplikace žere docela dost paměti a občas
neúměrně moc. Chtěli jsme to pořádně zanalyzovat, ale nebylo čím. Mile mne
překvapil modul
tracemalloc
. S ním to
bude sranda debugovat. :-)
Funkce min
a max
požírají parametr default
, řešící současné vyhazování
ValueError
při prázdné iterable proměnné.
Python 3.5: To nejlepší nakonec
Už žádné yield from
s AsyncIO. Vznik nových klíčových slov async
a
await
. Tedy abych byl přesný,
pravými klíčovými slovy budou až od Pythonu 3.7, ale je dobré se jim vyhnout
už nyní.
async def read_data(db):
data = await db.fetch('SELECT ...')
Type hinty. Druhá velká věc, o které se hodně mluví. Za mne se moc těším na možnost sem tam vynutit typ a pomoct tak i editorům v napovídání. Vlastně nemusím, jelikož se stub fily lze využít už nyní. Ale přímo v kódu je přímo v kódu. Konečně žádné cusotm dekorátory či asserty na začátku funkce.
def greeting(name: str) -> str:
return 'Hello ' + name
Jen se mi moc nelíbí jak se budou zapisovat nějaké složitější typy s dodávanou
knihovnou typing
. No,
diskutovalo kolem toho spoustu chytrých lidí, tak uvidíme, jaké to bude v
praxi.
Krom nových klíčových slov a type hintů se dostal do Pythonu i nový operýtor
@
pro násobení matic. Známá knihovna numpy
přijde s podporou ve verzi
1.10. Matematici se mohou na co těšit. :-)
>>> x = numpy.ones(3)
>>> x
array([ 1., 1., 1.])
>>> m = numpy.eye(3)
>>> m
array([[ 1., 0., 0.],
[ 0., 1., 0.],
[ 0., 0., 1.]])
>>> x @ m
array([ 1., 1., 1.])
Kolikrát jste potřebovali zmergovat slovník a používali pro to ne úplně jasnou
konstrukci dict(first, **second)
? Tak nově lze pěkně hezky: {**first,
**second}
. Samozřejmostí je funkčnost i se seznamy, sety apod.
A to víte, že existuje možnost zabalit Pythoní aplikaci do zipu a přímo
spustit? Se zipapp
už
ano. Zajímavá metoda pro drobnosti, které nepatří do PyPI a je zbytečné dělat
jiný, třeba debianí, balík.
configparser
ještě jednou. Je možné s předáním converters
přidat další
get*
metody.
>>> parser = ConfigParser(converters={'list': [item.strip() for item in
value.split() if item.strip()]}) >>> parser.read_string("""[section] ... value = a b c d""") >>> parser.get('section', 'value') 'a b c d' >>> parser.getlist('section', 'value') ['a', 'b', 'c', 'd']
Pár zajímavých věcí bylo přepsáno do céčka. Například OrderedDict
či
lru_cache
. A
spoustu
dalších
optimalizací
přes
všechny
releasy, jako
třeba zrychlení načtení interpretu, rychlejší dumpování všeho druhu (pickle,
marshall, json), rychlejší volání get property, optimalizace podmínky s výčtem
(tedy lepší psát if x in {a, b, c}
), …
Mne nejvíce potěšilo…
- Vždy přítomný pip a pyvenv
- Možnost občas hintit typy
- Asynchronní aplikace s AsyncIO
- Jednoduše mergovat slovníky
{**first, **second}
- Rozbalování tuplů s hvězdičkou
(a, *rest, b) = range(5)
- Možnost předat funkcím
min
amax
defaultní hodnotu - Všude iterátory
- Cachování s
lru_cache
- Práce s cestami s
pathlib
Takže… taky byste nejradši hned zahodili současný codebase a použili Python 3.5? :-)