A Function's name during its definition

Chris Angelico rosuav at gmail.com
Tue Feb 7 18:33:44 EST 2023


On Wed, 8 Feb 2023 at 10:18, Mark Bourne <nntp.mbourne at spamgourmet.com> wrote:
>
> Stefan Ram wrote:
> > Mark Bourne <nntp.mbourne at spamgourmet.com> writes:
> >> In the second case, eval() only gets the globals and immediate locals,
> >
> >    Yes, I think you are right. Curiously, the following program would
> >    mislead one to thing that eval /does/ see the intermediate names:
> >
> >    main.py
> >
> > def f():
> >      x = 22
> >      def g():
> >          print( x )
> >          print( eval( 'x' ))
> >      g()
> > f()
> >
> >    output
> >
> > 22
> > 22
> >
> >    . But "print( x )" had the effect of making that x local.
> >    Without it, we see the error:
> >
> >    main.py
> >
> > def f():
> >      x = 22
> >      def g():
> >          print( eval( 'x' ))
> >      g()
> > f()
> >
> >    error output
> >
> > NameError: name 'x' is not defined

Don't know if Stefan realises it, but newsgroup text that gets replies
ends up on the mailing list. So if this is a problem of copyright, you
should probably stop posting to the newsgroup - unless you want people
like Mark to unintentionally violate your copyright.

> That is interesting.  I know assigning to a value creates a local
> version (and the non-local then can't be accessed, even before the new
> value was assigned), but hadn't realised just referencing it brought it
> into local scope for reading.  I guess that's to do with closures, where
> any variables referenced within the function get bound to the function's
> scope.  e.g. if g() includes a reference to x [as it does in the first
> example above], and f() returned a reference to g(), calling g() later
> [from outside of f()] by using that reference would still be able to
> access x.

Yep, that's to do with closures. Your analysis is spot on. When a name
is local to an outer function and referenced in an inner function, it
becomes a nonlocal in both functions. You can also declare it with
"nonlocal x" in the inner function, which - like with a global
statement - allows you to assign to it.

> With just the eval() call and no other reference to x, the interpreter
> doesn't know at that time the closure is created that there is a
> reference to x.  At that point, the 'x' is just text in a string.  It's
> only when eval() gets called that it tries to execute that string as
> Python code - and finds that x isn't in scope.

Mostly true, yes. The eval function doesn't get any magic ability to
refer to nearby variables, so what it does is it depends on the
globals() and locals() mappings (which can be the same dictionary
under some circumstances).

I think you'll agree that globals() does not and should not contain a
reference to x here. It's definitely not a global.

But is x a local? Well, technically it's a nonlocal; but more
importantly, there's a difference between "theoretically could be a
nonlocal" and "is actually a nonlocal", as demonstrated by this:

def f():
    x = 42
    def g():
        print("without", locals())
    g()
    def h():
        x
        print("with", locals())
    h()

So, can eval() find x?

x = 1234
def f():
    x = 42
    def g():
        print("without", eval("x"))
    g()
    def h():
        x
        print("with", eval("x"))
    h()

>>> f()
without 1234
with 42

The compiler knows exactly which names are *theoretically available*
(which is a lexical concept), but at run time, the only names that are
actually available are those which have been captured.

ChrisA


More information about the Python-list mailing list