Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #100876 > unrolled thread

A newbie quesiton: local variable in a nested funciton

Started byjfong@ms4.hinet.net
First post2015-12-25 19:06 -0800
Last post2015-12-27 17:22 +1100
Articles 12 — 3 participants

Back to article view | Back to comp.lang.python


Contents

  A newbie quesiton: local variable in a nested funciton jfong@ms4.hinet.net - 2015-12-25 19:06 -0800
    Re: A newbie quesiton: local variable in a nested funciton Ben Finney <ben+python@benfinney.id.au> - 2015-12-26 14:41 +1100
      Re: A newbie quesiton: local variable in a nested funciton jfong@ms4.hinet.net - 2015-12-26 00:56 -0800
        Re: A newbie quesiton: local variable in a nested funciton Ben Finney <ben+python@benfinney.id.au> - 2015-12-26 20:37 +1100
    Re: A newbie quesiton: local variable in a nested funciton Chris Angelico <rosuav@gmail.com> - 2015-12-26 14:44 +1100
      Re: A newbie quesiton: local variable in a nested funciton jfong@ms4.hinet.net - 2015-12-26 01:07 -0800
        Re: A newbie quesiton: local variable in a nested funciton Chris Angelico <rosuav@gmail.com> - 2015-12-26 20:49 +1100
          Re: A newbie quesiton: local variable in a nested funciton jfong@ms4.hinet.net - 2015-12-26 20:05 -0800
            Re: A newbie quesiton: local variable in a nested funciton jfong@ms4.hinet.net - 2015-12-26 20:11 -0800
              Re: A newbie quesiton: local variable in a nested funciton Chris Angelico <rosuav@gmail.com> - 2015-12-27 17:32 +1100
                Re: A newbie quesiton: local variable in a nested funciton jfong@ms4.hinet.net - 2015-12-27 17:02 -0800
            Re: A newbie quesiton: local variable in a nested funciton Chris Angelico <rosuav@gmail.com> - 2015-12-27 17:22 +1100

#100876 — A newbie quesiton: local variable in a nested funciton

Fromjfong@ms4.hinet.net
Date2015-12-25 19:06 -0800
SubjectA newbie quesiton: local variable in a nested funciton
Message-ID<d070aa0d-e80f-4efb-a424-351737ddb2fc@googlegroups.com>
As a tranditional language programmer like me, the result is really weird.

Here is the test codes in file test1.py:
--------
def outerf():
    counter = 55
    def innerf():
        print(counter)
        #counter += 1
    return innerf

myf = outerf()
--------

the result is:
--------
>>> import test1
>>> test1.myf()
55
>>>
--------

that's OK. But if I un-comment the line "counter += 1", then it gives me this:
--------
>>> import test1
>>> test1.myf()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "D:\Work\Python34\test1.py", line 41, in innerf
    print(counter)
UnboundLocalError: local variable 'counter' referenced before assignment
>>> 
--------

In the first situation, the local variable 'counter' can be referenced correctly. But in the second, why a statement added after the print() statement can makes this variable "disappear", even the print() won't do the right thing. Isn't it wired? please help!

[toc] | [next] | [standalone]


#100877

FromBen Finney <ben+python@benfinney.id.au>
Date2015-12-26 14:41 +1100
Message-ID<mailman.16.1451101299.11925.python-list@python.org>
In reply to#100876
jfong@ms4.hinet.net writes:

> In the first situation, the local variable 'counter' can be referenced
> correctly. But in the second, why a statement added after the print()
> statement can makes this variable "disappear", even the print() won't
> do the right thing. Isn't it wired? please help!

The Python FAQ answers this, even using an example the same as yours
<URL:https://docs.python.org/3/faq/programming.html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value>.

-- 
 \          “… a Microsoft Certified System Engineer is to information |
  `\     technology as a McDonalds Certified Food Specialist is to the |
_o__)                               culinary arts.” —Michael Bacarella |
Ben Finney

[toc] | [prev] | [next] | [standalone]


#100882

Fromjfong@ms4.hinet.net
Date2015-12-26 00:56 -0800
Message-ID<ffcd67b4-365b-44bb-87c1-38ff3f49d5ef@googlegroups.com>
In reply to#100877
Ben Finney at 2015/12/26  UTC+8 11:42:08AM wrote:
> The Python FAQ answers this, even using an example the same as yours
> <URL:https://docs.python.org/3/faq/programming.html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value>.
> 
Thank you, Ben. It's amazing that you seem to know every piece of Python information hiding in the web:-)

see this question listed in python core language's Q&A makes me feel better for I am not the only one who commit this kind of crime. I should read this Q&A throughly before going any further.

--Jach

[toc] | [prev] | [next] | [standalone]


#100885

FromBen Finney <ben+python@benfinney.id.au>
Date2015-12-26 20:37 +1100
Message-ID<mailman.18.1451122658.11925.python-list@python.org>
In reply to#100882
jfong@ms4.hinet.net writes:

> Thank you, Ben. It's amazing that you seem to know every piece of
> Python information hiding in the web:-)

You're welcome, I'm glad to help. As for the “hiding”, the answer is in
the Python documentation itself.

> see this question listed in python core language's Q&A makes me feel
> better for I am not the only one who commit this kind of crime.

Never feel embarrassed of not knowing something; we should all be active
on the edge of our own ignorance. The embarrassment would be in not
seeking to improve our understanding.

> I should read this Q&A throughly before going any further.

Searching in the official documentation, and in archives of community
forums, is an essential skill to learn for a programmer. I wish you good
fortune in improving that skill :-)

-- 
 \      “Nothing is more sacred than the facts.” —Sam Harris, _The End |
  `\                                                   of Faith_, 2004 |
