On Wed, May 27, 2020 at 05:03:09AM +1000, Chris Angelico wrote:

> def foo():
>     if False: x = 0
>     # what is x now?
> 
> There is no *value* in x, yet x has a state. 

In Python code, no, it has no state, it's just an unbound name. That's 
literally a name that has nothing bound to it, hence no state.

In the CPython 3 implementation, it has a hidden state: there's a fixed 
array representing the locals, one of those array slots represents x, 
and there is some kind of C-level special state to distinguish between 
"this slot is filled" and "this slot is not filled". But that's purely 
an optimization. Locals can also be backed by a dict, like globals.

That is what happens in Jython, so when you call locals() you get back 
the actual local namespace dict and modifications to the variables 
works. (Unlike in CPython.)

    # Jython 2.7
    >>> def test():
    ...     locals()['x'] = 999
    ...     if False:
    ...         # Fool the compiler into treating x as a local.
    ...         x = None
    ...     print(x)
    ...
    >>> test()
    999

IronPython appears to be different yet again, but I don't understand 
what it is doing so I can't explain it.

In CPython 2,  some locals were backed by the fast array slots, some 
were not. I think (but I'm not sure!) that if the compiler saw an exec 
or a star import inside a function, it switched off the fast array 
optimization. Or something like that.

The bottom line here is that the Python execution model has names. Names 
can be bound to a value (an object), or they can be unbound in which 
case they have no state *in Python*.

Whether that unboundness is represented by a nil pointer or a special 
magic value or is a consequence of a key being missing from a namespace 
dict is part of the implementation, not part of the Python semantics.


> So we could have some
> kind of definition of optional parameters where, rather than receiving
> a default, they would simply not be bound.

We could have an `if undef` keyword :-)

    if undef x:
        x = something

(Not entirely serious about this proposal.)


> def foo(?x):
>     # what is x?
> 
> There would want to be a new way to query this state, though, because
> I think this code is ugly enough to die:
> 
> def foo(?x):
>     try:
>         x
>     except UnboundLocalError:
>         ... # do this if x wasn't passed in
>     else:
>         ... # do this if we have a value for x

There's always:

    if 'x' in locals()


> (It's probably best to define this ONLY for local variables. Module or
> class name bindings behave differently, so they would simply never be
> in this unbound state.

Of course they are!

    # Module level state.
    x = None; del x  # Ensure x is unbound.
    print(x)


I frequently write top-level module code that tests for the existence of 
a global or builtin:

    try:
        spam
    except NameError:
        def spam(): ...

That could become:

    if undef spam:
        def spam(): ...

I would be cross if `if undef` worked inside functions but not globally.



-- 
Steven
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/RG2JDSOYQXYRLRIZN5UVAPS5MDEHMGF4/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to