mirror of
https://github.com/python/cpython.git
synced 2025-08-02 16:13:13 +00:00

svn+ssh://svn.python.org/python/branches/py3k ................ r73941 | georg.brandl | 2009-07-11 12:39:00 +0200 (Sa, 11 Jul 2009) | 9 lines Merged revisions 73940 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73940 | georg.brandl | 2009-07-11 12:37:38 +0200 (Sa, 11 Jul 2009) | 1 line #6430: add note about size of "u" type. ........ ................ r73942 | georg.brandl | 2009-07-11 12:39:23 +0200 (Sa, 11 Jul 2009) | 1 line #6430: remove mention of "w" array typecode. ................ r73943 | georg.brandl | 2009-07-11 12:43:08 +0200 (Sa, 11 Jul 2009) | 1 line #6421: The self argument of module-level PyCFunctions is now a reference to the module object. ................ r74076 | georg.brandl | 2009-07-18 11:07:48 +0200 (Sa, 18 Jul 2009) | 1 line #6502: add missing comma in docstring. ................ r74094 | georg.brandl | 2009-07-19 09:25:56 +0200 (So, 19 Jul 2009) | 10 lines Recorded merge of revisions 74089 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74089 | senthil.kumaran | 2009-07-19 04:43:43 +0200 (So, 19 Jul 2009) | 3 lines Fix for issue5102, timeout value propages between redirects, proxy, digest and auth handlers. Fixed tests to reflect the same. ........ ................ r74186 | georg.brandl | 2009-07-23 11:19:09 +0200 (Do, 23 Jul 2009) | 9 lines Recorded merge of revisions 74185 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74185 | georg.brandl | 2009-07-23 11:17:09 +0200 (Do, 23 Jul 2009) | 1 line Fix the "pylocals" gdb command. ........ ................ r74211 | georg.brandl | 2009-07-26 16:48:09 +0200 (So, 26 Jul 2009) | 9 lines Recorded merge of revisions 74210 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74210 | georg.brandl | 2009-07-26 16:44:23 +0200 (So, 26 Jul 2009) | 1 line Move member descriptions inside the classes. ........ ................ r74212 | georg.brandl | 2009-07-26 16:54:51 +0200 (So, 26 Jul 2009) | 9 lines Merged revisions 74209 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74209 | georg.brandl | 2009-07-26 16:37:28 +0200 (So, 26 Jul 2009) | 1 line builtin -> built-in. ........ ................ r74213 | georg.brandl | 2009-07-26 17:02:41 +0200 (So, 26 Jul 2009) | 9 lines Merged revisions 74207 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74207 | georg.brandl | 2009-07-26 16:19:57 +0200 (So, 26 Jul 2009) | 1 line #6577: fix (hopefully) all links to builtin instead of module/class-specific objects. ........ ................ r74214 | georg.brandl | 2009-07-26 17:03:49 +0200 (So, 26 Jul 2009) | 9 lines Merged revisions 74205 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74205 | georg.brandl | 2009-07-26 15:36:39 +0200 (So, 26 Jul 2009) | 1 line #6576: fix cross-refs in re docs. ........ ................ r74247 | georg.brandl | 2009-07-29 09:27:08 +0200 (Mi, 29 Jul 2009) | 9 lines Merged revisions 74239 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74239 | georg.brandl | 2009-07-28 18:55:32 +0000 (Di, 28 Jul 2009) | 1 line Clarify quote_plus() usage. ........ ................ r74254 | georg.brandl | 2009-07-29 18:14:16 +0200 (Mi, 29 Jul 2009) | 1 line #6586: fix return/argument type doc for os.read() and os.write(). ................ r74262 | alexandre.vassalotti | 2009-07-29 21:54:39 +0200 (Mi, 29 Jul 2009) | 57 lines Merged revisions 74074,74077,74111,74188,74192-74193,74200,74252-74253,74258-74261 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74074 | georg.brandl | 2009-07-18 05:03:10 -0400 (Sat, 18 Jul 2009) | 1 line #6513: fix example code: warning categories are classes, not instances. ........ r74077 | georg.brandl | 2009-07-18 05:43:40 -0400 (Sat, 18 Jul 2009) | 1 line #6489: fix an ambiguity in getiterator() documentation. ........ r74111 | benjamin.peterson | 2009-07-20 09:30:10 -0400 (Mon, 20 Jul 2009) | 1 line remove docs for deprecated -p option ........ r74188 | benjamin.peterson | 2009-07-23 10:25:31 -0400 (Thu, 23 Jul 2009) | 1 line use bools ........ r74192 | georg.brandl | 2009-07-24 12:28:38 -0400 (Fri, 24 Jul 2009) | 1 line Fix arg types of et#. ........ r74193 | georg.brandl | 2009-07-24 12:46:38 -0400 (Fri, 24 Jul 2009) | 1 line Dont put "void" in signature for nullary functions. ........ r74200 | georg.brandl | 2009-07-25 09:02:15 -0400 (Sat, 25 Jul 2009) | 1 line #6571: add index entries for more operators. ........ r74252 | georg.brandl | 2009-07-29 12:06:31 -0400 (Wed, 29 Jul 2009) | 1 line #6593: fix link targets. ........ r74253 | georg.brandl | 2009-07-29 12:09:17 -0400 (Wed, 29 Jul 2009) | 1 line #6591: add reference to ioctl in fcntl module for platforms other than Windows. ........ r74258 | georg.brandl | 2009-07-29 12:57:05 -0400 (Wed, 29 Jul 2009) | 1 line Add a link to readline, and mention IPython and bpython. ........ r74259 | georg.brandl | 2009-07-29 13:07:21 -0400 (Wed, 29 Jul 2009) | 1 line Fix some markup and small factual glitches found by M. Markert. ........ r74260 | georg.brandl | 2009-07-29 13:15:20 -0400 (Wed, 29 Jul 2009) | 1 line Fix a few markup glitches. ........ r74261 | georg.brandl | 2009-07-29 13:50:25 -0400 (Wed, 29 Jul 2009) | 1 line Rewrite the section about classes a bit; mostly tidbits, and a larger update to the section about "private" variables to reflect the Pythonic consensus better. ........ ................ r74311 | georg.brandl | 2009-08-04 22:29:27 +0200 (Di, 04 Aug 2009) | 1 line Slightly improve buffer-related error message. ................ r74334 | georg.brandl | 2009-08-06 19:51:03 +0200 (Do, 06 Aug 2009) | 1 line #6648: mention surrogateescape handler where all standard handlers are listed. ................ r74368 | georg.brandl | 2009-08-13 09:56:35 +0200 (Do, 13 Aug 2009) | 21 lines Merged revisions 74328,74332-74333,74365 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74328 | georg.brandl | 2009-08-06 17:06:25 +0200 (Do, 06 Aug 2009) | 1 line Fix base keyword arg name for int() and long(). ........ r74332 | georg.brandl | 2009-08-06 19:23:21 +0200 (Do, 06 Aug 2009) | 1 line Fix punctuation and one copy-paste error. ........ r74333 | georg.brandl | 2009-08-06 19:43:55 +0200 (Do, 06 Aug 2009) | 1 line #6658: fix two typos. ........ r74365 | georg.brandl | 2009-08-13 09:48:05 +0200 (Do, 13 Aug 2009) | 1 line #6679: Remove mention that sub supports no flags. ........ ................
276 lines
9.1 KiB
ReStructuredText
276 lines
9.1 KiB
ReStructuredText
************************************
|
|
Idioms and Anti-Idioms in Python
|
|
************************************
|
|
|
|
:Author: Moshe Zadka
|
|
|
|
This document is placed in the public domain.
|
|
|
|
|
|
.. topic:: Abstract
|
|
|
|
This document can be considered a companion to the tutorial. It shows how to use
|
|
Python, and even more importantly, how *not* to use Python.
|
|
|
|
|
|
Language Constructs You Should Not Use
|
|
======================================
|
|
|
|
While Python has relatively few gotchas compared to other languages, it still
|
|
has some constructs which are only useful in corner cases, or are plain
|
|
dangerous.
|
|
|
|
|
|
from module import \*
|
|
---------------------
|
|
|
|
|
|
Inside Function Definitions
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
``from module import *`` is *invalid* inside function definitions. While many
|
|
versions of Python do not check for the invalidity, it does not make it more
|
|
valid, no more than having a smart lawyer makes a man innocent. Do not use it
|
|
like that ever. Even in versions where it was accepted, it made the function
|
|
execution slower, because the compiler could not be certain which names are
|
|
local and which are global. In Python 2.1 this construct causes warnings, and
|
|
sometimes even errors.
|
|
|
|
|
|
At Module Level
|
|
^^^^^^^^^^^^^^^
|
|
|
|
While it is valid to use ``from module import *`` at module level it is usually
|
|
a bad idea. For one, this loses an important property Python otherwise has ---
|
|
you can know where each toplevel name is defined by a simple "search" function
|
|
in your favourite editor. You also open yourself to trouble in the future, if
|
|
some module grows additional functions or classes.
|
|
|
|
One of the most awful question asked on the newsgroup is why this code::
|
|
|
|
f = open("www")
|
|
f.read()
|
|
|
|
does not work. Of course, it works just fine (assuming you have a file called
|
|
"www".) But it does not work if somewhere in the module, the statement ``from os
|
|
import *`` is present. The :mod:`os` module has a function called :func:`open`
|
|
which returns an integer. While it is very useful, shadowing builtins is one of
|
|
its least useful properties.
|
|
|
|
Remember, you can never know for sure what names a module exports, so either
|
|
take what you need --- ``from module import name1, name2``, or keep them in the
|
|
module and access on a per-need basis --- ``import module; print(module.name)``.
|
|
|
|
|
|
When It Is Just Fine
|
|
^^^^^^^^^^^^^^^^^^^^
|
|
|
|
There are situations in which ``from module import *`` is just fine:
|
|
|
|
* The interactive prompt. For example, ``from math import *`` makes Python an
|
|
amazing scientific calculator.
|
|
|
|
* When extending a module in C with a module in Python.
|
|
|
|
* When the module advertises itself as ``from import *`` safe.
|
|
|
|
|
|
from module import name1, name2
|
|
-------------------------------
|
|
|
|
This is a "don't" which is much weaker than the previous "don't"s but is still
|
|
something you should not do if you don't have good reasons to do that. The
|
|
reason it is usually bad idea is because you suddenly have an object which lives
|
|
in two separate namespaces. When the binding in one namespace changes, the
|
|
binding in the other will not, so there will be a discrepancy between them. This
|
|
happens when, for example, one module is reloaded, or changes the definition of
|
|
a function at runtime.
|
|
|
|
Bad example::
|
|
|
|
# foo.py
|
|
a = 1
|
|
|
|
# bar.py
|
|
from foo import a
|
|
if something():
|
|
a = 2 # danger: foo.a != a
|
|
|
|
Good example::
|
|
|
|
# foo.py
|
|
a = 1
|
|
|
|
# bar.py
|
|
import foo
|
|
if something():
|
|
foo.a = 2
|
|
|
|
|
|
except:
|
|
-------
|
|
|
|
Python has the ``except:`` clause, which catches all exceptions. Since *every*
|
|
error in Python raises an exception, this makes many programming errors look
|
|
like runtime problems, and hinders the debugging process.
|
|
|
|
The following code shows a great example::
|
|
|
|
try:
|
|
foo = opne("file") # misspelled "open"
|
|
except:
|
|
sys.exit("could not open file!")
|
|
|
|
The second line triggers a :exc:`NameError` which is caught by the except
|
|
clause. The program will exit, and you will have no idea that this has nothing
|
|
to do with the readability of ``"file"``.
|
|
|
|
The example above is better written ::
|
|
|
|
try:
|
|
foo = opne("file") # will be changed to "open" as soon as we run it
|
|
except IOError:
|
|
sys.exit("could not open file")
|
|
|
|
There are some situations in which the ``except:`` clause is useful: for
|
|
example, in a framework when running callbacks, it is good not to let any
|
|
callback disturb the framework.
|
|
|
|
|
|
Exceptions
|
|
==========
|
|
|
|
Exceptions are a useful feature of Python. You should learn to raise them
|
|
whenever something unexpected occurs, and catch them only where you can do
|
|
something about them.
|
|
|
|
The following is a very popular anti-idiom ::
|
|
|
|
def get_status(file):
|
|
if not os.path.exists(file):
|
|
print("file not found")
|
|
sys.exit(1)
|
|
return open(file).readline()
|
|
|
|
Consider the case the file gets deleted between the time the call to
|
|
:func:`os.path.exists` is made and the time :func:`open` is called. That means
|
|
the last line will throw an :exc:`IOError`. The same would happen if *file*
|
|
exists but has no read permission. Since testing this on a normal machine on
|
|
existing and non-existing files make it seem bugless, that means in testing the
|
|
results will seem fine, and the code will get shipped. Then an unhandled
|
|
:exc:`IOError` escapes to the user, who has to watch the ugly traceback.
|
|
|
|
Here is a better way to do it. ::
|
|
|
|
def get_status(file):
|
|
try:
|
|
return open(file).readline()
|
|
except (IOError, OSError):
|
|
print("file not found")
|
|
sys.exit(1)
|
|
|
|
In this version, \*either\* the file gets opened and the line is read (so it
|
|
works even on flaky NFS or SMB connections), or the message is printed and the
|
|
application aborted.
|
|
|
|
Still, :func:`get_status` makes too many assumptions --- that it will only be
|
|
used in a short running script, and not, say, in a long running server. Sure,
|
|
the caller could do something like ::
|
|
|
|
try:
|
|
status = get_status(log)
|
|
except SystemExit:
|
|
status = None
|
|
|
|
So, try to make as few ``except`` clauses in your code --- those will usually be
|
|
a catch-all in the :func:`main`, or inside calls which should always succeed.
|
|
|
|
So, the best version is probably ::
|
|
|
|
def get_status(file):
|
|
return open(file).readline()
|
|
|
|
The caller can deal with the exception if it wants (for example, if it tries
|
|
several files in a loop), or just let the exception filter upwards to *its*
|
|
caller.
|
|
|
|
The last version is not very good either --- due to implementation details, the
|
|
file would not be closed when an exception is raised until the handler finishes,
|
|
and perhaps not at all in non-C implementations (e.g., Jython). ::
|
|
|
|
def get_status(file):
|
|
fp = open(file)
|
|
try:
|
|
return fp.readline()
|
|
finally:
|
|
fp.close()
|
|
|
|
|
|
Using the Batteries
|
|
===================
|
|
|
|
Every so often, people seem to be writing stuff in the Python library again,
|
|
usually poorly. While the occasional module has a poor interface, it is usually
|
|
much better to use the rich standard library and data types that come with
|
|
Python than inventing your own.
|
|
|
|
A useful module very few people know about is :mod:`os.path`. It always has the
|
|
correct path arithmetic for your operating system, and will usually be much
|
|
better than whatever you come up with yourself.
|
|
|
|
Compare::
|
|
|
|
# ugh!
|
|
return dir+"/"+file
|
|
# better
|
|
return os.path.join(dir, file)
|
|
|
|
More useful functions in :mod:`os.path`: :func:`basename`, :func:`dirname` and
|
|
:func:`splitext`.
|
|
|
|
There are also many useful built-in functions people seem not to be aware of for
|
|
some reason: :func:`min` and :func:`max` can find the minimum/maximum of any
|
|
sequence with comparable semantics, for example, yet many people write their own
|
|
:func:`max`/:func:`min`. Another highly useful function is
|
|
:func:`functools.reduce`. A classical use of :func:`reduce` is something like
|
|
::
|
|
|
|
import sys, operator, functools
|
|
nums = list(map(float, sys.argv[1:]))
|
|
print(functools.reduce(operator.add, nums) / len(nums))
|
|
|
|
This cute little script prints the average of all numbers given on the command
|
|
line. The :func:`reduce` adds up all the numbers, and the rest is just some
|
|
pre- and postprocessing.
|
|
|
|
On the same note, note that :func:`float` and :func:`int` accept arguments of
|
|
type string, and so are suited to parsing --- assuming you are ready to deal
|
|
with the :exc:`ValueError` they raise.
|
|
|
|
|
|
Using Backslash to Continue Statements
|
|
======================================
|
|
|
|
Since Python treats a newline as a statement terminator, and since statements
|
|
are often more than is comfortable to put in one line, many people do::
|
|
|
|
if foo.bar()['first'][0] == baz.quux(1, 2)[5:9] and \
|
|
calculate_number(10, 20) != forbulate(500, 360):
|
|
pass
|
|
|
|
You should realize that this is dangerous: a stray space after the ``\`` would
|
|
make this line wrong, and stray spaces are notoriously hard to see in editors.
|
|
In this case, at least it would be a syntax error, but if the code was::
|
|
|
|
value = foo.bar()['first'][0]*baz.quux(1, 2)[5:9] \
|
|
+ calculate_number(10, 20)*forbulate(500, 360)
|
|
|
|
then it would just be subtly wrong.
|
|
|
|
It is usually much better to use the implicit continuation inside parenthesis:
|
|
|
|
This version is bulletproof::
|
|
|
|
value = (foo.bar()['first'][0]*baz.quux(1, 2)[5:9]
|
|
+ calculate_number(10, 20)*forbulate(500, 360))
|
|
|