A function that returns a drive in Python

I read Hackers and Artists and am confused by the issue mentioned by the author to illustrate the power of different programming languages.

The problem is this:

We want to write a function that generates batteries - a function that takes a number n, and returns a function that takes a different number i and returns n increased by i. (Thats increases, not plus. The battery should accumulate.)

The author mentions several solutions with different programming languages. For example, Common Lisp:

(defun foo (n) (lambda (i) (incf ni))) 

and JavaScript:

 function foo(n) { return function (i) { return n += i } } 

However, when it comes to Python, the following codes do not work:

 def foo(n): s = n def bar(i): s += i return s return bar f = foo(0) f(1) # UnboundLocalError: local variable 's' referenced before assignment 

A simple modification will make it work:

 def foo(n): s = [n] def bar(i): s[0] += i return s[0] return bar 

I am new to Python. Why does the first solution not work, and the second? The author mentions lexical variables, but I still do not understand.

+5
source share
4 answers

s += i is just sugar for s = s + i . *

This means that you are assigning a new value to the s variable (instead of mutating it in place). When you assign a variable, Python assumes that it is local to this function. However, before assigning it needs to evaluate s + i , but s is local and still not assigned β†’ Error.

In the second case, s[0] += i you never assign s directly, but only access the element from s . Thus, Python can clearly see that it is not a local variable and is looking for it in the outer scope.

Finally, a nicer alternative (in Python 3) is to explicitly say that s not a local variable:

 def foo(n): s = n def bar(i): nonlocal s s += i return s return bar 

(There is no need for s - you can just use n instead of bar ).)

* The situation is a little more complicated , but an important problem is that the calculations and assignment are performed in two separate steps.

+5
source

An infinite generator is one implementation. You can call __next__ on the generator instance to iteratively retrieve sequential results.

 def incrementer(n, i): while True: n += i yield n g = incrementer(2, 5) print(g.__next__()) # 7 print(g.__next__()) # 12 print(g.__next__()) # 17 

If you need a flexible increment, one of the possibilities is an object-oriented approach:

 class Inc(object): def __init__(self, n=0): self.n = n def incrementer(self, i): self.n += i return self.n g = Inc(2) g.incrementer(5) # 7 g.incrementer(3) # 10 g.incrementer(7) # 17 
+1
source

The following will work:

 def foo(n): s = n def bar(i): s_ = s + i return s_ return bar 

The inner function bar looks locally found within the scope of s , and if it does not find it, it will look up one level in the enclosing area where it finds s , which is a local variable from foo . But if you say s = s + 1 , you declare s as a new local variable in the bar ("the assignment operator creates variables in the local area"), which leads to an error because you do not have the value s assigned before adding that something to him (referring to him).

In another example, the expression s[0] = s[0] + 1 is different from the fact that you do not declare a new local variable inside the panel, you get access to the first element s , which is located in the covering area of ​​the panel.

0
source

In Python, if we use a variable and pass it to a function, then it will "Call by value" any changes you make to the variable that will not be reflected in the original variable.

But when you use a list instead of a variable, the changes you make to the list in the functions are reflected in the original List outside the function, so this is called a link call.

And this is the reason that the second option works, but the first option does not.

-1
source

Source: https://habr.com/ru/post/1275638/


All Articles