Commits

Miki Tebeka  committed 7028d19

Python Gotchas

  • Participants
  • Parent commits f3e381b

Comments (0)

Files changed (65)

File python-gotchas/Makefile

+html = python-gotchas.html
+css := div.linenodiv {border-right:1px solid gray;padding-right:2px;margin-right:2px;}
+
+
+all: $(html)
+
+$(html): python-gotchas.txt src/*.py
+	asciidoc -a stylesheet=${PWD}/style.css $<
+
+clean:
+	rm -f $(html)
+
+fresh: clean all
+
+.PHONY: all clean fresh

File python-gotchas/images/icons/callouts/1.png

Added
New image

File python-gotchas/images/icons/callouts/10.png

Added
New image

File python-gotchas/images/icons/callouts/11.png

Added
New image

File python-gotchas/images/icons/callouts/12.png

Added
New image

File python-gotchas/images/icons/callouts/13.png

Added
New image

File python-gotchas/images/icons/callouts/14.png

Added
New image

File python-gotchas/images/icons/callouts/15.png

Added
New image

File python-gotchas/images/icons/callouts/2.png

Added
New image

File python-gotchas/images/icons/callouts/3.png

Added
New image

File python-gotchas/images/icons/callouts/4.png

Added
New image

File python-gotchas/images/icons/callouts/5.png

Added
New image

File python-gotchas/images/icons/callouts/6.png

Added
New image

File python-gotchas/images/icons/callouts/7.png

Added
New image

File python-gotchas/images/icons/callouts/8.png

Added
New image

File python-gotchas/images/icons/callouts/9.png

Added
New image

File python-gotchas/images/icons/caution.png

Added
New image

File python-gotchas/images/icons/example.png

Added
New image

File python-gotchas/images/icons/home.png

Added
New image

File python-gotchas/images/icons/important.png

Added
New image

File python-gotchas/images/icons/next.png

Added
New image

File python-gotchas/images/icons/note.png

Added
New image

File python-gotchas/images/icons/prev.png

Added
New image

File python-gotchas/images/icons/tip.png

Added
New image

File python-gotchas/images/icons/up.png

Added
New image

File python-gotchas/images/icons/warning.png

Added
New image

File python-gotchas/pypy_logo.png

Added
New image

File python-gotchas/python-gotchas.txt

+Python Gotchas
+==============
+:author: Miki Tebeka <miki.tebeka@gmail.com>
+:backend: slidy
+:max-width: 45em
+:data-uri:
+:icons:
+
+
+[big]#``Computers have enabled people to make more mistakes faster than almost any
+invention in history, with the possible exception of tequila and hand guns.''#
+
+    - Mitch Ratcliffe
+
+[big]#``Experience is that marvelous thing that enables you to recognize a
+mistake when you make it again.''
+    
+    - Franklin P. Jones
+
+Compare
+-------
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/compare.py[]
+---------------------------------------------------
+
+nums.py
+-------
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/nums.py[]
+---------------------------------------------------
+
+Fix
+---
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/compare_fixed.py[]
+---------------------------------------------------
+
+Scope
+-----
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/scope.py[]
+---------------------------------------------------
+
+Fix
+---
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/scope_fixed.py[]
+---------------------------------------------------
+
+<1> Default argument are evaluated at function definition time.
+<2> `i` is passed as argument.
+
+Default Arguments
+-----------------
+[source,python,numbered]
+---------------------------------------------------
+include::src/defarg.py[]
+---------------------------------------------------
+
+Fix
+---
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/defarg_fixed.py[]
+---------------------------------------------------
+
+WARNING: Never use mutable default arguments.
+
+
+Constructor
+-----------
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/ctor.py[]
+---------------------------------------------------
+
+Fix
+---
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/ctor_fixed.py[]
+---------------------------------------------------
+
+<1> Need to call parent class constructor explicitly.
+
+Sequence
+--------
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/seq.py[]
+---------------------------------------------------
+
+Fix
+---
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/seq_fixed.py[]
+---------------------------------------------------
+
+<1> Use a list of you need to keep items around.
+<2> Use link:http://docs.python.org/library/itertools.html#itertools.tee[tee]
+    to multiplex iterators.
+
+Print
+-----
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/print.py[]
+---------------------------------------------------
+
+Fix
+---
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/print_fixed.py[]
+---------------------------------------------------
+
+<1> In Python 3 `print` is a function.
+<2> Prefer functions over lambdas.
+
+Closure
+-------
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/closure.py[]
+---------------------------------------------------
+
+Fix
+---
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/closure_fixed.py[]
+---------------------------------------------------
+
+<1> You can *mutate* "non local" variables.
+
+Fix (Python 3)
+--------------
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/closure_fixed3.py[]
+---------------------------------------------------
+
+<1> Python 3 introduced
+    link:http://docs.python.org/release/3.0.1/reference/simple_stmts.html#nonlocal[nonlocal]
+
+Float
+-----
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/float.py[]
+---------------------------------------------------
+
+Fix
+---
+
+----
+"Floating point is sort of like quantum physics: the closer you look,
+the messier it gets." 
+    - Grant Edwards 
+----
+
+Read link:http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html[
+What Every Computer Scientist Should Know About Floating-Point Arithmetic]
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/float_fixed.py[]
+---------------------------------------------------
+
+<1> Use link:http://docs.python.org/library/decimal.html[decimal] whenever you
+    care about precision.
+
+Except
+------
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/except.py[]
+---------------------------------------------------
+
+Fix
+---
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/except_fixed.py[]
+---------------------------------------------------
+
+<1> Use the new `as` syntax.
+
+Reload
+------
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/reload.py[]
+---------------------------------------------------
+
+Fix
+---
+Don't do that :) +
+
+Restart your program.
+
+Decorators
+----------
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/dec.py[]
+---------------------------------------------------
+
+Fix
+---
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/dec_fixed.py[]
+---------------------------------------------------
+
+<1> link:http://docs.python.org/library/functools.html#functools.wraps[functools.wraps] 
+    copies all the interesting bits for us.
+
+For
+---
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/for.py[]
+---------------------------------------------------
+
+items.py
+--------
+[source,python,numbered]
+---------------------------------------------------
+include::src/items.py[]
+---------------------------------------------------
+
+
+Fix
+---
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/for_fixed.py[]
+---------------------------------------------------
+
+<1> Always initialize
+<2> `sum([]) => 0`
+
+Import
+------
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/imp.py[]
+---------------------------------------------------
+
+<1> But ... link:http://docs.python.org/library/email.parser.html#email.message_from_file[
+it's there]
+
+
+Fix
+---
+Never call your modules in the name of standard library modules.
+
+
+However
+-------
+Dude, it doesn't work!
+
+----
+$ mv src/email.py src/better_email.py
+$ python src/imp.py 
+Traceback (most recent call last):
+  File "src/imp.py", line 2, in <module>
+    message = email.message_from_file('/tmp/1.eml')
+AttributeError: 'module' object has no attribute 'message_from_file'
+----
+
+
+Fix 2
+-----
+
+----
+$ rm src/email.py[co]
+----
+
+It's a good idea to have the following at the top of your test script:
+
+----
+find . -name '*.py[co]' -exec rm {} \;
+----
+
+
+Find
+----
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/find.py[]
+---------------------------------------------------
+
+
+Fix
+---
+link:http://docs.python.org/library/stdtypes.html#str.find[RTFM].
+----
+Return the lowest index in the string where substring sub is found, ...
+Return -1 if sub is not found.
+
+Note
+The find() method should be used only if you need to know the position 
+of sub.
+To check if sub is a substring or not, use the in operator ...
+----
+
+Fix (2)
+-------
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/find_fixed.py[]
+---------------------------------------------------
+
+Equal
+-----
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/eq.py[]
+---------------------------------------------------
+
+ae.py
+-----
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/ae.py[]
+---------------------------------------------------
+
+<1> `AE` stands for "Always Equal"
+
+Fix
+---
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/eq_fixed.py[]
+---------------------------------------------------
+
+<1> Note that `1 is not 1.0`
+
+StringIO
+--------
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/strio.py[]
+---------------------------------------------------
+
+<1> But ... it's
+link:http://docs.python.org/library/stringio.html#module-StringIO[there]
+
+Fix
+---
+
+[source,python,numbered]
+---------------------------------------------------
+include::src/strio_fixed.py[]
+---------------------------------------------------
+
+<1> link:http://docs.python.org/library/io.html[io] is a backport from 3.x
+
+
+Length
+------
+FIXME: missing , at end of list of strings
+
+
+References
+----------
+* link:http://wiki.python.org/moin/PythonWarts[Python Warts]
+* link:https://groups.google.com/d/topic/comp.lang.python/5HiOEB9oZnk/discussion[
+    comp.lang.python thread]
+* link:https://www.destroyallsoftware.com/talks/wat[WAT]
+* link:http://www.infoq.com/presentations/Java-Puzzlers[
+    Java Puzzlers: Scraping the Bottom of the Barrel]
+
+This presentation was made with
+link:http://www.methods.co.nz/asciidoc/[asciidoc] using the
+link:http://www.w3.org/Talks/Tools/Slidy2/Overview.html[slidy] backend and
+link:http://pygments.org/[Pygments] syntax highlighter.
+
+Thank You
+---------
+image:pypy_logo.png[]
+
+// vim: ft=asciidoc spell

File python-gotchas/src/ae.py

+class AE(object):
+    def __init__(self, i):
+        self.i = i
+
+    def __eq__(self, other):  # <1>
+        return True

File python-gotchas/src/closure.py

+def make_counter():
+    x = 0
+    def counter():
+        x += 1
+        return x
+    return counter
+
+c = make_counter()
+print(c())
+# Traceback (most recent call last):
+#   File "src/closure.py", line 10, in <module>
+#     print(c())
+#   File "src/closure.py", line 4, in counter
+#     x += 1
+# UnboundLocalError: local variable 'x' referenced before assignment
+

File python-gotchas/src/closure_fixed.py

+def make_counter():
+    x = [0]  # <1>
+    def counter():
+        x[0] += 1
+        return x[0]
+    return counter
+
+c = make_counter()
+print(c())
+# => 1
+print(c())
+# => 2

File python-gotchas/src/closure_fixed3.py

+#!/usr/bin/env python3
+
+def make_counter():
+    x = 0
+    def counter():
+        nonlocal x  # <1>
+        x += 1
+        return x
+    return counter
+
+c = make_counter()
+print(c())
+# => 1
+print(c())
+# => 2

File python-gotchas/src/clp.eml

+Received: by 10.68.129.169 with SMTP id nx9mr726769pbb.2.1333578860561;
+        Wed, 04 Apr 2012 15:34:20 -0700 (PDT)
+Path: r9ni19129pbh.0!nntp.google.com!news2.google.com!postnews.google.com!glegroupsg2000goo.googlegroups.com!not-for-mail
+From: Miki Tebeka <miki.teb...@gmail.com>
+Newsgroups: comp.lang.python
+Subject: Python Gotcha's?
+Date: Wed, 4 Apr 2012 15:34:20 -0700 (PDT)
+Organization: http://groups.google.com
+Lines: 10
+Message-ID: <7367295.815.1333578860181.JavaMail.geo-discussion-forums@ynpp8>
+NNTP-Posting-Host: 207.171.18.139
+Mime-Version: 1.0
+X-Trace: posting.google.com 1333578860 6170 127.0.0.1 (4 Apr 2012 22:34:20 GMT)
+X-Complaints-To: groups-abuse@google.com
+NNTP-Posting-Date: Wed, 4 Apr 2012 22:34:20 +0000 (UTC)
+Complaints-To: groups-abuse@google.com
+Injection-Info: glegroupsg2000goo.googlegroups.com; posting-host=207.171.18.139;
+ posting-account=uo-8fwoAAACKsFzFX78JHudx1V7WDXZ0
+User-Agent: G2/1.0
+Content-Type: text/plain; charset=ISO-8859-1
+
+Greetings,
+
+I'm going to give a "Python Gotcha's" talk at work.
+If you have an interesting/common "Gotcha" (warts/dark corners ...) please share.
+
+(Note that I want over http://wiki.python.org/moin/PythonWarts already).
+
+Thanks,
+--
+Miki

File python-gotchas/src/compare.py

+import nums
+
+print(nums.a)
+# => 1
+print(nums.b)
+# => 2
+
+print(nums.b < nums.a)
+# => True

File python-gotchas/src/compare_fixed.py

+#!/usr/bin/env python3
+
+import nums
+
+print(nums.a)
+# => 1
+print(nums.b)
+# => 2
+
+print(nums.b < nums.a)
+# Traceback (most recent call last):
+#   File "src/compare_fixed.py", line 8, in <module>
+#     print(nums.b < nums.a)  # => True
+# TypeError: unorderable types: int() < str()

File python-gotchas/src/ctor.py

+class Id(object):
+    _next_id = 0
+    def __init__(self):
+        self.id, Id._next_id = Id._next_id, Id._next_id + 1
+
+class PointId(Id):
+    def __init__(self, x, y):
+        self.x, self.y = x, y
+    def __repr__(self):
+        return 'Point#{}: {},{}'.format(self.id, self.x, self.y)
+
+pi = PointId(1, 2)
+print(pi)
+# Traceback (most recent call last):
+#   File "src/ctor.py", line 19, in <module>
+#     print(pi)
+#   File "src/ctor.py", line 15, in __repr__
+#     return 'Point#{}: {},{}'.format(self.id, self.x, self.y)
+# AttributeError: 'PointId' object has no attribute 'id'
+

File python-gotchas/src/ctor_fixed.py

+class Id(object):
+    _next_id = 0
+    def __init__(self):
+        self.id, Id._next_id = Id._next_id, Id._next_id + 1
+
+class PointId(Id):
+    def __init__(self, x, y):
+        super(PointId, self).__init__()  # <1>
+        self.x, self.y = x, y
+    def __repr__(self):
+        return 'Point#{}: {},{}'.format(self.id, self.x, self.y)
+
+
+pi = PointId(1, 2)
+print(pi)
+# => Point#0: 1,2

File python-gotchas/src/dec.py

+def logger(fn):
+    def wrapper(*args, **kw):
+        print('{} called'.format(fn.__name__))
+        return fn(*args, **kw)
+
+    return wrapper
+
+@logger
+def add(x, y):
+    '''Adds x to y'''
+    return x + y
+
+add(1, 2)
+# => add called
+print(add.__doc__)
+# => None

File python-gotchas/src/dec_fixed.py

+from functools import wraps
+
+def logger(fn):
+    @wraps(fn)  # <1>
+    def wrapper(*args, **kw):
+        print('{} called'.format(fn.__name__))
+        return fn(*args, **kw)
+
+    return wrapper
+
+@logger
+def add(x, y):
+    '''Adds x to y'''
+    return x + y
+
+add(1, 2)
+# => add called
+print(add.__doc__)
+# => Addes x to y

File python-gotchas/src/defarg.py

+def append_print(i, items=[]):
+    items.append(i)
+    print(items)
+
+append_print(1)
+# => [1]
+append_print(2)
+# => [1, 2]

File python-gotchas/src/defarg_fixed.py

+def append_print(i, items=None):
+    items = items or []
+    items.append(i)
+    print(items)
+
+append_print(1)
+# => [1]
+append_print(2)
+# => [2]

File python-gotchas/src/email.py

Empty file added.

File python-gotchas/src/eq.py

+from ae import AE
+
+ae = AE(1)
+if ae == None:
+    print('srsly?')
+# => srsly?
+

File python-gotchas/src/eq_fixed.py

+from ae import AE
+
+ae = AE(1)
+if ae is not None:  # <1>
+    print('is fine')
+# => is fine

File python-gotchas/src/except.py

+try:
+    raise ValueError
+except IndexError, ValueError:
+    print('oops')
+# Traceback (most recent call last):
+#   File "src/catch.py", line 2, in <module>
+#     raise ValueError
+# ValueError
+

File python-gotchas/src/except_fixed.py

+try:
+    raise ValueError
+except (IndexError, ValueError) as e:  # <1>
+    print('oops')
+# => oops

File python-gotchas/src/find.py

+haystack = 'hello there'
+needle = 'bugs'
+if haystack.find(needle):
+    print('WAT?')
+# => WAT
+needle = 'hello'
+if not haystack.find(needle):
+    print('is broken')
+# => is broken

File python-gotchas/src/find_fixed.py

+haystack = 'hello there'
+needle = 'bugs'
+if 'bugs' in haystack:
+    print('WAT?')
+# => 
+needle = 'hello'
+if needle not in haystack:
+    print('is broken')
+# => 

File python-gotchas/src/float.py

+1.1 * 1.1
+# => 1.2100000000000002 

File python-gotchas/src/float_fixed.py

+from decimal import Decimal  # <1>
+
+Decimal('1.1') * Decimal('1.1')
+# => Decimal('1.21')

File python-gotchas/src/for.py

+from items import items
+
+for count, _ in enumerate(items, 1):
+    pass
+
+print('There are {} items'.format(count))
+# Traceback (most recent call last):
+#   File "src/for.py", line 6, in <module>
+#     print('There are {} items'.format(count))
+# NameError: name 'count' is not defined
+
+

File python-gotchas/src/for_fixed.py

+from items import items
+
+count = 0  # <1>
+for count, _ in enumerate(items, 1):
+    pass
+print('There are {} items'.format(count))
+
+# OR
+
+count = sum(1 for i in items)  # <2>
+print('There are {} items'.format(count))

File python-gotchas/src/imp.py

+import email
+with open('src/clp.eml') as input:
+    message = email.message_from_file(input)
+# Traceback (most recent call last):
+#   File "src/imp.py", line 2, in <module>
+#     message = email.message_from_file('/tmp/1.eml')
+# AttributeError: 'module' object has no attribute 'message_from_file' <1>
+

File python-gotchas/src/items.py

+items = []

File python-gotchas/src/nums.py

+a = '1'
+b = 2

File python-gotchas/src/print.py

+if verbose:
+    log = lambda message: print(message)
+else:
+    log = lambda message: pass
+#   File "src/print.py", line 2
+#     log = lambda message: print(message)
+#                               ^
+# SyntaxError: invalid syntax
+

File python-gotchas/src/print_fixed.py

+from __future__ import print_function # or use python3  <1>
+verbose = 1
+if verbose:
+    log = lambda message: print(message)
+else:
+    log = lambda message: 1
+
+# OR
+if verbose:
+    def log(message): print(message) # <2>
+else:
+    def log(message): pass

File python-gotchas/src/reload.py

+import module
+print(module.submodule.value)
+# => 1
+# Now change submodule value to 7
+reload(module)
+print(module.submodule.value)
+# => 1

File python-gotchas/src/scope.py

+callbacks = [lambda: i for i in range(5)]
+print([c() for c in callbacks])
+# => [4, 4, 4, 4, 4]

File python-gotchas/src/scope_fixed.py

+callbacks = [lambda i=i: i for i in range(5)]  # <1>
+print([c() for c in callbacks])
+# => [0, 1, 2, 3, 4]
+
+
+def make_callback(i):  # <2>
+    return lambda: i
+
+
+callbacks = [make_callback(i) for i in range(10)]
+print([c() for c in callbacks])
+# => [0, 1, 2, 3, 4]

File python-gotchas/src/seq.py

+items = (str(i) for i in xrange(5))
+print(', '.join(items))
+# => 0, 1, 2, 3, 4
+print(', '.join(items))
+# => 

File python-gotchas/src/seq_fixed.py

+items = [str(i) for i in xrange(5)]  # <1>
+print(', '.join(items))
+# => 0, 1, 2, 3, 4
+print(', '.join(items))
+# => 0, 1, 2, 3, 4
+
+from itertools import tee
+items = (str(i) for i in xrange(5))
+i1, i2 = tee(items)  # <2>
+print(', '.join(i1))
+# => 0, 1, 2, 3, 4
+print(', '.join(i2))
+# => 0, 1, 2, 3, 4

File python-gotchas/src/strio.py

+from io import StringIO  # <1>
+
+stream = StringIO('Thank you Sir, may I have another?')
+# Traceback (most recent call last):
+#   File "src/strio.py", line 3, in <module>
+#     stream = StringIO('Thank you Sir, may I have another?')
+# TypeError: initial_value must be unicode or None, not str

File python-gotchas/src/strio_fixed.py

+from cStringIO import StringIO  # <1>
+
+stream = StringIO('Thank you Sir, may I have another?')

File python-gotchas/style.css

+/* Vertical lines between line numbers and code */
+div.linenodiv {
+    border-right:1px solid gray;
+    padding-right:2px;
+    margin-right:2px;
+}