Python Worst Practices

sohcahtoa82 at gmail.com sohcahtoa82 at gmail.com
Fri Feb 27 20:32:28 EST 2015


On Friday, February 27, 2015 at 5:09:49 PM UTC-8, Steven D'Aprano wrote:
> 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

Very well-said!

Just because a feature (In this case, shadowing built-in functions) can be abused or cause problems doesn't mean it's a bad feature.

It reminds me of the people that rip on C++'s operator overloading because some people write bad code and implement non-intuitive operators for classes.

For example, I've seen someone create a Socket class, then created an operator overload that allowed you to "add" a string to your socket to make the socket send the string, with the result being a status code indicating success or an error.



More information about the Python-list mailing list