[Python-ideas] global and nonlocal with atributes

Steven D'Aprano steve at pearwood.info
Sun May 5 13:10:29 CEST 2013


On 05/05/13 18:46, Chris Angelico wrote:
> On Sun, May 5, 2013 at 4:41 PM, Steven D'Aprano <steve at pearwood.info> wrote:
>>> That way you can have both "global.x", "nonlocal.x" and "x" variables
>>> without conflict.
>>>
>>> x = 10
>>> def f(x):
>>>      def g():
>>>          # how can I set both "x" variables inside here?
>>
>>
>> You can't, and you shouldn't need to. How hard is it for you to use a
>> different name for the global and the local? There are billions of possible
>> names to choose from, why use the same name for both?
>
> The conflict already exists in another form, and with the same solution:
>
>>>> def foo():
> 	list=[1,2,3]
> 	# ...
> 	return __builtins__.list()
>
>>>> foo()
> []
>
> We're allowed to shadow builtins, and what's more, there's a way to
> explicitly call up the builtin even after it's been shadowed. If
> nothing else, it allows a measure of simplicity with common names like
> 'id' - instead of having to pick a different name, you just go right
> ahead and use the obvious one, knowing that the builtin _is_
> retrievable. With globals, yes it's possible to reference them via
> globals()["some_name"], but that looks ugly.


Shadowing is a real issue, but any solution must be reasonable. Adding special magic syntax for some, but not all, keywords to act almost, but not quite, like an object is a million lightyears beyond reasonable when there are already two perfectly good solutions to the shadowing problem:

- pick a different name;

- use globals()['some_name']


Only the first applies to non-locals, of course, but even so, "pick a different name" remains the best solution to this problem.

Of course you are right, we are allowed to shadow builtins, or anything else. But the usual "consenting adults" disclaimer applies:

- shadow things only when you need to (although the barrier for "need" can be quite low);

- provided doing so doesn't cause you pain;

- if it does cause you pain, then the old doctor's advice still stands:

"Doc, it hurts when I do this."

"Then don't do that!"

- And if you absolutely insist on shooting yourself in the foot, then Python provides some tools for recovering from your self-inflicted wounds (in this case, doing a lookup on globals()) without promising to go to extraordinary efforts to disguise the fact that your code is bad and you should feel bad. If you are deliberately shadowing names *that you need*, then you are doing something dumb, and it is not Python's responsibility to make dumb things less painful.

Magic syntax as suggested counts as extraordinary effort. It spoils the nice clean design of the language. "Everything is an object" becomes "everything is an object, except for global and nonlocal". How far do we want the global keyword to simulate being an object? For example, what happens if I do this?

     print(global)


Can I do this?

     f = global.__getitem__
     f("x")  # like global.x


Is global.__dict__ another way to spell globals()? Can I pass global as an argument to functions? If I can do this:

     eval(expr, global)


then why can't I do this?

     x = global


but in that case, what is type(x), given that global is not actually an object?

Note that for backwards compatibility, we cannot just make global a reserved name, like None, that refers to the global namespace. Doing this might be good Python 4000 territory. But for Python 3.x, this proposal adds complexity and complication to the language to solve no problem that actually needs to be solved.



> Would the OP's proposal
> look better if, instead of "globals.x", it were "__globals__.x"?  Or
> possibly "__module__.x", that being a magic word that references your
> current module, whatever-it-may-be?

Whether you spell it globals() or __globals__ or __module__, the OP's suggestion isn't to add another name for the current global namespace. That would merely be redundant. We already have at least three ways to do the same thing:

     global x; x

     globals()['x']

     eval('x', globals())

If somebody wants a forth way, using attribute access instead of key lookup, I'm sure they could write a proxy to globals() that works fine. Put it on ActiveState, and if it gets lots of interest, then maybe there would be a case for adding it as standard.

But the OP's proposal is specifically to allow the global and nonlocal keywords to appear in places where currently expressions can appear, as well as still appearing as statements, which implies that they behave almost but not quite like objects without actually being objects. That's the sort of nonsense that you get in PHP, where there are things that look like functions or operators, like list() and (int), but are actually magic handled by the parser.

http://me.veekun.com/blog/2012/04/09/php-a-fractal-of-bad-design/


-- 
Steven



More information about the Python-ideas mailing list