_o__)                                                                  |
Ben Finney

[toc] | [prev] | [next] | [standalone]


#100878

FromChris Angelico <rosuav@gmail.com>
Date2015-12-26 14:44 +1100
Message-ID<mailman.17.1451101449.11925.python-list@python.org>
In reply to#100876
On Sat, Dec 26, 2015 at 2:06 PM,  <jfong@ms4.hinet.net> wrote:
> As a tranditional language programmer like me, the result is really weird.

By "traditional", I'm guessing you mean that you know C-like languages
(Java, ECMAScript/JavaScript, etc). In C, and in many languages
derived from or inspired by it, variable scoping is defined by
declarations that say "here begins a variable".

> Here is the test codes in file test1.py:
> --------
> def outerf():
>     counter = 55
>     def innerf():
>         print(counter)
>         #counter += 1
>     return innerf
>
> myf = outerf()

Pike is semantically very similar to Python, but it uses C-like
variable scoping. Here's an equivalent, which might help with
comprehension:

function outerf()
{
    int counter = 55;
    void innerf()
    {
        write("%d\n", counter);
        int counter;
        counter += 1;
    }
    return innerf;
}

Based on that, I think you can see that having a variable declaration
in the function turns things into nonsense. What you're actually
wanting here is to NOT have the "int counter;" line, such that the
name 'counter' refers to the outerf one.

In Python, assignment inside a function creates a local variable,
unless you declare otherwise. To make your example work, all you need
is one statement:

nonlocal counter

That'll cause the name 'counter' inside innerf to refer to the same
thing as it does in outerf.

Hope that helps!

ChrisA

[toc] | [prev] | [next] | [standalone]


#100884

Fromjfong@ms4.hinet.net
Date2015-12-26 01:07 -0800
Message-ID<701dd0e6-a9c1-4aa9-a3a2-6607cd3f3759@googlegroups.com>
In reply to#100878
Chris Angelico at 2015/12/26  UTC+8 11:44:21AM wrote:
> Pike is semantically very similar to Python, but it uses C-like
> variable scoping. Here's an equivalent, which might help with
> comprehension:
> 
> function outerf()
> {
>     int counter = 55;
>     void innerf()
>     {
>         write("%d\n", counter);
>         int counter;
>         counter += 1;
>     }
>     return innerf;
> }
Hi! ChrisA, this is the first time I hear the name "Pike" programming language:-)

> Based on that, I think you can see that having a variable declaration
> in the function turns things into nonsense. What you're actually
> wanting here is to NOT have the "int counter;" line, such that the
> name 'counter' refers to the outerf one.
> 
> In Python, assignment inside a function creates a local variable,
> unless you declare otherwise. To make your example work, all you need
> is one statement:
> 
> nonlocal counter
> 
> That'll cause the name 'counter' inside innerf to refer to the same
> thing as it does in outerf.

Thank you for the explanation. It reminds me to dig out something which seems I had been read before. It's about nested scope in the book "Learning Python" by Mark Lutz.

 "An assignment (X = value) creates or changes the name X in the current local
scope, by default. If X is declared global within the function, the assignment creates or changes the name X in the enclosing module's scope instead. If, on the other hand, X is declared  nonlocal within the function in 3.X (only), the assignment changes the name X in the closest enclosing function's local scope."

I shouldn't forget this:-(
 
> Hope that helps!

You have a correct answer. Thanks again.

--Jach

[toc] | [prev] | [next] | [standalone]


#100886

FromChris Angelico <rosuav@gmail.com>
Date2015-12-26 20:49 +1100
Message-ID<mailman.19.1451123395.11925.python-list@python.org>
In reply to#100884
On Sat, Dec 26, 2015 at 8:07 PM,  <jfong@ms4.hinet.net> wrote:
> Thank you for the explanation. It reminds me to dig out something which seems I had been read before. It's about nested scope in the book "Learning Python" by Mark Lutz.
>
>  "An assignment (X = value) creates or changes the name X in the current local
> scope, by default. If X is declared global within the function, the assignment creates or changes the name X in the enclosing module's scope instead. If, on the other hand, X is declared  nonlocal within the function in 3.X (only), the assignment changes the name X in the closest enclosing function's local scope."
>

