Vele beginnende Python-gebruikers vragen zich af met welke versie van Python ze moeten beginnen. Mijn antwoord op deze vraag is meestal iets in de trant van “ga gewoon voor de versie waarin je favoriete tutorial is geschreven, en bekijk later de verschillen.”
Maar wat als je een nieuw project begint en de keuze hebt om te kiezen? Ik zou zeggen dat er op dit moment geen “goed” of “fout” is, zolang zowel Python 2.7.x als Python 3.x de bibliotheken ondersteunen die je van plan bent te gebruiken. Het is echter de moeite waard om eens te kijken naar de belangrijkste verschillen tussen deze twee meest populaire versies van Python om veel voorkomende valkuilen te vermijden bij het schrijven van de code voor een van beide, of als u van plan bent om uw project te porten.
Secties
- Secties
- De
__future__
module - De printfunctie
- Python 2
- Python 3
- Python 2
- Python 3
- Python 2
- Python 3
- xrange
- Python 2
- Python 3
- De
__contains__
methode voorrange
objecten in Python 3- Note over de snelheidsverschillen in Python 2 en 3
- Roepen uitzonderingen op
- Python 2
- Python 3
- Behandeling van uitzonderingen
- Python 2
- Python 3
- Python 2
- Python 3
Elkaar delen
Unicode
- Python 2
- Python 3
- Python 2
- Python 3
- Python 2
- Python 3
Retureren van iterabele objecten in plaats van lijsten
- Python 2
- Python 3
- Python 2
- Python 3
De __future__ module
Python 3.x introduceerde enkele Python 2-incompatibele sleutelwoorden en functies die kunnen worden geïmporteerd via de ingebouwde __future__
module in Python 2. Het is aanbevolen om __future__
te importeren als u Python 3.x ondersteuning voor uw code plant. Bijvoorbeeld, als we Python 3.x’s integer division gedrag in Python 2 willen, kunnen we het importeren via
from __future__ import division
Meer functies die kunnen worden geïmporteerd uit de __future__
module staan in de onderstaande tabel:
feature | optional in | mandatory in | effect |
---|---|---|---|
nested_scopes | 2.1.0b1 | 2.2 | PEP 227:Statically Nested Scopes |
generators | 2.2.0a1 | 2.3 | PEP 255:Simple Generators |
division | 2.2.0a2 | 3.0 | PEP 238:De delingsoperator veranderen |
absolute_import | 2.5.0a1 | 3.0 | PEP 328:Imports: Multi-line and Absolute/Relative |
with_statement | 2.5.0a1 | 2.6 | PEP 343:The “with” Statement |
print_function | 2.6.0a2 | 3.0 | PEP 3105:Maak van print een functie |
unicode_literals | 2.6.0a2 | 3.0 | PEP 3112:Bytes literals in Python 3000 |
from platform import python_version
De printfunctie
Zeer triviaal, en de verandering in de print-syntax is waarschijnlijk de meest bekende verandering, maar toch is het het vermelden waard: Python 2’s print statement is vervangen door de print()
functie, wat betekent dat we het object dat we willen printen moeten wikkelen in parantheses.
Python 2 heeft geen probleem met extra parantheses, maar Python 3 daarentegen zou een SyntaxError
geven als we de print-functie de Python 2-weg aanroepen zonder de haakjes.
Python 2
print 'Python', python_version()print 'Hello, World!'print('Hello, World!')print "text", ; print 'print more text on the same line'
Python 2.7.6Hello, World!Hello, World!text print more text on the same line
Python 3
print('Python', python_version())print('Hello, World!')print("some text,", end="")print(' print more text on the same line')
Python 3.4.1Hello, World!some text, print more text on the same line
print 'Hello, World!'
File "<ipython-input-3-139a7c5835bd>", line 1 print 'Hello, World!' ^SyntaxError: invalid syntax
Note:
Het afdrukken van “Hello, World” hierboven via Python 2 zag er heel “normaal” uit. Als we echter meerdere objecten binnen de parantheses hebben, maken we een tuple, aangezien print
een “statement” is in Python 2, en geen functie-aanroep.
print 'Python', python_version()print('a', 'b')print 'a', 'b'
Python 2.7.7('a', 'b')a b
Geïntegreerde deling
Deze wijziging is vooral gevaarlijk als u code port, of als je Python 3 code uitvoert in Python 2, omdat de verandering in het delingsgedrag van gehele getallen vaak onopgemerkt blijft (het geeft geen SyntaxError
).
Dus heb ik nog steeds de neiging om een float(3)/2
of 3/2.0
te gebruiken in plaats van een 3/2
in mijn Python 3 scripts om de Python 2 jongens wat moeite te besparen (en vice versa, adviseer ik een from __future__ import division
in uw Python 2 scripts).
Python 2
print 'Python', python_version()print '3 / 2 =', 3 / 2print '3 // 2 =', 3 // 2print '3 / 2.0 =', 3 / 2.0print '3 // 2.0 =', 3 // 2.0
Python 2.7.63 / 2 = 13 // 2 = 13 / 2.0 = 1.53 // 2.0 = 1.0
Python 3
print('Python', python_version())print('3 / 2 =', 3 / 2)print('3 // 2 =', 3 // 2)print('3 / 2.0 =', 3 / 2.0)print('3 // 2.0 =', 3 // 2.0)
Python 3.4.13 / 2 = 1.53 // 2 = 13 / 2.0 = 1.53 // 2.0 = 1.0
Unicode
Python 2 heeft ASCII str()
types, aparte unicode()
, maar geen byte
type.
Nu, in Python 3, hebben we eindelijk Unicode (utf-8) str
ings, en 2 byte klassen: byte
en bytearray
s.
Python 2
print 'Python', python_version()
Python 2.7.6
print type(unicode('this is like a python3 str type'))
<type 'unicode'>
print type(b'byte type does not exist')
<type 'str'>
print 'they are really' + b' the same'
they are really the same
print type(bytearray(b'bytearray oddly does exist though'))
<type 'bytearray'>
Python 3
print('Python', python_version())print('strings are now utf-8 \u03BCnico\u0394é!')
Python 3.4.1strings are now utf-8 μnicoΔé!
print('Python', python_version(), end="")print(' has', type(b' bytes for storing data'))
Python 3.4.1 has <class 'bytes'>
print('and Python', python_version(), end="")print(' also has', type(bytearray(b'bytearrays')))
and Python 3.4.1 also has <class 'bytearray'>
'note that we cannot add a string' + b'bytes for data'
---------------------------------------------------------------------------TypeError Traceback (most recent call last)<ipython-input-13-d3e8942ccf81> in <module>()----> 1 'note that we cannot add a string' + b'bytes for data'TypeError: Can't convert 'bytes' object to str implicitly
xrange
Het gebruik van xrange()
is erg populair in Python 2.x voor het maken van een iterabel object, bijv, in een for-lus of list/set-dictionary-comprehension.
Het gedrag leek erg op dat van een generator (d.w.z., “lazy evaluation”), maar hier is de xrange-iterable niet uitputbaar – wat betekent dat je er oneindig over zou kunnen itereren.
Dankzij de “lazy-evaluation”, is het voordeel van de gewone range()
dat xrange()
over het algemeen sneller is als je er maar 1 keer over hoeft te itereren (b.v. in een for-loop). In tegenstelling tot eenmalige iteraties is het echter niet aan te raden als je de iteratie meerdere keren herhaalt, omdat de generatie elke keer van voren af aan gebeurt!
In Python 3, is de range()
geïmplementeerd als de xrange()
functie, zodat een speciale xrange()
functie niet meer bestaat (xrange()
roept een NameError
op in Python 3).
import timeitn = 10000def test_range(n): return for i in range(n): passdef test_xrange(n): for i in xrange(n): pass
Python 2
print 'Python', python_version()print '\ntiming range()'%timeit test_range(n)print '\n\ntiming xrange()'%timeit test_xrange(n)
Python 2.7.6timing range()1000 loops, best of 3: 433 µs per looptiming xrange()1000 loops, best of 3: 350 µs per loop
Python 3
print('Python', python_version())print('\ntiming range()')%timeit test_range(n)
Python 3.4.1timing range()1000 loops, best of 3: 520 µs per loop
print(xrange(10))
---------------------------------------------------------------------------NameError Traceback (most recent call last)<ipython-input-5-5d8f9b79ea70> in <module>()----> 1 print(xrange(10))NameError: name 'xrange' is not defined
De __contains__ methode voor reeksobjecten in Python 3
Een ander vermeldenswaardig ding is dat range
een “nieuwe” __contains__
methode heeft gekregen in Python 3.x (met dank aan Yuchen Ying, die ons hierop wees). De __contains__
methode kan “look-ups” in Python 3.x range
aanzienlijk versnellen voor integer en Boolean types.
x = 10000000
def val_in_range(x, val): return val in range(x)
def val_in_xrange(x, val): return val in xrange(x)
print('Python', python_version())assert(val_in_range(x, x/2) == True)assert(val_in_range(x, x//2) == True)%timeit val_in_range(x, x/2)%timeit val_in_range(x, x//2)
Python 3.4.11 loops, best of 3: 742 ms per loop1000000 loops, best of 3: 1.19 µs per loop
Gebaseerd op de timeit
resultaten hierboven, zie je dat de uitvoering voor de “look up” ongeveer 60.000 sneller was wanneer het een integer type was in plaats van een float. Echter, sinds Python 2.x’s range
of xrange
geen __contains__
methode heeft, zou de “opzoeksnelheid” niet veel verschillen voor gehele getallen of vlottende getallen:
print 'Python', python_version()assert(val_in_xrange(x, x/2.0) == True)assert(val_in_xrange(x, x/2) == True)assert(val_in_range(x, x/2) == True)assert(val_in_range(x, x//2) == True)%timeit val_in_xrange(x, x/2.0)%timeit val_in_xrange(x, x/2)%timeit val_in_range(x, x/2.0)%timeit val_in_range(x, x/2)
Python 2.7.71 loops, best of 3: 285 ms per loop1 loops, best of 3: 179 ms per loop1 loops, best of 3: 658 ms per loop1 loops, best of 3: 556 ms per loop
Hieronder de “bewijzen” dat de methode __contain__
nog niet aan Python 2.x is toegevoegd:
__contain__
.x nog niet is toegevoegd:
print('Python', python_version())range.__contains__
Python 3.4.1<slot wrapper '__contains__' of 'range' objects>
print 'Python', python_version()range.__contains__
Python 2.7.7---------------------------------------------------------------------------AttributeError Traceback (most recent call last)<ipython-input-7-05327350dafb> in <module>() 1 print 'Python', python_version()----> 2 range.__contains__AttributeError: 'builtin_function_or_method' object has no attribute '__contains__'
print 'Python', python_version()xrange.__contains__
Python 2.7.7---------------------------------------------------------------------------AttributeError Traceback (most recent call last)<ipython-input-8-7d1a71bfee8e> in <module>() 1 print 'Python', python_version()----> 2 xrange.__contains__AttributeError: type object 'xrange' has no attribute '__contains__'
Note over de snelheidsverschillen in Python 2 en 3
Sommigen hebben gewezen op het snelheidsverschil tussen Python 3’s range()
en Python2’s xrange()
. Aangezien ze op dezelfde manier zijn geïmplementeerd zou men dezelfde snelheid verwachten. Maar het verschil hier komt gewoon uit het feit dat Python 3 over het algemeen langzamer draait dan Python 2.
def test_while(): i = 0 while i < 20000: i += 1 return
print('Python', python_version())%timeit test_while()
Python 3.4.1100 loops, best of 3: 2.68 ms per loop
print 'Python', python_version()%timeit test_while()
Python 2.7.61000 loops, best of 3: 1.72 ms per loop
Uitzonderingen oproepen
Waar Python 2 beide notaties accepteert, de ‘oude’ en de ‘nieuwe’ syntaxis, verslikt Python 3 zich (en roept op zijn beurt een SyntaxError
op) als we het argument van de uitzondering niet tussen haakjes plaatsen:
Python 2
print 'Python', python_version()
Python 2.7.6
raise IOError, "file error"
---------------------------------------------------------------------------IOError Traceback (most recent call last)<ipython-input-8-25f049caebb0> in <module>()----> 1 raise IOError, "file error"IOError: file error
raise IOError("file error")
---------------------------------------------------------------------------IOError Traceback (most recent call last)<ipython-input-9-6f1c43f525b2> in <module>()----> 1 raise IOError("file error")IOError: file error
Python 3
print('Python', python_version())
Python 3.4.1
raise IOError, "file error"
File "<ipython-input-10-25f049caebb0>", line 1 raise IOError, "file error" ^SyntaxError: invalid syntax
De juiste manier om een uitzondering op te roepen in Python 3:
print('Python', python_version())raise IOError("file error")
Python 3.4.1---------------------------------------------------------------------------OSError Traceback (most recent call last)<ipython-input-11-c350544d15da> in <module>() 1 print('Python', python_version())----> 2 raise IOError("file error")OSError: file error
Handling exceptions
Ook de afhandeling van uitzonderingen is in Python 3 enigszins veranderd. In Python 3 moeten we nu het “as
” sleutelwoord gebruiken
Python 2
print 'Python', python_version()try: let_us_cause_a_NameErrorexcept NameError, err: print err, '--> our error message'
Python 2.7.6name 'let_us_cause_a_NameError' is not defined --> our error message
Python 3
print('Python', python_version())try: let_us_cause_a_NameErrorexcept NameError as err: print(err, '--> our error message')
Python 3.4.1name 'let_us_cause_a_NameError' is not defined --> our error message
Omdat next()
.next()
) zo’n veelgebruikte functie (methode) is, is dit een andere syntaxiswijziging (of liever wijziging in de implementatie) die het vermelden waard is: waar je zowel de functie als de methode syntaxis kunt gebruiken in Python 2.7.5, is de next()
functie het enige dat overblijft in Python 3 (het aanroepen van de .next()
methode roept een AttributeError
op).
Python 2
print 'Python', python_version()my_generator = (letter for letter in 'abcdefg')next(my_generator)my_generator.next()
Python 2.7.6'b'
Python 3
print('Python', python_version())my_generator = (letter for letter in 'abcdefg')next(my_generator)
Python 3.4.1'a'
my_generator.next()
---------------------------------------------------------------------------AttributeError Traceback (most recent call last)<ipython-input-14-125f388bb61b> in <module>()----> 1 my_generator.next()AttributeError: 'generator' object has no attribute 'next'
For-loop-variabelen en het lek in de wereldwijde naamruimte
Goed nieuws is: In Python 3.x lekken for-loop variabelen niet meer naar de globale namespace!
Dit gaat terug op een wijziging die in Python 3.x is doorgevoerd en in What’s New In Python 3.0 als volgt wordt beschreven:
“List comprehensions ondersteunen niet langer de syntactische vorm . Gebruik in plaats daarvan
. Merk ook op dat list comprehensions een andere semantiek hebben: ze zijn dichter bij syntactische suiker voor een generator expressie binnen een
list()
constructor, en in het bijzonder worden de loop control variabelen niet langer gelekt in de omringende scope.”
Python 2
print 'Python', python_version()i = 1print 'before: i =', iprint 'comprehension: ', print 'after: i =', i
Python 2.7.6before: i = 1comprehension: after: i = 4
Python 3
print('Python', python_version())i = 1print('before: i =', i)print('comprehension:', )print('after: i =', i)
Python 3.4.1before: i = 1comprehension: after: i = 1
Vergelijken van niet-ordenbare typen
Een andere leuke verandering in Python 3 is dat er een TypeError
als waarschuwing wordt gegeven als we niet-ordenbare typen proberen te vergelijken.
Python 2
print 'Python', python_version()print " > 'foo' = ", > 'foo'print "(1, 2) > 'foo' = ", (1, 2) > 'foo'print " > (1, 2) = ", > (1, 2)
Python 2.7.6 > 'foo' = False(1, 2) > 'foo' = True > (1, 2) = False
Python 3
print('Python', python_version())print(" > 'foo' = ", > 'foo')print("(1, 2) > 'foo' = ", (1, 2) > 'foo')print(" > (1, 2) = ", > (1, 2))
Python 3.4.1---------------------------------------------------------------------------TypeError Traceback (most recent call last)<ipython-input-16-a9031729f4a0> in <module>() 1 print('Python', python_version())----> 2 print(" > 'foo' = ", > 'foo') 3 print("(1, 2) > 'foo' = ", (1, 2) > 'foo') 4 print(" > (1, 2) = ", > (1, 2))TypeError: unorderable types: list() > str()
Parsing user inputs via input()
Gelukkig maar waar, is de input()
-functie in Python 3 zodanig aangepast dat deze de gebruikersinvoer altijd opslaat als str
-objecten. Om het gevaarlijke gedrag in Python 2 te vermijden om andere types dan strings
in te lezen, moeten we in plaats daarvan raw_input()
gebruiken.
Python 2
Python 2.7.6 op darwinType "help", "copyright", "credits" of "license" voor meer informatie.>>> my_input = input('voer een getal in: ')voer een getal in: 123>>> type(mijn_input)<type 'int'>>>> my_input = raw_input('voer een getal in: ')voer een getal in: 123>>> type(mijn_input)<type 'str'>
Python 3
Python 3.4.1 op darwinType "help", "copyright", "credits" of "license" voor meer informatie.>>> my_input = input('voer een getal in: ')voer een getal in: 123>>> type(mijn_input)<klasse 'str'>
Retourneer iterable objecten in plaats van lijsten
Zoals we al gezien hebben in het xrange
gedeelte, geven sommige functies en methoden in Python 3 nu iterabele objecten terug – in plaats van lijsten in Python 2.
Omdat we daar meestal toch maar één keer overheen itereren, denk ik dat deze verandering heel zinvol is om geheugen te besparen. Het is echter ook mogelijk – in tegenstelling tot generatoren – om er meerdere keren overheen te itereren als dat nodig is, het is alleen niet zo efficiënt.
En voor die gevallen waarin we de list
-objecten echt nodig hebben, kunnen we het iterable object gewoon omzetten in een list
via de list()
-functie.
Python 2
print 'Python', python_version()print range(3)print type(range(3))
Python 2.7.6<type 'list'>
Python 3
print('Python', python_version())print(range(3))print(type(range(3)))print(list(range(3)))
Python 3.4.1range(0, 3)<class 'range'>
Enkele veelgebruikte functies en methoden die in Python 3 geen lijsten meer teruggeven:
-
zip()
-
map()
-
filter()
-
dictionary’s
.keys()
methode -
dictionary’s
.values()
methode -
dictionary’s
.items()
methode
Banker’s Rounding
Python 3 heeft de nu standaard manier van afronden van decimalen aangenomen wanneer het resulteert in een gelijkspel (.5) bij de laatste significante cijfers. Nu, in Python 3, worden decimalen afgerond naar het dichtstbijzijnde even getal. Hoewel het een ongemak is voor de portabiliteit van de code, is het een betere manier van afronden dan afronden naar boven, omdat het de bias naar grote getallen vermijdt. Voor meer informatie, zie de uitstekende Wikipedia artikelen en paragrafen:
- https://en.wikipedia.org/wiki/Rounding#Round_half_to_even
- https://en.wikipedia.org/wiki/IEEE_floating_point#Roundings_to_nearest
Python 2
print 'Python', python_version()
Python 2.7.12
round(15.5)
16.0
round(16.5)
17.0
Python 3
print('Python', python_version())
Python 3.5.1
round(15.5)
16
round(16.5)
16
Meer artikelen over Python 2 en Python 3
Hier volgt een lijst met een aantal goede artikelen over Python 2 en 3 die ik zou aanraden als vervolg-vervolg.
/ Porten naar Python 3
-
Moet ik Python 2 of Python 3 gebruiken voor mijn ontwikkel-activiteit?
-
Porteren naar Python 3
-
Porteren van Python 2 code naar Python 3
- Hoe houd je Python 3 vooruit kan
Wat is er nieuw in Python 3.0
/ Pro en anti Python 3
- 10 geweldige functies van Python die je niet kunt gebruiken omdat je weigert te upgraden naar Python 3
-
Alles wat je niet wilde weten over Unicode in Python 3
-
Python 3 is killing Python
-
Python 3 kan Python doen herleven
-
Python 3 is prima