"Don't rebind built-in names*" - it confuses readers

Steven D'Aprano steve+comp.lang.python at pearwood.info
Tue Jun 11 01:52:00 EDT 2013


On Mon, 10 Jun 2013 20:30:41 -0700, rusi wrote:

>> Certainly it would be a PITA, and defeat the purpose of having nested
>> scopes, if inner names had to be globally unique. Wouldn't it be
>> absolutely horrible if adding a global variable "foo"[1] suddenly meant
>> that all your functions that used "foo" as a local variable stopped
>> working?
>>
>> [1] For some value of "foo".
> 
> Your opinion.

Well duh :-)

Mind you, I don't hear very many people *defending* the idea that local 
variables should be globally unique, or designing languages where this is 
the case. So if it's just an opinion, it's an opinion shared by the 
majority of programmers and language designers.


> Not so convincing if the sequence of composing the program was the
> other-way-round:
> if I have a global variable, say errno, and 'lose' it by introducing a
> local variable errno.

The consequences of inadvertent local-shadows-global are *much* less than 
the other way around. Any harm is local to the one function.

If you've shadowed a global with a local, there are two possibilities:

- You intended to shadow the global, in which case, good for you. I'm not 
going to past judgement and say you mustn't do this, so long as you're 
aware of what you're doing and have your reasons.

- You didn't intend to shadow the global, in which case you've just made 
a bug, and you'll soon find out and fix it.


> And in fact for a reader of a program, the order of its writing should
> not matter.

It doesn't. However, edits to working code can make it become non-
working. That's part of the business of being a programmer. Consider two 
scenarios:

- Add a local variable, and suddenly the function which you were editing 
stops working? Painful, but business as usual. At least you know that the 
bug exists within the function you just edited.

- Add a global variable, and suddenly dozens of functions all over the 
place stop working? Or worse, only a small handful of functions stop 
working, and you don't find out for a while. It's a lot harder to fix a 
bug caused by a new global shadowing your local when you might not even 
know that global exists.


> Which brings us pat into Terry's example.  [Also notice that changing
> from a 'parametric-semantic' name like foo to a more 'fixed-semantic'
> name like 'errno' or 'list' changes the desirability of this feature.

Absolutely! It makes the ability to shadow globals *more desirable*.

def myfunc(arg, list=list):
    do_this()
    do_that()
    return list(arg)


Now you have a nicely localised, safe, tame monkey-patch, without 
compromising on the best name for "list".


>> I take it you have never programmed in a programming language with a
>> single, flat, global namespace? :-)
> 
> Well Ive used Basic and Assembler -- which are fun in the way that
> childhood and mountaineering respectively are fun.
> 
> What it seems you are not getting about Erlang's outlook about block-
> structure is this: There are two separable aspects to it: 1. Names can
> be created in local scopes which dont leak into (more) global scopes --
> a desirable feature

I can see that it is desirable, although I don't know how this works in 
practice in Erland. If you have a global x, and a local x, how do you 
refer to them both?

x = x 

Which one is which?


> 2. Names in local scopes can override names in global scope -- a
> dangerous feature [BTW which is what this thread is about].  And
> Erlang's approach seems to be the most nuanced -- you can do it if you
> go out of your way to say: "unstick the global namespace".

I can see that this is also desirable, especially in a more "bondage and 
discipline" language that makes you ask permission before doing anything. 
I don't think it is desirable *in Python*, which is a lot more laisse 
faire.


> This is somewhat analogous to gotos in Pascal. For Pascal goto was a
> sufficiently undesirable feature that using it was not completely easy. 
> However if you did surely want it, you had to declare the goto label.
> 
> Or by example:
> 
> def foo(x)...
> def bar(x,y)...
> there is no reason to confuse the two xes.
> 
> Whereas
> 
> x = ...
> def foo(x)...
> Now there is!
> 
> The first should be encouraged, the second discouraged.

Discouraging it means telling people that every time they need a local 
variable, they should consider the entire global environment before 
choosing a name. I call that bogus. Why shouldn't I call a local variable 
"id" if that's the best name for it, just because there's a global "id" 
that hardly anyone ever uses? If there's a global "x", and my function 
doesn't use it, why shouldn't it reuse "x" for a local if "x" is the best 
name in context?

Shadowing has both uses and abuses, pros and cons, and there's no doubt 
that it can be confusing to beginners. There are arguments against it, 
and I agree with them. But there are also arguments in favour, and I 
agree with them too. A good programmer[1] will weigh up the pros and cons 
of "use the most readable, descriptive name for the variable" versus 
"shadow a global or built-in with the same name" and decide on the merits 
of the specific case in question -- should I use a less-appropriate name 
("mylist", blah) in the interest of not confusing some readers, or the 
right name but risk shadowing a name in a higher scope?

Python takes a very hands-off approach to this. Other languages are more 
in-your-face. There is room in the world for both philosophies.



[1] In my opinion of good *wink*


-- 
Steven



More information about the Python-list mailing list