Yep! That's an accurate description of how assignment works.

One of the cool things about Python is that there are all sorts of
things that work _exactly_ the same way. Every one of these statements
is a form of assignment:

import json  # 1
from sys import argv  # 2
def testfiles(files): # 3, 4
    failures = 0 # 5
    for file in files:  # 6
        with open(file) as fp: # 7
            try: data = json.load(fp) # 8
            except JSONDecodeError as err: # 9
                failures += 1 # 10
    return failures
count = testfiles(argv) # 11

Okay, every one except the 'return' statement. :)

1: "import x" is basically the same as "x = __import__('x')".
2: "from x import y" is basically "y = __import__('x').y" (more or
less). This grabs "sys.argv" and assigns it to the name "argv".
3: Defining a function constructs a new function object and assigns it
to the name. This is like doing "testfiles = <magic>".
4: As the function gets called, a new scope is created, and inside
that scope, the interpreter does the equivalent of "files = <magic>",
where the magic snags a reference to whatever was used as the argument
(so this is basically "files = argv").
5: That's straight-forward assignment, right there.
6: A 'for' loop grabs an iterator, then repeatedly does "file =
next(iterator)" until there's nothing more to do.
7: A 'with' statement does some work, and then does "fp = <result of
that work>" if it has an 'as' clause.
8: Another straight-forward assignment, because I couldn't think of
anything better to use. (Normally you'd lay this out with 'try:' on a
separate line, but then I'd have had a line without any assignment at
all.)
9: Like a 'with' statement, 'except' assigns to its name. There's a
special case here, in that it also does 'del err' at the end of the
except block, to clean stuff up; but it's still just assignment.
10: Augmented assignment is assignment too. x+=1, x-=1, x*=1, etc, are
all assigning to x.
11: Another normal assignment, because otherwise the rest of the work
is pointless. :)

Every one of these follows the standard rules for assignment. For
instance, you could create a function that does a top-level import:

$ python3
Python 3.6.0a0 (default:6e114c4023f5, Dec 20 2015, 19:15:28)
[GCC 4.9.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def do_imports():
...     global os, sys, json
...     import os, sys, json
...
>>> do_imports()
>>> json
<module 'json' from '/usr/local/lib/python3.6/json/__init__.py'>

You wouldn't normally do this for standard modules like 'os' and
'sys', but if you have something huge to load up (like pandas, or
oauth2client), it might be convenient to use them globally, but load
them conditionally. Since 'import' is a statement, not a declaration,
you can do this!

Python's flexibility and simplicity are a huge part of why I love the
language so much.

ChrisA

[toc] | [prev] | [next] | [standalone]


#100900

Fromjfong@ms4.hinet.net
Date2015-12-26 20:05 -0800
Message-ID<661a13a1-8084-41ca-b458-297462dc3367@googlegroups.com>
In reply to#100886
Chris Angelico at 2015/12/26  UTC+8 5:50:07PM wrote:
> 11: Another normal assignment, because otherwise the rest of the work
> is pointless. :)

Thanks for this detailed example. As I had learned so far, Python really take "name" seriously, and every meaningful result you got have to assign to a name at last. This morning I played some codes on the http://pythonturor.com and found out that every objects which was not created at top-level of a module or in a class will disappear after import. A very "unusual" view. 

> Python's flexibility and simplicity are a huge part of why I love the
> language so much.

simplicity? Maybe because you are soooo familiar with Python. It's not to me, at least at this moment. Please see my next question follows.

--Jach

[toc] | [prev] | [next] | [standalone]


#100901

Fromjfong@ms4.hinet.net
Date2015-12-26 20:11 -0800
Message-ID<0bf611ba-c4d9-4465-8d61-6a94bcee79a4@googlegroups.com>
In reply to#100900
Last night I noticed that Python does not resolve name in "def" during import, as C does in the compile/link stage, it was deferred until it was referenced (i.e. codes was executed). That's OK for Anyway codes has to be debugged sooner or later. I just have to get used to this style.

But check these codes, it seems not.
-------
x = 1  # a global variable
print(x)

class Test:
    x = 4  # a class attribute
    print(x)
    def func(self):
        print(x)

x1 = Test()
x1.x = 41  # a instance's attribute
x1.func()  # it's 1 but 41 was expect:-(
--------

--Jach

[toc] | [prev] | [next] | [standalone]


#100903

