Pouczające fragmenty kodu w języku Python
Radomir Dopieralski (STX Next)
pycon@sheep.art.pl
PyCon.pl 2010
>>> import this
...
Identyczność to nie to samo co równość:
>>> 1 == 1.0
True
>>> 1 is 1.0
False
>>> [1] == [1]
True
>>> [1] is [1]
False
Zawsze używamy 'is' i 'is not' do porównywania z None:
>>> class EqualToEverything(object):
... def __eq__(self, to_what):
... return True
>>> EqualToEverything() == None
True
>>> EqualToEverything() is None
False
Zmienne nie są pojemnikami, tylko etykietami
>>> x = [1]
>>> y = x
>>> x is y
True
>>> y = [1]
>>> x is y
False
>>> list1 = [1]
>>> list2 = list1 # Nie tworzy kopii!
>>> list1.append(2)
>>> list1
[1, 2]
>>> list2
[1, 2]
>>> list2 = [3, 4]
>>> list1.append(5)
>>> list1
[1, 2, 5]
>>> list2
[3, 4]
Parametry są przekazywane przez referencję:
Źle:
>>> def empty_list(what):
... what = []
>>> list1 = [1, 2, 3]
>>> empty_list(list1)
>>> list1
[1, 2, 3]
Dobrze:
>>> def empty_list(what):
... what[:] = []
>>> list1 = [1, 2, 3]
>>> empty_list(list1)
>>> list1
[]
Źle:
>>> if x == True:
>>> if x != []:
>>> if x == []:
>>> if x == {}:
>>> if len(x) == 0:
>>> if x == '':
>>> return True if x else False
Dobrze:
>>> if x:
>>> if not x:
>>> return x
>>> return bool(x)
Źle:
>>> ...
>>> if 0 < x and x < 5:
... ...
Dobrze:
>>> ...
>>> if 0 < x < 5:
... ...
Przy okazji:
>>> None < float('-inf') < 0 < float('+inf')
True
Uwaga, () jest większy od większości obiektów, ale to przypadek implementacyjny, nie należy tego uzywać.
>>> a = b = c = 0
>>> a, b, c
(0, 0, 0)
Uwaga!
>>> a = b = c = []
>>> a.append(1)
>>> a, b, c
([1], [1], [1])
>>> a, b, c = 1, 2, 3
>>> a, b, c
(1, 2, 3)
>>> FOO, BAR, BAZ = range(3)
>>> BAR
1
>>> head, tail = 'a, b, c'.split(',', 1)
>>> head, tail
('a', ' b, c')
>>> a, b = 1, 2
>>> b, a = a, b
>>> a, b
(2, 1)
>>> a, b = 1, 2, 3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: too many values to unpack
>>> for i in [1, 2, 3]:
... print 'a'
... else:
... print 'b'
...
a
a
a
b
>>> for i in [1, 2, 3]:
... print 'a'
... break;
... else:
... print 'b'
...
a
>>> hello = "Hello world!"
>>> hello[0]
'H'
>>> hello[0:5]
'Hello'
>>> hello[-1]
'!'
>>> hello[1:-1]
'ello world'
>>> hello[:5]+hello[5:]
'Hello world!'
>>> hello[::2]
'Hlowrd'
>>> hello[::-1]
'!dlrow olleH'
>>> a = [1, 2, 3, 4]
>>> a[1:3] = [5, 6, 7, 8]
>>> a
[1, 5, 6, 7, 8, 4]
>>> s = list('abc')
>>> s[1:2] = 'ABC'
>>> ''.join(s)
'aABCc'
>>> input = u'yap foo yup bar yep yelp'
>>> for prev, this, next in zip(input, input[1:], input[2:]):
... if prev.lower() == 'y' and next.lower() == 'p':
... print prev+this+next
...
yap
yup
yep
>>> [x+1 for x in (1, 2, 3)]
[2, 3, 4]
>>> [x for x in [1, 2, 3, 4] if x % 2]
[1, 3]
>>> ['(%s)' % c for c in 'a.b.c' if c != '.']
['(a)', '(b)', '(c)']
>>> [x for y in [[1, 2, 3], [4, 5, 6], [7, 8, 9]] for x in y]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> [part.strip() for part in
... 'a, b, c, '.split(',') if part.strip()]
['a', 'b', 'c']
>>> a = [(0, 1), (3, 0), (1, 2)]
>>> a.sort()
>>> a
[(0, 1), (1, 2), (3, 0)]
>>> import operator
>>> a.sort(key=operator.itemgetter(1))
>>> a
[(3, 0), (0, 1), (1, 2)]
>>> sorted(a)
[(0, 1), (1, 2), (3, 0)]
>>> a
[(3, 0), (0, 1), (1, 2)]
>>> sum([1, 2, 3])
6
>>> max([1, 2, 3])
3
Źle:
>>> sum([0.0000000001]*10000000)
0.0010000000001300626
Dobrze:
>>> import math
>>> math.fsum([0.0000000001]*10000000)
0.001
Źle:
>>> a = ['a', 'b', 'c']
>>> for i in xrange(len(a)):
>>> print a[i]
Dobrze:
>>> for item in ['a', 'b', 'c']:
>>> print item
>>> class MyIter(object):
... def __init__(self):
... self.count = 0
... def next(self):
... self.count += 1
... if self.count > 3:
... raise StopIteration()
... return self.count
...
>>> class MyIterable(object):
... def __iter__(self):
... return MyIter()
...
>>> for i in MyIterable():
... print i
...
1
2
3
>>> class MyIterable(object):
... def __iter__(self):
... yield 1
... yield 2
... yield 3
...
>>> for i in MyIterable():
... print i
...
1
2
3
>>> def parenthize(what):
... for item in what:
... yield '(%s)' % item
...
>>> ', '.join(parenthize('abc'))
'(a), (b), (c)'
>>> g = (c for c in 'a.b.c.d' if c != '.')
>>> list(g)
['a', 'b', 'c']
>>> list(g)
[]
>>> g = iter('abcd')
>>> for a, b in zip(g, g):
... print a, b
...
a b
c d
>>> g = iter('abcd')
>>> for i in g:
... print i, g.next()
...
a b
c d
>>> for i, c in enumerate('abc'):
... print i, c
...
0 a
1 b
2 c
>>> def integers():
... i = 0
... while True:
... yield i
... i += 1
...
>>> for i, c in zip(integers(), 'abc'):
... print i, c
...
0 a
1 b
2 c
Funkcje mozna przekazywac jako zmienne i parametry:
>>> def foo(name):
... return 'hello %s' % name
>>> bar = foo
>>> bar('world')
'hello world'
>>> functions = {
... 'foo': foo,
>>> }
>>> functions['foo']('world')
'hello world'
Łatwo zapomniec wywolac funckje:
>>> f = open('filename')
>>> data = f.read()
>>> f.close
<built-in method close of file object at 0xb786a020>
>>> f.close()
Dobrze:
>>> def function(foo=1, bar=None):
... if bar is None:
... bar = []
Źle:
>>> def append_one(where=[]):
... where.append(1)
... return where
>>> append_one()
[1]
>>> append_one()
[1, 1]
>>> def function(first, second, *args, **kwargs):
... return first, second, args, kwargs
>>> function(1, 2, 3, 4, 5, foo=6, bar=7)
(1, 2, [3, 4, 5], {'foo': 6, 'bar':7})
Źle:
>>> foo = {'bar': 2}
>>> try:
... print fooo['bar']
>>> except:
... print "nie ma bar"
Dobrze:
>>> foo = {'bar': 2}
>>> try:
... print fooo['bar']
>>> except KeyError:
... print "nie ma bar"
Źle:
>>> foo = {'bar': 2}
>>> try:
... print foo['bar']
... baz = foo['bar'] + foo['foo']
>>> except KeyError:
... print "nie ma bar"
Dobrze:
>>> foo = {'bar': 2}
>>> try:
... print foo['bar']
>>> except KeyError:
... print "nie ma bar"
>>> else:
... baz = foo['bar'] + foo['foo']
Źle:
>>> foo = {'bar': 2}
>>> if 'bar' in foo:
... bar = foo['bar']
>>> else:
... bar = 0
>>> bar = foo['bar'] if foo.has_key('foo') else 0
Dobrze:
>>> foo = {'bar': 2}
>>> try:
... bar = foo['bar']
>>> except KeyError:
... bar = 0
>>> bar = foo.get('bar', 0)
>>> f = open('filename')
>>> try:
... data = f.read()
>>> finally:
... f.close()
Po nowemu:
>>> from __future__ import with_statement
>>> with open('filename') as f:
... data = f.read()
Źle:
>>> try:
... f = open('filename')
>>> except OSError, IOError:
... # OSError jest łapany i przypisywany do zmiennej IOError
Dobrze:
>>> try:
... f = open('filename')
>>> except (OSError, IOError):
... print 'something went wrong'
>>> try:
... f = open('filename')
>>> except IOError, e:
... if e.errno == 2:
... print "nie ma pliku"
... else:
... raise
>>> except OSError:
... print "blad systemowy"
>>> d1 = {
... 'foo': 1,
... 'bar': 2,
... }
>>> d2 = dict(baz=3, bop=4)
>>> d3 = dict((k, v) for k, v in d.iteritems() if v % 2)
>>> d3
{'foo': 1}
>>> d3.update(d2)
>>> d3
{'foo': 1, 'bar': 2}
>>> d3.update(baz=3)
>>> d = {}
>>> for a, b in [(1, 'a'), (1, 'b'), (2, 'a')]:
... d.setdefault(a, []).append(b)
...
>>> d
{1: ['a', 'b'], 2: ['a']}
>>> import collections
>>> d = collections.defaultdict(list)
>>> for a, b in [(1, 'a'), (1, 'b'), (2, 'a')]:
... d[a].append(b)
...
>>> d
defaultdict(<type 'list'>, {1: ['a', 'b'], 2: ['a']})
>>> stack = [root]
>>> while stack:
>>> node = stack.pop()
>>> for child in node.children:
>>> stack.append(child)
>>> print node
Źle:
>>> queue = [root]
>>> while queue:
>>> node = queue.pop(0)
>>> for child in node.children:
>>> queue.append(child)
>>> print node
Dobrze:
>>> import collections
>>> queue = collections.deque([root])
>>> while queue:
>>> node = queue.popleft()
>>> for child in node.children:
>>> queue.append(child)
>>> print node
>>> def make_adder(additive):
... def adder(what):
... return what + additive
...
... return adder
...
>>> adder = make_adder(3)
>>> adder(2)
5
>>> def get_closures():
... closures = []
... for c in ['A', 'B', 'C']:
... def closure():
... return c
...
... closures.append(closure)
...
... return closures
...
>>> for c in get_closures():
>>> print c()
C
C
C
Źle:
>>> class Foo(object):
... foo = 1
... bar = 2
...
>>> foo = Foo()
>>> attr = 'foo'
>>> x = eval('foo.%s' % attr)
>>> y = 3
>>> exec('foo.%s = %s' % (attr, repr(y))
Dobrze:
>>> x = getattr(foo, attr)
>>> setattr(foo, attr, y)
Źle:
>>> data = open('config.cfg').read()
>>> config = eval(data)
Tak sobie:
>>> import ast
>>> config = ast.literal_eval(data)
Dobrze:
>>> import json
>>> config = json.loads(data)
>>> config = json.load(open('config.cfg'))
>>> marked = {}
>>> def mark(func):
... marked[func.__name__] = func
... return func
...
>>> @mark
... def foo(bar):
... return bar
...
>>> marked
{'foo': <function foo at 0xb753c72c>}
>>> class Foo(object):
... bar = 3
... @property
... def foo(self):
... return self.bar + 1
...
... @foo.setter
... def foo(self, value):
... self.bar = value - 1
...
... @foo.deleter
... def foo(self):
... self.bar = 0
>>> class Base(object):
... def __init__(self, *args, **kwargs):
... print 'base'
...
>>> class A(Base):
... def __init__(self, *args, **kwargs):
... super(A, self).__init__(*args, **kwargs)
... print 'A'
...
>>> class B(Base):
... def __init__(self, *args, **kwargs):
... super(B, self).__init__(*args, **kwargs)
... print 'B'
...
>>> class C(A, B):
... pass
...
>>> c = C()
base
B
A