A Python 3 újdonságai
A 3-as Python az első olyan verzió, amely felhagy az előző verziókkal való kompatibilitással. Megszűntet számos olyan modult, amely az előző (2.X) verziók során elavulttá válnak, új, jobb alternatívákat vezet be az néhol ellentmondásos régi megoldások helyett, valamint az implementációban is jelentős optimalizálások figyelhetők meg.
Ez a dokumentum nem kíván teljes körű leírást adni az összes változásról és optimalizációs részletről, csupán összefoglalja a Python programozó számára a fontosabb módosításokat. A régebbi, 2.5-ös python kódunk 3.X-es verzióra való migrálása javarészt automatikusan történhet, hiszen rendelkezésünkre áll a 2to3 nevű modul, ami a legtöbb változtatást el tudja végezni a forráskódon.
A print már függvény
A print utasítást kivették a nyelvből, pontosabban lecserélték a print() beépített függvényre. A print-hez kapcsolódó szintaktikai elemeket mind átemelték a beépített függvénybe, keyword argumentumok segítségével. Néhány példa:
Old: print "The answer is", 2*2
New: print("The answer is", 2*2)
Old: print x, # Trailing comma suppresses newline
New: print(x, end=" ") # Appends a space instead of a newline
Old: print # Prints a newline
New: print() # You must call the function!
Old: print >>sys.stderr, "fatal error"
New: print("fatal error", file=sys.stderr)
Old: print (x, y) # prints repr((x, y))
New: print((x, y)) # Not the same as print(x, y)!
A pontos szintaxis: print([object, ...][, sep=' '][, end='\n'][, file=sys.stdout])
Listák helyett View-k és Iterátorok
- A dictionary (dict) dict.keys(), dict.values(), és dict.items() metódusai már nem listát adnak vissza, hanem egy ún. view object-et. A view object nem más, mint egy dinamikus nézete az alárendelt dictionary-nek, tehát ha a dictionary változik, akkor ezt a változást a view object is "tudni" fogja. A view object-ek bevezetésének okat kettős: egyrészt lehetővé teszik, hogy a dictionary elemein való iterálás közben változtassuk magát az adatszerkezetet, másrészt pedig mivel nem kell listát létrehozni, így a fent nevezett metódusok jóval hatékonyabbak, mint régebben.
- Ezzel párhuzamosan a dict.iterkeys(), dict.itervalues(), és dict.iteritems() metódusok megszűntek.
- A map() és filter() beépített függvények már nem az eredménnyel térnek vissza, hanem egy iterátorral, aminek bejárása megadja az eredményt. Ha mégis lista kellene, akkor a készítők a list comprehension-ök használatát javasolják.
- az xrange() függvény megszűnik, a range() úgy viselkedik, ahogy régen az xrange(), azzal a bővítéssel, hogy tetszőleges méretű értékekkel tud dolgozni.
A zip() függvény is lista helyett iterátorral tér vissza, ennek a változásnak szintén hatékonysági okai vannak.
Rendezési relációk (összehasonlítások)
A Python 3.0 leegyszerűsíti a rendezési relációk szabályait:
- A rendezési összehasonlító operátorok (<, >, ≤, ≥) TypeError kivételt váltanak ki, ha az elemek között nincs természetes rendezés, illetve nincs köztük definiálva a reláció művelete. Például ezek a kifejezések: 1 < '', 0 > None vagy len <= len már nem érvényesek, továbbá pl. a None < None is TyperError-t vált ki ahelyett, hogy False-t adna. A logika e változtatás mögött az, hogy különböző típusú elemeket tartalmazó adatszerkezetet nincs értelme a rendezésnek, hiszen ahhoz minden elemnek összehasonlíthatónak kell lennie a másikkal. Azt ne felejtsük, hogy a == és != operátorok továbbra is alkalmazhatóak tetszőleges típusú operandusokra, nyilván a különböző típusú operandusokkal az egyenlőségvizsgálat mindig hamissal tér vissza.
- A beépített sorted() függvény, és a list.sort() metódus többé nem fogadja el a cmp keyword argumentumot, ami korábban az elemek közötti összehasonlító függvényt biztosította. Ehelyett a key keyword argumentumnak lehet megadni egy olyan egyparaméteres függvényt, amely egy adott elemnek visszatér az összehasonlítást képező alakjával. Példa: l.sort(key=str.lower), ahol a listában szereplő stringeket kisbetűs alakjuk szerint hasonlítjuk össze.
- A cmp() beépített függvény megszűnt, így a __cmp__() alapú összehasonlítás is. Rendezéshez használjuk a __lt__() metódust, az egyenlőséghez az __eq__()-t, illetve a __hash__()-t.
Integer-ek
- A long típus megszűnt, illetve egybe lett olvasztva az int-tel. Magyarán szólva az int egy tetszőleges hosszúságú egész értékeket reprezentáló típus lett.
- Az integer osztás jelentése megváltozott: az 1 / 2 kifejezés értéke immáron egy float típusú érték (0.5). Ha a jól megszokott egész csonkítást szeretnénk elérni, akkor a // operátort kell használnunk (1//2).
- A repr() függvény a long integerek elé nem teszi oda már az 'L' karaktert, a régi kód migrálásakor ezzel is számolni kell (ha pl. programmatikusan levettük az elejéről).
- Az oktális literáloknak megváltozott a formája: 0270 helyett 0o270-et kell írni.
Text vs. Data az Unicode vs. 8-bit helyett
Minden, ami az Unicode-dal és a bináris adattal kapcsolatos, a Pyton 3.0-ban megváltozott.
- A Python 3.0 a text (szöveges) és binary (bináris) adat koncepcióját alkalmazza a korábbi Unicode string és 8-bit string helyett. Minden szöveg Unicode; bár, az enkódolt Unicode szöveg bináris adatként van reprezentálva. A szöveg tárolásának típusa str, a bináris tárolásának típusa bytes. A leglényegesebb különbség a 2.X és a 3.0 között ezen a téren abban nyilvánul meg, hogy bármilyen kísérlet a text és binary adatok keverésére TypeError-t eredményez, míg a 2.X Pythonban ez működött, ha a 8-bit-es string kizárólag ASCII karaktereket tartalmazott.
- A fent említett változásnak köszönhetően minden, ami használja az Unicode stringeket, enkódolást vagy bináris adatokat, változnia kell a migráláskor. A fejlesztők azt ajánlják, hogy a programozók használják a unicode modult a nem kódolt szöveges tartalomhoz, az str modult pedig a kódolt vagy bináris szövegekhez, így a 2to3 modul az Unicode migrációt nagyjából automatikusan el tudja végezni.
- Nem lehet többé használni az u"..." alakú literálokat az Unicode szövegekhez (hiszen minden szöveg Unicode), viszont a bináris adathoz kötelező az b"..." alak.
- Mivel az str és a bytes típusokat nem lehet keverni, ezért a programozónak mindig explicit módon meg kell határoznia közöttük a konverziót, ahol az szükséges. Használd a str.encode() metódust, az str-ről bytes-a való konvertáláshoz, illetve a bytes.decode()-ot a másik irányhoz. A beépített bytes(s, encodeing=...) és str(b, encodeing=...) is használható a metódusok helyett.
- Az str-hez hasonlóan a bytes is immutable típus. Van egy mutable változat is a bináris adatok tárolására, a bytearray típus. Szinte minden API, ami paraméterként elfogadja a bytes típust, elfogadja a bytearray-t is.
- A beépített basestring absztrakt típus el lett távolítva, mivel a str és bytes típusoknak nincs sok közös tulajdonságuk. Helyette az str típus használatos.
- A text fájlként megnyitott fájlok (alapértelmezett mód) mindig használnak enkódolást a memóriában levő stringek és a diszk-en (fájlban) levő byte-ok között. A binary módban megnyitott fájlok a memóriában is mindig byte-okat használnak. Ennek a legfontosabb következménye az, hogy a rossz módban megnyitott fájlokon az I/O művelet kivételt fog dobni, míg korábban ez figyelmeztetés nélküli adat korrupcióhoz vezetett.
- A forrásállományok default kódolása a korábbi latin-1 helyett UTF-8 lett
- Az azonosítókban nem megengedett a nem-ASCII karakterek (pl ékezetes betűk) használata.
- A StringIO és cStringIO modulok megszűntek (alapjában véve a teljes IO kezelés át lett alakítva), helyettük az új io modul a használatos (io.StringIO és io.BytesIO az új típusoknak megfelelően.)
Szintaxis változások
- Új lehetőség a függvények paramétereinek és visszatérési értékeének típusának jelölése annotációk formájában. Ezen annotációkhoz nincs semmiféle szemantika rendelve, leszámítva a tényt, hogy futási időben elemződnek, és az eredmény metaadat bekerül a függvény objektum __annotations__ attribútumába. Egy egyszerű példa:
def haul(item: Haulable, *vargs: PackAnimal) -> Distance:
...
Az általános szintaxis:
def foo(a: expression, b: expression = 5) -> expresion:
...
Tehát látható, hogy az annotációk tetszőleges kifejezések lehetnek (lehet osztályobjektum vagy string, de akár egy aritmetikai kifejezés is!). A kifejezések kiértékelődnek, és eredményük bekerül a func_annotation nevű dictionary-be. Példa:
def foo(a: 'x', b: 5 + 6, c: list) -> max(2, 9):
...
, amihez tartozó func_annotation dictionary:
{'a': 'x',
'b': 11,
'c': list,
'return': 9}
Ez a dictionary futás közben lekérdezhető, felhasználható, így lehetőség van akár a futás idejű típusellenőrzésre is. Lehetőséget ad továbbá ez az újítás a függvények túlterhelésére is.
- Csak kulcsszavas argumentumok. Az első változtatás igen egyszerű: lehetővé teszi, hogy normális (kizárólag kulcsszavas) argumentumok szerepeljenek a változó számú argumentumok után. Például most már az alábbi definíció helyes:
def sortwords(*wordlist, case_sensitive=False):
...
Ez a függvény elfogad változó számban tetszőleges argumentumokat, valamint elfogadja a kulcsszavas case_sensitive argumentumot is (ami viszont soha sem lesz az argumentum-listában).
A második változtatás az azonosító nélküli *-os argumentum használata, ami lehetővé teszi a kizárólag kulcsszavas paraméterátadást. Az alábbi példában a key argumentumot nem lehet pozíció szerint megfeleltetni, kizárólag név szerint:
def compare(a, b, *, key=None):
...
- Lista és kulcsszavas argumentumok megengedettek az osztálydefiníciók ősosztály-deklarációjánál, amit az új metaclass-séma fel tud dolgozni. Ezzel gyakorlatilag az ősosztályok megadása dinamikusan, futási időben is történhet. Erről részletesebben nem írunk egyelőre...
- A nonlocal utasítás. A nonlocal x használatával lehetőség van arra, hogy egy külső (de nem globális) hatókör változójának értékét használjuk fel, illetve adjunk értéket neki.
- Kibővített iterált-kicsomagolás. Már értelmezettek *-os kicsomagoló értékadások is: a, b, *rest = some_sequence, ahol some_sequence legalább két elemű sorozat, és a 3. és további elemek a rest listába kerülnek. Például az (a, *rest, b) = range(5) értékadás hatására az a értéke 0, a b értéke 4, és a rest lista [1, 2, 3] lesz.
- Dictionary comprehension-ök. {k:v for k, v in stuff} ugyanazt jelenti, mint dict(stuff), csak jóval rugalmasabb, hiszen a list comprehension-ökhöz hasonlóan szűrőfeltételek is beállíthatók.
- Halmaz literálok, pl {1, 2}. A {} literál továbbá is üres dictionary-t jelent, üres halmazt a set() függvényhívással tudunk létrehozni. A set comprehension-ök is támogatottak már: {x for x in stuff}.
- Új oktális literálok, például 0o720. A régi literál alak 0720 már nem használható.
- Új bináris literálok, például 0b1010, és egy ehhez tartozó beépített bin() függvény.
- Bytes literálokat a szóvégi b vagy B karakterrel tudunk létrehozni, ehhez tartozik a beépített bytes() függvény.
- Az as, és a with mostantól foglalt kulcsszavak.
- A True és a False kulcsszavak lettek.
- A list comprehension-ök nem támogatják mostantól a zárójel nélküli szintaxist, tehát ez a kód szintaktikailag helytelen: [... for var in item1, item2, item3], helyette ez a helyes: [... for var in (item1, item2, item3)].
- A Tuple függvényparaméter kicsomagolást megszűntették. Tehát a következő már nem helyes: def foo(a, (b, c)): .... Ehelyett például a következő használható: def foo(a, b_c): b, c = b_c ....
- A <> operátor megszűnt, nem-egyenlőség vizsgálatra kizárólag a != használható.
- A from module import * kizárólag modul szinten használható, függvény törzsekben már nem.
- A Classic osztályok megszűntek.
A with utasítás
A with utasítást a try... finally... típusú finalizáló tevékenységeket végző kódok leváltására vezették be. Az alapstruktúra a következő:
with expression [as variable]:
with-block
Az
expression kiértékelődik, és visszatér egy olyan objektummal, ami támogatja a context manager protokollt (rendelkezik a
__enter__() és
__exit__() metódusokkal). Mielőtt a
with-block végrehajtódna, azelőtt lefut az
__enter__() metódus, majd a blokk végrehajtása után lefut az
__exit__() metódus is.
A 2.6-os verziótól kezdődően számos Python objektum támogatja a context manager protokollt, például a file objektum is. Például:
with open('/etc/passwd', 'r') as f:
for line in f:
print line
... more processing code ...
Az utasítás végrehajtása után a fájl automatikusan be fog záródni, még akkor is, ha a for ciklus közben valamilyen kivétel lép fel.
A
threading modul lock-jai és és egyéb objektumai is támogatják a protokollt:
lock = threading.Lock()
with lock:
# Critical section of code
...
A lock-ot megszerzi a with-blokk, mielőtt végrehajtódna a törzs, és a végrehajtás után a lockot automatikusan el is engedi.
Hogyan írjunk context manager osztályokat? A protokoll lényegében a következő:
- Az expression kiértékelődik, ami egy olyan objektummal tér vissza, amely rendelkezik az __enter__() és __exit__() metódusokkal.
- Meghívódik a context manager __enter__() metódusa. A visszatért érték értékül adódik a var-nak. Ha nem szerepel as var, akkor a visszatérési érték egyszerűen nem lesz figyelembe véve.
- A with-block végrehajtódik
- Ha a blokkban kivétel váltódik ki, akkor meghívódik a context manager __exit__(type, value, traceback) metódusa, az kivétel adataival (típus, érték, stack trace info). A metódus visszatérési értéke határozza meg, hogy a kivétel újra kiváltódik-e: ha igaz értékű, akkor kiváltódik, ha hamis, akkor nem. Célszerű a kivételt újra kiváltani, hiszen ha nem így tennénk, akkor a with-block-ban sosem látnánk, hogy valójában hiba történt.
- Ha a blokkban nem váltódik ki kivétel, akkor is meghívódik az __exit__() metódus, de mindegyik paramétere None lesz.
Egy példa context managerre:
db_connection = DatabaseConnection()
with db_connection as cursor:
cursor.execute('insert into ...')
cursor.execute('delete from ...')
# ... more operations ...
class DatabaseConnection:
# Database interface
def cursor(self):
"Returns a cursor object and starts a new transaction"
def commit(self):
"Commits current transaction"
def rollback(self):
"Rolls back current transaction"
def __enter__(self):
# Code to start a new transaction
cursor = self.cursor()
return cursor
def __exit__(self, type, value, tb):
if tb is None:
# No exception, so commit
self.commit()
else:
# Exception occurred, so rollback.
self.rollback()
# return False
Library változások
Az alábbiakban felsoroljuk a legfontosabb library változásokat a 2.X verzió óta. Ez közel sem egy teljes lista, csupán egy összefoglalása annak!
- Számos régi modul el lett távolítva. Ilyen például a gopherlib, az md5 (a hashlib váltotta fel). Más modulok azért lettek eltávolítva, mert jónéhány platform támogatása megszűnt (Mac OS 9, Irix, BeOS). Más modulok azért lettek kivéve a 3.0-ból, mert egy jobb alternatíva létezik helyettük. A teljes lista ezen a linken található: Library változások.
- A standard library-k közül eltávolították a bsddb3 package-t, mivel a core developer-ek számára folyamatos akadályt jelentett a Berkeley DB release-ek határidejeinek követése, és a tesztek instabilitása. Ettől függetlenül externális package-ként továbbra is fejlesztik a library-t.
- Néhány modul át lett nevezve, mivel az újonnan bevezetett szabványnak ellentmond a névhasználatuk. A _winreg winreg-re, a ConfigParser configparser-re, a copy_reg copyreg-re, a Queue queue-ra, a SocketServer socketserver-re lett átnevezve.
- A 2.X-es verzióban jellemző, hogy egy modul egyik verziója tisztán Pythonban, míg egy opcionális, gyorsított verzió C-ben van implementálva, mint például a pickle és a cPickle. Ez a kettősség gátat szab annak, hogy importáljuk a gyorsabb verziót, probléma esetén pedig a lassabb változatot használjuk. A 3.0-ás verziótól kezdődően ez a viselkedés automatikus: a programozónak mindig a sztandard verziót kell implementálni, ami megpróbálja használni a gyorsított verziót, probléma esetén pedig a lassabb változatot fogja használni.
- Számos egymással rokon modul csoportosítva lett egy modulba. Ezek:
- dbm (anydbm, dbhash, dbm, dumbdbm, gdbm, whichdb).
- html (HTMLParser, htmlentitydefs).
- http (httplib, BaseHTTPServer, CGIHTTPServer, SimpleHTTPServer, Cookie, cookielib).
- tkinter. Minden tkinter-rel kapcsolatos modul, kivéve a turtle.
- urllib (urllib, urllib2, urlparse, robotparse).
- xmlrpc (xmlrpclib, DocXMLRPCServer, SimpleXMLRPCServer).
- Néhány egyéb, library-t érintő változtatás:
- A sets modult kivették, helyette a set() beépített adattípus használható.
- A sys modul kitakarítása: eltávolították a sys.exitfunc(), sys.exc_clear(), sys.exc_type(), sys.exc_value() és sys.exc_traceback() függvényeket.
- Az array.array típust is kitakarították: megszűnt a redundáns read és write metódus (fromfile és tofile hazsnálható).
- És még jópár modulban kisebb-nagyobb változtatások, lásd a 3.0-ás library dokumentációban.
String formázás
A % operátoros formázás a 3.1-ben elavulttá válik, egy későbbi verzióban pedig végleg meg fog szűnni. E helyett a 2.6-os verzióban már bevezetett format() beépített függvény, illetve a string-ek format() metódusa használható szöveg formázásra. Itt nem kívánjuk részletezni a % operátoros, illetve a format() metódus általi formázás különbségeit.
Kivétel kezelés
További mellékes változtatások
- Új I/O library. Mostantól az io modul biztosítja a sztandard IO műveleteket, és a sys.stdin, sys.stdout és sys.stderr kezdeti értékei már az io.TextIOBase osztály példányai. A beépített open() függvény egy alias az io.open() függvényre, és további keyword argumentumokat vezet be: encoding, errors, newline és closefd
- A __getslice__(), __setslice__() és __delslice__() függvények megszűntek. Mostantól az a[i:j] kifejezés erre fordul le: a.__getitem__(slice(i, j)) (illetve __setitem__ és __delitem__ a használattól függően.)
- A __nonzero__ speciális metódus mostantól __bool__ névre hallgat.
- És még számos más apróbb változtatás a beépített függvények és library-k terén :-)