* Steven D'Aprano, on 13.07.2010 01:34:
On Mon, 12 Jul 2010 20:28:49 +0200, Alf P. Steinbach /Usenet wrote:

As I see it it doesn't matter whether the implementation is CPython call
frame slots or that mechanism called something else or a different
mechanism called the same or a different mechanism called something
different; what IMO matters is same semantics, that any assignment to a
variable within a routine serves as a compile time declaration, creating
that local variable in advance, unless, with Python 3.x., that name has
been declared as a 'global' or 'nonlocal'.

So, this is a possible point of disagreement.

I say the semantics of local variable creation are part of the language
definition, but I get the /impression/ that maybe you think it's
CPython-specific, that e.g.

    def foo():
        x
        x = 0

might not raise an unassigned variable exception with some conforming
Python implementation, i.e. different effect for same code with
different implementations, that this is at least /unspecified behavior/
in Python?

Almost.

I believe that "any assignment to a variable within a routine serves as a
compile time declaration" is a promise of the language, but what an
implementation does in response to that declaration is unspecified. So
long as foo() raises NameError, or a subclass of it, it will be a
conforming Python implementation.

That's what Python 1.5 does, and I think if some competing implementation
targeted 1.5 we'd still be happy to call it Python. (Although we might
wonder about the author's sanity...) Implementations are free to subclass
NameError, as CPython does with UnboundLocalError, but mustn't raise a
completely unrelated error, or no error at all. E.g. I don't think it
would be acceptable to implicitly create new names and bind them to some
arbitrary default value.

E.g. an implementation might do something like this:

* when parsing the function, prior to compiling the byte-code, tag
   every name with a sigil representing whether it is local or non-local;
* compile a single byte-code for name lookup;
* when executing that instruction, if the name is tagged as a local,
   search only the local namespace, otherwise search the nonlocal,
   global and builtin namespaces;
* when displaying names to the user (say, in tracebacks) suppress
   the sigil.

Under this implementation, no variable actually exists until it is
assigned to.

This is equivalent to shifting the decision to use LOAD_FAST or
LOAD_GLOBAL to runtime rather than compile time, so it would probably
hurt performance rather than increase it, but it would still be a
conforming implementation.

But of course I'm not Guido, and he has the final word on what counts as
acceptable behaviour.

The 3.1.1 docs explicitly require UnboundLocalError (I just checked).

So, at least for 3.x it is not an implementation detail.

Anyway, your phrase "actually exist" presumably refers to storage allocation. That is an implementation detail. As a similar implementation scheme, a compiler can in certain cases detect that a variable is only assigned once, and substitute the value whereever that variable is used, not allocating any storage for it. This is the case in Python, in C++ and in almost any language. We don't start doubting the existence of variables in general (as some in the Python community do) on such grounds. More to the point of this sub-thread, it would be impossible to reason about things if one had to take into account the particular implementation's details at all times. Doing that renders most terms, including "exist", pretty meaningless and useless, since you would have to check whether each particular variable was optimized away or not: for the purpose of discussing existence in Python, what matters is portable effect.

Summing up, how CPython implements the required semantics, is irrelevant.

:-)


Cheers from Norway,

- Alf

--
blog at <url: http://alfps.wordpress.com>
--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to