Python Worst Practices
Steven D'Aprano
steve+comp.lang.python at pearwood.info
Fri Feb 27 20:09:31 EST 2015
Travis Griggs wrote:
> If I were giving a talk at SPLASH (or some other suitable polyglot
> conference), I might do one called “Language Design Worst Practices”.
>
> One of my first slides might be titled:
>
> Abuse Common Tokens in Confusing Ways
>
> * Make your language have a lot of keywords. Enough to make memorizing
> them ALL unlikely, requiring constant visits to your documentation
Is 33 a lot?
py> import keyword
py> keyword.kwlist
['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class',
'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for',
'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal',
'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']
> * Make sure said keywords are many of the obvious words programmers would
> use in their applications (map, object, bytes, dir, etc)
Luckily, Python doesn't make that mistake of making built-ins keywords. That
would require making actual changes to the parser each time a new built-in
function was added, as well as breaking people's existing code.
Fortunately, Python has a much better system: a small set of keywords, very
few of which would make useful variable names ("else = 23"), and a much
larger set of regular names in a built-in namespace.
py> import builtins # use __builtin__ in Python 2
py> sorted(vars(builtins).keys())
['ArithmeticError', 'AssertionError', ... 'type', 'vars', 'zip']
There's 147 of the built-ins in Python 3.3, although a few of those aren't
truly built-in, merely added at interpreter startup.
The ability to shadow built-ins is not a bug, it is a feature. It's an
amazingly powerful feature, and not particularly newbie-friendly, but
*many* things are not easy for newbies to master or avoid abusing.
- Code can override, or overload, built-ins, either at the level of
an entire module, or inside a particular function.
- Modules can offer functions which clash with a built-in name.
E.g. reprlib.repr, math.pow.
- More importantly, modules can offer stable APIs with no fear that
the introduction of a new built-in function will require them to
change their function's name.
- Which is a special case of a more general benefit, the introduction
of a new built-in name does *not* break existing code that already
uses that name.
Newbies misuse this feature because they still have a wishful-thinking
approach to programming. One example of wishful-thinking is the common
newbie mistake of wondering why their loop variable never changes:
# Toss a coin until you get Tails.
x = random.random()
while x < 0.5:
print "Heads"
print "Tails"
Isn't it obvious that I want x to get a new random number every time through
the loop? I wish the computer understood me so I didn't need to write all
the steps out.
Likewise:
int = 23
n = int("42")
Isn't it obvious that the second use of int has to be the built-in function?
I wish that the computer would understand from context which one I mean.
Other newbie stylistic mistakes which can increase the chance of shadowing
errors include:
* Too many overly generic variable names like "int" and "str".
* Insufficient use of functions and too much top-level code. When they
shadow a built-in, they shadow it everywhere.
* Excessively large functions that do too much. By the time they reach
the end of their 300 line function, they have forgotten that they
have already used "list" for a variable name.
However, even experienced developers can make this mistake too. Generally
speaking, it's trivially easy to recover from. Although if you're doing it
*regularly* that might be a hint of deeper problems, e.g. poor variable
naming skills, too much top-level code.
There's no harm in calling a local variable "id", if you don't use the
built-in id() inside that function. That's one of the reasons why functions
exist, so that the names you use inside a function are distinct from those
outside.
> * Design your syntax so that you can’t disambiguate them contextually
> between bind and reference
Do you have an example of where Python cannot distinguish between a binding
operation and a reference?
> * Be sure to use it in a late bound language where no warnings will be
> provided about the mistake you’re making at authorship time, deferring the
> educational experience to sundry run times
Python raises a SyntaxError at compile time, not run time, if you try to
bind to a keyword:
py> global = 23
File "<stdin>", line 1
global = 23
^
SyntaxError: invalid syntax
--
Steven
More information about the Python-list
mailing list