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. -- https://mail.python.org/mailman/listinfo/python-list