FromChris Angelico <rosuav@gmail.com>
Date2015-12-27 17:32 +1100
Message-ID<mailman.27.1451197940.11925.python-list@python.org>
In reply to#100901
On Sun, Dec 27, 2015 at 3:11 PM,  <jfong@ms4.hinet.net> wrote:
> Last night I noticed that Python does not resolve name in "def" during import, as C does in the compile/link stage, it was deferred until it was referenced (i.e. codes was executed). That's OK for Anyway codes has to be debugged sooner or later. I just have to get used to this style.
>
> But check these codes, it seems not.
> -------
> x = 1  # a global variable
> print(x)
>
> class Test:
>     x = 4  # a class attribute
>     print(x)
>     def func(self):
>         print(x)
>
> x1 = Test()
> x1.x = 41  # a instance's attribute
> x1.func()  # it's 1 but 41 was expect:-(
> --------
>
> --Jach

When you import this module, it runs all top-level code. So the
'print' at the top will happen at import time.

Among the top-level statements is a class definition. When that gets
run (to construct the class itself - distinct from instantiating it,
which happens further down), it builds a class by executing all the
statements in it, in order. That results in that value of x being
printed, and then defines a function.

The function definition is being run at time of class construction,
and it creates a new attribute on the Test class. At that time, the
function body isn't actually executed (as you might expect). However,
it's worth noting that the function does not inherit class scope. The
unadorned name 'x' references the global. If you want to access
class-scope names from inside methods, you need to say 'self.x', which
also applies to instance attributes. That's what would do what you
expect here.

ChrisA

[toc] | [prev] | [next] | [standalone]


#100921

Fromjfong@ms4.hinet.net
Date2015-12-27 17:02 -0800
Message-ID<fa863fcb-1bbf-45ba-9b70-62c408f94856@googlegroups.com>
In reply to#100903
Chris Angelico at 2015/12/27  UTC+8 2:32:32PM wrote:
> On Sun, Dec 27, 2015 at 3:11 PM,  <jfong@ms4.hinet.net> wrote:
> > Last night I noticed that Python does not resolve name in "def" during import, as C does in the compile/link stage, it was deferred until it was referenced (i.e. codes was executed). That's OK for Anyway codes has to be debugged sooner or later. I just have to get used to this style.
> >
> > But check these codes, it seems not.
> > -------
> > x = 1  # a global variable
> > print(x)
> >
> > class Test:
> >     x = 4  # a class attribute
> >     print(x)
> >     def func(self):
> >         print(x)
> >
> > x1 = Test()
> > x1.x = 41  # a instance's attribute
> > x1.func()  # it's 1 but 41 was expect:-(
> > --------
> >
> > --Jach
> 
> When you import this module, it runs all top-level code. So the
> 'print' at the top will happen at import time.
> 
> Among the top-level statements is a class definition. When that gets
> run (to construct the class itself - distinct from instantiating it,
> which happens further down), it builds a class by executing all the
> statements in it, in order. That results in that value of x being
> printed, and then defines a function.
> 
> The function definition is being run at time of class construction,
> and it creates a new attribute on the Test class. At that time, the
> function body isn't actually executed (as you might expect). However,
> it's worth noting that the function does not inherit class scope. The
> unadorned name 'x' references the global. If you want to access
> class-scope names from inside methods, you need to say 'self.x', which
> also applies to instance attributes. That's what would do what you
> expect here.
> 
> ChrisA

Yea, right, it's in a method, not a function. A stupid mistake I had made:-(
Thanks for your kindly patient with me, and Happy New Year to you:-)

--Jach

[toc] | [prev] | [next] | [standalone]


#100902

FromChris Angelico <rosuav@gmail.com>
Date2015-12-27 17:22 +1100
Message-ID<mailman.26.1451197757.11925.python-list@python.org>
In reply to#100900
On Sun, Dec 27, 2015 at 3:05 PM,  <jfong@ms4.hinet.net> wrote:
>> Python's flexibility and simplicity are a huge part of why I love the
>> language so much.
>
> simplicity? Maybe because you are soooo familiar with Python. It's not to me, at least at this moment. Please see my next question follows.
>

I define "simplicity" in terms of the number and complexity of the
rules you have to keep in your head. It's not necessarily the best
thing to do; for instance, a C-style #include directive is *extremely*
simple (it is literally "drop the file contents in at this location"),
but I prefer Python's semantics. On the other hand, PHP's include
directive is *not* simple; in many ways it behaves like C's #include,
but it can't be used inside class definitions, and if used in a
function, some of what it does is at top level and some is inside the
function.

Python's rules for imports (whether they're "import X" or "from X
import Y") include a somewhat complicated definition of search path,
but ultimately, it's a matter of hunting down a module and executing
it (all of it) in its own namespace, and then giving you a reference
to sys.modules["X"] (or sys.modules["X"].Y for a from-import).

ChrisA

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web