Using Arguments in Python Functions

I know about mutuable vs immutable arguments in Python, and which is, but here is a strange problem that I have encountered with mutable arguments. A simplified version is as follows:

def fun1a(tmp): tmp.append(3) tmp.append(2) tmp.append(1) return True def fun1(a): b = fun1a(a) print a #prints [3,2,1] return b def fun2a(): tmp = [] tmp.append(3) tmp.append(2) tmp.append(1) return [True, tmp] def fun2(a): [b, a] = fun2a() print a #prints [3,2,1] return b def main(): a=[] if fun1(a): print a #prints [3,2,1] if fun2(b): print b #prints garbage, eg (0,1) 

As you can see, the only difference is that fun2 indicates the link passed in the argument to the list created inside fun2a, and fun1 is simply added to the list created in main. As a result, fun1 returns the correct result, and fun2 returns random garbage, not the result that I would expect. What is the problem?

thanks

+4
source share
3 answers

This is not so much a variable / immutable problem as one of the areas.

"b" exists only in bodies fun1 and fun2. It is absent on a major or global scale (at least intentionally)

- EDIT -

 >>> def fun1(b): ... b = b + 1 ... return b ... >>> def fun2(a): ... b = 1 ... return b ... >>> fun1(5) 6 >>> fun2(b) Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'b' is not defined 

(From my interpreter in the terminal)

I assume that your 'b' was initialized somewhere else. What happened in another function does not affect this.

This is me using your exact code:

 >>> main() [3, 2, 1] [3, 2, 1] Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 5, in main NameError: global name 'b' is not defined >>> b = 'whatever' >>> main() [3, 2, 1] [3, 2, 1] [3, 2, 1] whatever 
+3
source

As others have pointed out, there is no name < b in your main() function.

The best way to say how your code works is unit test. Unit testing is very easy in Python and a great habit. When I first started writing Python a few years ago, the guy I was paired with insisted on testing everything. From that day on, I continued and never had to use the Python debugger as a result! I'm distracted ...

Consider:

 import unittest class Test(unittest.TestCase): def test_fun1a_populates_tmp(self): some_list = [] fun1a(tmp=some_list) self.assertEquals([3, 2, 1], some_list) def test_fun1a_returns_true(self): some_list = [] ret = fun1a(tmp=some_list) self.assertTrue(ret) def test_fun1_populates_a(self): some_list = [] fun1(a=some_list) self.assertEquals([3, 2, 1], some_list) def test_fun1_returns_true(self): some_list = [] ret = fun1(a=some_list) self.assertTrue(ret) def test_fun2a_populates_returned_list(self): ret = fun2a() self.assertEquals([True, [3, 2, 1]], ret) def test_fun2_returns_true(self): some_list = [] ret = fun2(some_list) self.assertTrue(ret) def test_fun2_des_not_populate_passed_list(self): some_list = [] fun2(some_list) self.assertEqual(0, len(some_list)) if __name__ == '__main__': unittest.main() 

Each of these unit tests passes and documents how your functions behave (except for printing, you can add tests for those if necessary). They also provide a binding for editing code, because they must keep going or fail if you break something.

I have not tested unit main() since it is clearly broken.

+1
source

The problem may be related to the difference between lists and tuples. In fun2, do not put brackets around a, b. In fun2a, return a tuple of two objects, not a list. Python should write variables correctly if this is the problem you are trying to solve. Also, you called fun2 with argument b when b was never defined. Of course, the fun2 parameter is never used because it is overwritten before it is read.

In the end, your code should look like this:

 def fun1a(tmp): tmp.append(3) tmp.append(2) tmp.append(1) return True def fun1(a): b = fun1a(a) print a #prints [3,2,1] return b def fun2a(): tmp = [] tmp.append(3) tmp.append(2) tmp.append(1) return (True, tmp) def fun2(): b, a = fun2a() print a #prints [3,2,1] return b def main(): a=[] if fun1(a): print a #prints [3,2,1] if fun2(): print b #prints garbage, eg (0,1) 

which should print [3,2,1] both times.

-1
source

All Articles