What (if anything) is actually different between using a name in a nested function and passing a name to a nested function? If there's no difference, which is preferred by convention?
def foo(bar):
def put():
bar.append('World!')
print(', '.join(bar))
put()
foo(['Hello'])
versus
def foo(bar):
def put(bar):
bar += ['World!']
print(', '.join(bar))
put(bar)
foo(['Hello'])
Since you're only talking about nested functions, and not closures, unless you have a specific reason to have it be nested, I would recommend defining each function in the module scope:
def put(bar):
bar += ['World!']
print(', '.join(bar))
def foo(bar):
put(bar)
foo(['Hello'])
If for some reason you do need it to be nested, it's better to pass in bar explicitly. However, if you do think you need it to be nested, ask about your reasoning first; there's a good chance that you don't. :)
The difference is that in the first one, bar variable is in a scope of the parent function, it can be used in the child function , unless you do assignment on it (This would be the case something similar to using global variables in function) . Example -
>>> def foo(bar):
... def put():
... bar = bar + ['World']
... print(', '.join(bar))
... put()
...
>>>
>>> foo(['Hello'])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in foo
File "<stdin>", line 3, in put
UnboundLocalError: local variable 'bar' referenced before assignment
In this case if you want to use the bar and assign to it as well, you need to use the nonlocal keyword , Example -
>>> def foo(bar):
... def put():
... nonlocal bar
... bar = bar + ['World!']
... print(', '.join(bar))
... put()
...
>>> foo(['Hello'])
Hello, World!
Whereas in the second one, bar is a local variable to the put() function (because its an argument to it) and can be assigned without the above UnboundLocalError , Example -
>>> def foo(bar):
... def put(bar):
... bar = bar + ['World!']
... print(', '.join(bar))
... put(bar)
...
>>>
>>> foo(['Hello'])
Hello, World!
I would prefer explicitly passing the required arguments as done in the second case.
Related
I'm aware of creating a custom __repr__ or __add__ method (and so on), to modify the behavior of operators and functions. Is there a method override for len?
For example:
class Foo:
def __repr__(self):
return "A wild Foo Class in its natural habitat."
foo = Foo()
print(foo) # A wild Foo Class in its natural habitat.
print(repr(foo)) # A wild Foo Class in its natural habitat.
Could this be done for len, with a list? Normally, it would look like this:
foo = []
print(len(foo)) # 0
foo = [1, 2, 3]
print(len(foo)) # 3
What if I want to leave search types out of the count? Like this:
class Bar(list):
pass
foo = [Bar(), 1, '']
print(len(foo)) # 3
count = 0
for item in foo:
if not isinstance(item, Bar):
count += 1
print(count) # 2
Is there a way to do this from within a list subclass?
Yes, implement the __len__ method:
def __len__(self):
return 42
Demo:
>>> class Foo(object):
... def __len__(self):
... return 42
...
>>> len(Foo())
42
From the documentation:
Called to implement the built-in function len(). Should return the length of the object, an integer >= 0. Also, an object that doesn’t define a __bool__() method and whose __len__() method returns zero is considered to be false in a Boolean context.
For your specific case:
>>> class Bar(list):
... def __len__(self):
... return sum(1 for ob in self if not isinstance(ob, Bar))
...
>>> len(Bar([1, 2, 3]))
3
>>> len(Bar([1, 2, 3, Bar()]))
3
Yes, just as you have already discovered that you can override the behaviour of a repr() function call by implementing the __repr__ magic method, you can specify the behaviour from a len() function call by implementing (surprise surprise) then __len__ magic:
>>> class Thing:
... def __len__(self):
... return 123
...
>>> len(Thing())
123
A pedant might mention that you are not modifying the behaviour of len(), you are modifying the behaviour of your class. len just does the same thing it always does, which includes checking for a __len__ attribute on the argument.
Remember: Python is a dynamically and Duck Typed language.
If it acts like something that might have a length;
class MyCollection(object):
def __len__(self):
return 1234
Example:
>>> obj = MyCollection()
>>> len(obj)
1234
if it doesn't act like it has a length; KABOOM!
class Foo(object):
def __repr___(self):
return "<Foo>"
Example:
>>> try:
... obj = Foo()
... len(obj)
... except:
... raise
...
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
TypeError: object of type 'Foo' has no len()
From Typing:
Python uses duck typing and has typed objects but untyped variable
names. Type constraints are not checked at compile time; rather,
operations on an object may fail, signifying that the given object is
not of a suitable type. Despite being dynamically typed, Python is
strongly typed, forbidding operations that are not well-defined (for
example, adding a number to a string) rather than silently attempting
to make sense of them.
Example:
>>> x = 1234
>>> s = "1234"
>>> x + s
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'
You can just add a __len__ method to your class.
class Test:
def __len__(self):
return 2
a=Test()
len(a) # --> 2
I was playing around with closures in Python and I do not understand why the following does not work and how to make it work:
>>> def make_counter():
... i = 0
... def inner():
... i += 1
... return i
... return inner
...
>>> c = make_counter()
>>> c()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in inner
UnboundLocalError: local variable 'i' referenced before assignment
Could anybody shed some light on it?
Thanks.
In the inner function, the statement
i += 1
can be understood like this
i = i + 1
since you are assigning a value to i in this function, Python thinks that you are creating a new variable scoped to this current function. And then when it executes the right hand side expression, i + 1, it finds that i has not been assigned any value before using it. That is why it is throwing
UnboundLocalError: local variable 'i' referenced before assignment
To fix this, you need to explicitly tell Python that you are not creating a new variable, instead, you are accessing a variable from the outer scope, with nonlocal (Python 3.x) like this
>>> def make_counter():
... i = 0
... def inner():
... nonlocal i
... i += 1
... return i
... return inner
...
>>>
>>> make_counter()()
1
Note: If you are using Python 2.x, follow any of the methods mentioned in this question
Need help with understanding the following sentence from PEP 227 and the Python Language Reference
If a variable is referenced in an enclosed scope, it is an error to
delete the name. The compiler will raise a SyntaxError for 'del
name'.
Lack of examples caused I couldn't reproduce an error at compile time, so an explanation with examples is highly desirable.
The following raises the execption:
def foo():
spam = 'eggs'
def bar():
print spam
del spam
because the spam variable is being used in the enclosed scope of bar:
>>> def foo():
... spam = 'eggs'
... def bar():
... print spam
... del spam
...
SyntaxError: can not delete variable 'spam' referenced in nested scope
Python detects that spam is being referenced in bar but does not assign anything to that variable, so it looks it up in the surrounding scope of foo. It is assigned there, making the del spam statement a syntax error.
This limitation was removed in Python 3.2; you are now responsible for not deleting nested variables yourself; you'll get a runtime error (NameError) instead:
>>> def foo():
... spam = 'eggs'
... def bar():
... print(spam)
... del spam
... bar()
...
>>> foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in foo
File "<stdin>", line 4, in bar
NameError: free variable 'spam' referenced before assignment in enclosing scope
An example can be this one:
>>> def outer():
... x = 0
... y = (x for i in range(10))
... del x
...
SyntaxError: can not delete variable 'x' referenced in nested scope
Basically it means you can't delete variables that are used in inner blocks(in that case the genexp).
Note that this apply for python <= 2.7.x and python < 3.2.
In python3.2 it's it does not raise syntax error:
>>> def outer():
... x = 0
... y = (x for i in range(10))
... del x
...
>>>
See this link for the whole story of the change.
I think the python3.2 semanthics is more correct because if you write the same code outside a function it works:
#python2.7
>>> x = 0
>>> y = (x for i in range(10))
>>> del x
>>> y.next() #this is what I'd expect: NameError at Runtime
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <genexpr>
NameError: global name 'x' is not defined
While putting the same code into a function, not only changes exception but the error is at compile time.
This question already has answers here:
Local variables in nested functions
(4 answers)
Closed 8 years ago.
Given this piece of code(python)
s = [None]*10
def func():
for i in range(10):
def update():
print i,
s[i] = update
func()
for i in range(10):
s[i]()
why this result is ten 9s, instead of 0,1,2,3,...9 ?
btw, I've also print s[0]~s[9],which are 10 function addresses, and there are different from each other.
You've created a bunch of closures over i, but they are all sharing the same (final) value of i
You need to make a tiny modification like this
>>> s = [None]*10
>>> def func():
... for i in range(10):
... def update(i=i):
... print i,
... s[i] = update
...
>>> func()
>>> for i in range(10):
... s[i]()
...
0 1 2 3 4 5 6 7 8 9
#gnibbler's code will fix yours (so accept his answer if anyone's). As for why it is printing all 9's, look at your update function. You are (again, as #gnibbler mentioned) continuously returning the i that you defined in for i in range(10). Since in your original code you did not call the update method with a function argument, it simply prints the i that was defined in the func scope (a completely different i which will be 9 after the function completes).
To perhaps make it more clear, try changing the i in func to a completely different name - the result will be the same.
What's happening is the same as this:
>>> a = 5
>>> def foo(): print(a)
>>> foo()
5
>>> a = 10
>>> foo()
10
>>> a = 'fred'
>>> foo()
fred
And also the same as this:
>>> def bar(): return b
>>> bar()
Traceback (most recent call last):
File "<pyshell#30>", line 1, in <module>
bar()
File "<pyshell#29>", line 1, in bar
def bar(): return b
NameError: global name 'b' is not defined
>>> b = 3
>>> bar()
3
The variables you used inside a function aren't resolved until the function is called not when it is written. There's some magic, called closures, that means functions defined inside other functions (as your update functions are defined inside func) still have access to all the variables defined in the outer function - but they still don't actually get resolved until the function is called. So, by the time each of your update functions is called, i is 9.
Using default argument values, as in #gnibbler's answer, works because the i that each update looks up will resolve to the argument (which shadows the outer variable). Those won't change, because default argument values are evaluated when the function is defined (which also leads to the mutable defaults bug that a lot of people run into sooner or later).
I am trying a piece of code from the question in Lexical closures in Python
flist = []
for i in xrange(3):
def func(x): return x*i
flist.append(func)
for f in flist:
print f.func_closure
The output is:
None
None
None
Shouldn't it be?:
(<cell at 0x9222d94: int object at 0x8cabdbc>,)
(<cell at 0x9222d94: int object at 0x8cabdbc>,)
(<cell at 0x9222d94: int object at 0x8cabdbc>,)
I have got the above output using the following code:
flist = []
def actualFact():
for i in xrange(3):
def func(x): return x * i
flist.append(func)
for f in flist:
print f.func_closure
I am using Python 2.6.6 (r266:84292, Sep 15 2010, 15:52:39).
Closures are only introduced if there are variables to be referenced outside of the global (module) scope:
>>> def foo():
... def bar(): pass
... return bar
...
>>> foo().func_closure is None
True
>>> spam = 'eggs'
>>> def foo():
... def bar(): return spam
... return bar
...
>>> foo().func_closure is None
True
Only when the inner function refers to a variable in the surrounding scope are closures generated:
>>> def foo():
... spam = 'eggs'
... def bar(): return spam
... return bar
...
>>> foo().func_closure is None
False
>>> foo().func_closure
(<cell at 0x108472718: str object at 0x108471de0>,)
Note that you actually have to refer to a variable in the surrounding scope. Simply ignoring the scope gives you None again:
>>> def foo():
... spam = 'eggs'
... def bar(): pass
... return bar
...
>>> foo().func_closure is None
True
In your first example, i is a module-scope variable, only in your second example do you introduce a new scope by wrapping the code in a new function actualFact.
The language reference specifies that func_closure is "None or a tuple of cells that contain bindings for the function’s free variables."
Now, note the difference between your two versions: in the first version i is a module-level (i.e. global) variable. The result of evaluating each of the functions is the same:
>>> [f(2) for f in flist]
[4, 4, 4]
In each function, i is not free, but refers to the global i, so no, the output should not be a list of non-zero-length tuples.
In practice, you probably don't care about the value of func_closure, unless you're doing some fairly deep magic. If you are doing something magic, note that given the specification, there seems to be no good reason why func_closure should not be an empty tuple if there are no free variables, so handle that case appropriately if you want your code to be portable between even different point-versions of python.
A cheap way to do this without a closure
for i in xrange(3):
def func(x, i=i): return x*i
flist.append(func)