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


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

Re: scope of function parameters

Started byWolfgang Rohdewald <wolfgang@rohdewald.de>
First post2011-05-29 11:47 +0200
Last post2011-05-29 13:12 -0600
Articles 9 — 5 participants

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

This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by below is the oldest one visible, not the original post.


Contents

  Re: scope of function parameters Wolfgang Rohdewald <wolfgang@rohdewald.de> - 2011-05-29 11:47 +0200
    Re: scope of function parameters Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2011-05-29 12:47 +0000
      Re: scope of function parameters Chris Angelico <rosuav@gmail.com> - 2011-05-30 03:53 +1000
        Re: scope of function parameters Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2011-05-29 18:28 +0000
      Re: scope of function parameters Chris Rebert <clp2@rebertia.com> - 2011-05-29 11:01 -0700
      Re: scope of function parameters Chris Angelico <rosuav@gmail.com> - 2011-05-30 04:38 +1000
        Re: scope of function parameters Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2011-05-29 18:53 +0000
          Re: scope of function parameters Chris Angelico <rosuav@gmail.com> - 2011-05-30 05:20 +1000
      Re: scope of function parameters Ian Kelly <ian.g.kelly@gmail.com> - 2011-05-29 13:12 -0600

#6522 — Re: scope of function parameters

FromWolfgang Rohdewald <wolfgang@rohdewald.de>
Date2011-05-29 11:47 +0200
SubjectRe: scope of function parameters
Message-ID<mailman.2217.1306662671.9059.python-list@python.org>
On Sonntag 29 Mai 2011, Henry Olders wrote:
> It seems that in Python, a variable inside a function is
> global unless it's assigned.

no, they are local

> I would have thought that a function parameter would
> automatically be considered local to the function. It doesn't
> make sense to me to pass a global to a function as a
> parameter.

it is local. But consider what you actually passed:
You did not pass a copy of the list but the list itself.
You could also say you passed a reference to the list.
All python variables only hold a pointer (the id) to
an object. This object has a reference count and is
automatically deleted when there are no more references
to it.

If you want a local copy of the list you can either
do what you called being ugly or do just that within
the function itself - which I think is cleaner and
only required once.

def fnc2(c):
		c = c[:]
        c[1] = 'having'
        return c


-- 
Wolfgang

[toc] | [next] | [standalone]


#6531

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2011-05-29 12:47 +0000
Message-ID<4de24045$0$29996$c3e8da3$5496439d@news.astraweb.com>
In reply to#6522
On Sun, 29 May 2011 11:47:26 +0200, Wolfgang Rohdewald wrote:

> On Sonntag 29 Mai 2011, Henry Olders wrote:
>> It seems that in Python, a variable inside a function is global unless
>> it's assigned.
> 
> no, they are local

I'm afraid you are incorrect. Names inside a function are global unless 
assigned to somewhere.

>>> a = 1
>>> def f():
...     print a  # Not local, global.
...
>>> f()
1


By default, names inside a function must be treated as global, otherwise 
you couldn't easily refer to global functions:

def f(x):
    print len(x)

because len would be a local name, which doesn't exist. In Python, built-
in names are "variables" just like any other.

Python's scoping rule is something like this:

If a name is assigned to anywhere in the function, treat it as a local, 
and look it up in the local namespace. If not found, raise 
UnboundLocalError.

If a name is never assigned to, or if it is declared global, then look it 
up in the global namespace. If not found, look for it in the built-ins. 
If still not found, raise NameError.

Nested scopes (functions inside functions) make the scoping rules a 
little more complex.

If a name is a function parameter, that is equivalent to being assigned 
to inside the function.


-- 
Steven

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


#6555

FromChris Angelico <rosuav@gmail.com>
Date2011-05-30 03:53 +1000
Message-ID<mailman.2229.1306691607.9059.python-list@python.org>
In reply to#6531
On Sun, May 29, 2011 at 10:47 PM, Steven D'Aprano
<steve+comp.lang.python@pearwood.info> wrote:
> If a name is assigned to anywhere in the function, treat it as a local,
> and look it up in the local namespace. If not found, raise
> UnboundLocalError.
>

Wait wha? I've never seen this... wouldn't it just create it in the
local namespace?

Can you give example code that will trigger this error? I'm curious, now...

Chris Angelico

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


#6560

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2011-05-29 18:28 +0000
Message-ID<4de29043$0$29996$c3e8da3$5496439d@news.astraweb.com>
In reply to#6555
On Mon, 30 May 2011 03:53:24 +1000, Chris Angelico wrote:

> On Sun, May 29, 2011 at 10:47 PM, Steven D'Aprano
> <steve+comp.lang.python@pearwood.info> wrote:
>> If a name is assigned to anywhere in the function, treat it as a local,
>> and look it up in the local namespace. If not found, raise
>> UnboundLocalError.
>>
>>
> Wait wha? I've never seen this... wouldn't it just create it in the
> local namespace?
> 
> Can you give example code that will trigger this error? I'm curious,
> now...

def f():
    print a  # a is not yet defined, i.e. unbound
    a = 1  # this makes a local


-- 
Steven

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


#6557

FromChris Rebert <clp2@rebertia.com>
Date2011-05-29 11:01 -0700
Message-ID<mailman.2231.1306692087.9059.python-list@python.org>
In reply to#6531
On Sun, May 29, 2011 at 10:53 AM, Chris Angelico <rosuav@gmail.com> wrote:
> On Sun, May 29, 2011 at 10:47 PM, Steven D'Aprano
> <steve+comp.lang.python@pearwood.info> wrote:
>> If a name is assigned to anywhere in the function, treat it as a local,
>> and look it up in the local namespace. If not found, raise
>> UnboundLocalError.
>>
>
> Wait wha? I've never seen this... wouldn't it just create it in the
> local namespace?
>
> Can you give example code that will trigger this error? I'm curious, now...

def foo():
    print bar
    bar = 42

foo()

===>
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in foo
UnboundLocalError: local variable 'bar' referenced before assignment

Cheers,
Chris

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


#6561

FromChris Angelico <rosuav@gmail.com>
Date2011-05-30 04:38 +1000
Message-ID<mailman.2233.1306694309.9059.python-list@python.org>
In reply to#6531
On Mon, May 30, 2011 at 4:01 AM, Chris Rebert <clp2@rebertia.com> wrote:
> def foo():
>    print bar
>    bar = 42
>
> foo()
>
> ===>
> Traceback (most recent call last):
>  File "<stdin>", line 1, in <module>
>  File "<stdin>", line 2, in foo
> UnboundLocalError: local variable 'bar' referenced before assignment

Wow

I thought it basically functioned top-down. You get a different error
on the print line if there's a "bar = 42" *after* it. This could make
debugging quite confusing.

Guess it's just one of the consequences of eschewing variable
declarations. Sure it's easier, but there's complications down the
road.

Chris Angelico

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


#6563

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2011-05-29 18:53 +0000
Message-ID<4de29612$0$29996$c3e8da3$5496439d@news.astraweb.com>
In reply to#6561
On Mon, 30 May 2011 04:38:26 +1000, Chris Angelico wrote:

> On Mon, May 30, 2011 at 4:01 AM, Chris Rebert <clp2@rebertia.com> wrote:
>> def foo():
>>    print bar
>>    bar = 42
>>
>> foo()
>>
>> ===>
>> Traceback (most recent call last):
>>  File "<stdin>", line 1, in <module>
>>  File "<stdin>", line 2, in foo
>> UnboundLocalError: local variable 'bar' referenced before assignment
> 
> Wow
> 
> I thought it basically functioned top-down. You get a different error on
> the print line if there's a "bar = 42" *after* it. This could make
> debugging quite confusing.

UnboundLocalError is a subclass of NameError, so it will still be caught 
by try...except NameError.

If you're crazy enough to be catching NameError :)

Go back to Python1.5, and there was no UnboundLocalError. It was 
introduced because people were confused when they got a NameError after 
forgetting to declare something global:


>>> def f():
...     print a
...     a = a + 1
...
>>> a = 42
>>> f()
Traceback (innermost last):
  File "<stdin>", line 1, in ?
  File "<stdin>", line 2, in f
NameError: a


While UnboundLocalError is jargon, and not the easiest error message to 
comprehend, at least it confuses in a different way :)


-- 
Steven

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


#6566

FromChris Angelico <rosuav@gmail.com>
Date2011-05-30 05:20 +1000
Message-ID<mailman.2235.1306696854.9059.python-list@python.org>
In reply to#6563
On Mon, May 30, 2011 at 4:53 AM, Steven D'Aprano
<steve+comp.lang.python@pearwood.info> wrote:
> UnboundLocalError is a subclass of NameError, so it will still be caught
> by try...except NameError.
>
> If you're crazy enough to be catching NameError :)

Ah okay. So it is still NameError, it just doesn't look like one.

> While UnboundLocalError is jargon, and not the easiest error message to
> comprehend, at least it confuses in a different way :)

I have nothing against jargon, and specific errors are better than
generic ones (imagine if every error were thrown as Exception with a
string parameter... oh wait, that's what string exceptions are).

It still seems a little odd that a subsequent line can affect this
one. But Python's mostly doing what would be expected of it; the worst
I can come up with is this:

def f():
  print(foo) # reference a global
  ...
  for foo in bar: # variable only used in loop
    pass

If you're used to C++ and declaring variables inside a for loop eg
"for (int i=0;i<10;++i)", you might not concern yourself with the fact
that 'foo' is masking a global; it's not an issue, because you don't
need that global inside that loop, right? And it would be fine, except
that that global IS used somewhere else in the function. It'd be a bit
confusing to get the UnboundLocalError up on the print(foo) line (the
one that's meant to be using the global), since that line isn't wrong;
and the "obvious fix", adding an explicit "global foo" to the top of
the function, would be worse (because it would mean that the for loop
overwrites the global).

This is why I would prefer to declare variables. The Zen of Python
says that explicit is better than implicit, but in this instance,
Python goes for DWIM, guessing whether you meant global or local. It
guesses fairly well, though.

Chris Angelico

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


#6565

FromIan Kelly <ian.g.kelly@gmail.com>
Date2011-05-29 13:12 -0600
Message-ID<mailman.2234.1306696367.9059.python-list@python.org>
In reply to#6531
On Sun, May 29, 2011 at 12:38 PM, Chris Angelico <rosuav@gmail.com> wrote:
> I thought it basically functioned top-down. You get a different error
> on the print line if there's a "bar = 42" *after* it. This could make
> debugging quite confusing.
>
> Guess it's just one of the consequences of eschewing variable
> declarations. Sure it's easier, but there's complications down the
> road.

It's also a consequence of local variable access being optimized with
different bytecode: the type of storage has to be determined at
compile time.  The compiler could in principle figure out that "bar"
cannot be bound at that point and make it a global reference, but it
is easy to concoct situations involving loops or conditionals where
the storage cannot be determined at compile time, and so the compiler
follows the simple rule of making everything local unless it's never
assigned.

Cheers,
Ian

[toc] | [prev] | [standalone]


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


csiph-web