why there are differences between those two codes? [duplicate] - python

This question already has answers here:
Why does += behave unexpectedly on lists?
(9 answers)
Closed 6 years ago.
i wrote 2 simple codes in python that should make the same work, but it doesnt.
if you can tell me why is this, and also explain me more about the "everything is an object in python". thanks :)
def f(p=[]):
print p
f.a += 1
print id(p)
if(f.a == 1):
p = p + [4]
else:
p = p + [5]
return p
f.a = 0
f()
f()
f()
answer:
[]
40564496
[]
40564496
[]
40564496
def f(p=[]):
print p
f.a += 1
print id(p)
if(f.a == 1):
p += [4]
else:
p +=[5]
return p
#print f()
#print f()
f.a = 0
f()
f()
f()
answer:
[]
40892176
[4]
40892176
[4, 5]
40892176
like you see - the first code every time does a new list, and the second add to the last used one...

You should never* pass a mutable object as a default value in python function as there will be just one. Consequently your second function always uses the same list, that was instationed during function creation. First one, on the other hand creates new lists during p = p + [4] assignments, thus there is no mutation of the default arg. Code this this way:
def f(p=None):
if p is None:
p = []
print p
f.a += 1
print id(p)
if(f.a == 1):
p += [4]
else:
p +=[5]
return p
* by never I mean "unless you really know what you are doing". There is always an exception from the rule, but if you need to pass a mutable as a default value, you need to understand its consequences very well, thus you would probably not be reading this answer anyway. In particular, it is discouraged in many Python style guides, such as Google python style guide
Do not use mutable objects as default values in the function or method definition.
This should also cause pylint warning W0102
dangerous-default-value (W0102):
Dangerous default value %s as argument Used when a mutable value as list or dictionary is detected in a default value for an argument.

The operators obj += other and obj = obj + other are not the same. The previous often uses inplace modification, whereas the later usually creates a new object. In the case of lists, obj += other is the same as obj.extend(other).
The second important thing is that def statements are only evaluated once per scope. If you define a function at module scope, def is evaluated once - that includes the creation of its default parameters!
def foo(bar=[]):
print(id(bar))
In this case, bar will always default to the same list object that has been created when def foo was first evaluated.
Whenever you modify the default value, it will carry over to the next time this default is used.
Contrast this with:
def foo(bar=None):
bar = bar if bar is not None else []
print(id(bar))
In this case, the default list is recreated every time the function is called.

Related

Why can't I change the default value of a function after it was defined?

i = 5
def f(arg=i):
print(arg)
i = 6
f()
I am learning Python from the official documentation. There I find the above piece of code which I am unable to understand as to why 5 is printed instead of 6. I am relatively new to Python. Can somebody help me understand the concept?
def f(arg=i) says "make me a function f where the default value for arg is whatever i is right now". At the time of defining the function, i=5.
i = 5
def f(arg=i)
print(arg)
The i is evaluated at the time of definition, so the code above has the same meaning as the code below:
def f(arg=5)
print(arg)
This means that, when the function is called without arguments, arg will have the value 5, no matter what the value of i is now.
In order to get what you want, just do the following:
def f(arg)
print(arg)
i = 6
f(i)
Because the function takes its default value on the first declaration of 'i'.
Change to i=6 on the first line if you want you code to print 6.
Hope I helped !
This is the difference between something being handled by reference vs by value. When you defined the function f you told it to set the argument's default value to i this is done by value, not by reference, so it took whatever the value of i was at that time and set the default for the function to that. Changing the value of i after that point does not change the value of arg. If you want it to work that way you could do this:
i = 5
def f(arg = None):
if (arg = None)
arg = i
print(arg)
i = 6
f()
This lets you pass a value for arg into the function as normal, but if you don't (or you explicitly pass None) it updates arg to the current value of i if arg is still None (Python's version of NULL if you're familiar with other languages)
Something similar can be done using the or operator, arg = arg or i,but that will check if arg is falsy, and when using integers like you are in your example, 0 will be caught by the check.
What others have said is true...the default is evaluated at the time of function creation, but it is not that it takes the "value of i" at the time of creation. The default is assigned the object referred to by "i" at the time of creation. This is an important point, because if that object is mutable, the default can be changed!
Here's what happens:
import inspect
i = 5 # name "i" refers to an immutable Python integer object of value 5.
print(f'i = {i} (id={id(i)})') # Note value and ID
# Create function "f" with a parameter whose default is the object
# referred to by name "i" *at this point*.
def f(arg=i):
print(f'arg = {arg} (id={id(arg)})')
# Use the inspect module to extract the defaults from the function.
# Note the value and ID
defaults = dict(inspect.getmembers(f))['__defaults__']
print(f'defaults = {defaults} (id={id(defaults[0])})')
# name "i" now refers to a different immutable Python integer object of value 6.
i = 6
print(f'i = {i} (id={id(i)})') # Note value and ID (changed!)
f() # default for function still referes to object 5.
f(i) # override the default with object currently referred to by name "i"
Output:
i = 5 (id=2731452426672) # Original object
defaults = (5,) (id=2731452426672) # default refers to same object
i = 6 (id=2731452426704) # Different object
arg = 5 (id=2731452426672) # f() default is the original object
arg = 6 (id=2731452426704) # f(i) parameter is different object
Now see the results of a mutable default:
import inspect
i = [5] # name "i" refers to an mutable Python list containing immutable integer object 5
print(f'i = {i} (id={id(i)})') # Note value and ID
# Create function "f" with a parameter whose default is the object
# referred to by name "i" *at this point*.
def f(arg=i):
print(f'arg = {arg} (id={id(arg)})')
# Use the inspect module to extract the defaults from the function.
# Note the value and ID
defaults = dict(inspect.getmembers(f))['__defaults__']
print(f'defaults = {defaults} (id={id(defaults[0])})')
# name "i" now refers to a different immutable Python integer object of value 6.
i[0] = 6 # MUTATE the content of the object "i" refers to.
print(f'i = {i} (id={id(i)})') # Note value and ID (UNCHANGED!)
f() # default for function still refers to original list object, but content changed!
i = [7] # Create a different list object
print(f'i = {i} (id={id(i)})') # Note value and ID (changed)
f(i) # override the default currently refered to by name "i"
Output:
i = [5] (id=2206901216704) # Original object
defaults = ([5],) (id=2206901216704) # default refers to original object
i = [6] (id=2206901216704) # Still original object, but content changed!
arg = [6] (id=2206901216704) # f() default refers to orginal object, but content changed!
i = [7] (id=2206901199296) # Create a new list object
arg = [7] (id=2206901199296) # f(i) parameter refers to new passed object.
This can have strange side effects if not understood well:
>>> def f(a,b=[]): # mutable default
... b.append(a)
... return b
...
>>> x = f(1)
>>> x
[1]
>>> y = f(2) # some would think this would return [2]
>>> y
[1, 2]
>>> x # x changed from [1] to [1,2] as well!
[1, 2]
Above, b refers to the original default list object. Appending to it mutates the default list. Returning it makes x refer to the same object. The default list now contains [1] so appending in the 2nd call make it [1,2]. y refers to the same default object as x so both names refer see the same object content.
To fix, make the default immutable and create a new list when the default is seen:
>>> def f(a,b=None):
... if b is None:
... b = []
... b.append(a)
... return b
...
>>> f(1)
[1]
>>> f(2)
[2]
This is because you are assigning the value when the function is created. arg at the time of creation will be defaulted to what i is in that moment. Since at the time of the function being created the value of i is 5 then that's what the default value of that argument becomes. After the initial creation of the function i in the function argument is no longer linked to i in the body.

How to assign value to function parameter in python

I'd like to initialise a variable inside a function (so that the final print statement in my example outputs 10):
def init_param(param):
param = 10
n = 1
init_param(n)
print n # prints 1
Is this doable in python?
Arguments are assigned inside the function as it's local variables. So all principles apply here.
Immutable objects cannot be changed.
Mutable objects can be modified in place.
you're indenting to modify an immutable object, which is not possible. So your only options are :-
def init_param(param):
param = 10
return param
n = 1
n = init_param(n)
print n
which is pretty much useless OR
def init_param(param):
param[0] = 10
n = [1]
init_param(n)
print n
First of all python function passes the value by object and the reference name here param is just a reference to a value hold by n.
Now coming to the solution, yes it could be possible provided you pass the variable name
def init_param(var_name):
globals()[var_name] = 10
n = 1
init_param('n')
print n
Hope it will answer!
Short answer: no, you can't.
Longer answer: in
def init_param(param):
param = 10
the name param is local to the init_param function. Rebinding this name will change the value bound to the name param in the function's local scope, but will have absolutely no effect on the name n in the caller's scope - those names live in totally distinct namespaces. You can read Ned Batcheler's reference article on Python's names and binding for more in-depth explanations.
What would work would be to use a mutable container - a dict for example - and mutate this container, ie:
def init_param(params, name, value):
params[name] = value
params = {
"n": 1,
"answer": 42,
"parrot": "dead"
}
init_params(params, "n", 10)
print(params)
(if you don't understand why this one works, re-read Ned Batcheler's article linked above)
def init_param(param):
param = 10
n = 1
init_param(n)
print n
here n is a integer (immutable data type) so it will be passed by value and so value of n will be unchanged.
lets take a mutable data type (ex. list) then it will be passed by referenced and so values in list will be changed.
def init_param(a):
a[0] = 10
arr = [1]
init_param(arr)
print(arr[0]) # print 10
so you have to check first whether the data is mutable or immutable.
otherwise you can use global keyword to access global variables.
def f():
global n
n = 10
n = 1
f()
print(n) # print 10

How to implement a static counter in python

I am calling a method and I need a static counter within this method. It's required to parse the elements of the list. The counter will tell which position of the list to lookup at.
For e.g
static_var_with_position = 0
noib_list = [3, 2, 2, 2, 2, 1, 2, 2]
def foo(orig_output, NOB):
# tried two ways
#static_var_with_position += 1 # doesn't work
#global static_var_with_position
#static_var_with_position += 1 # doesn't work either
bit_required = noib_list[static_var_with_position]
converted_output = convert_output(orig_output, NOB, bit_required)
The static_var_with_position value is never incremented. I have commented the two ways I tried to increment value.
In c++ its piece of cake, but I couldn't find anything similar in python so far. Any help will be appreciated :)
Thanks!
Instead of using a global/static counter variable, you could use an iterator:
iterator = iter(noib_list)
def foo(orig_output, NOB):
bit_required = next(iterator)
converted_output = convert_output(orig_output, NOB, bit_required)
The iterator will automatically keep track which is the next element internally.
When the iterator is exhausted (i.e. when you reached the end of the list), next will raise a StopIteration error, so it you do not know when the end is reached, you can use bit_required = next(iterator, None) instead; then just test whether the value is None and you know that the list is exhausted (or just use try/except).
Following this example, you could do the same with your counter :
def inc() :
global global_cpt
global_cpt +=1
print global_cpt
if __name__ == '__main__' :
global_cpt = 0
inc()
inc()
inc()
will print
> 1
> 2
> 3
I don't actually advocate doing this in your case, but it's a little-known hack for creating a "static" variable within a function: put it as a parameter with a mutable default value! You can modify it within the function and it will hold until the next function call, as long as the caller doesn't pass a value for it.
def foo(value=[0]):
value[0] += 1
print(value[0])
>>> foo()
1
>>> foo()
2
>>> foo()
3
>>> foo([906])
907
>>> foo()
4

How Does Calling Work In Python? [duplicate]

This question already has answers here:
Does Python make a copy of objects on assignment?
(5 answers)
How do I pass a variable by reference?
(39 answers)
Why can a function modify some arguments as perceived by the caller, but not others?
(13 answers)
Closed last month.
For a project I'm working on, I'm implementing a linked-list data-structure, which is based on the idea of a pair, which I define as:
class Pair:
def __init__(self, name, prefs, score):
self.name = name
self.score = score
self.preferences = prefs
self.next_pair = 0
self.prev_pair = 0
where self.next_pair and self.prev_pair are pointers to the previous and next links, respectively.
To set up the linked-list, I have an install function that looks like this.
def install(i, pair):
flag = 0
try:
old_pair = pair_array[i]
while old_pair.next_pair != 0:
if old_pair == pair:
#if pair in remainders: remainders.remove(pair)
return 0
if old_pair.score < pair.score:
flag = 1
if old_pair.prev_pair == 0: # we are at the beginning
old_pair.prev_pair = pair
pair.next_pair = old_pair
pair_array[i] = pair
break
else: # we are not at the beginning
pair.prev_pair = old_pair.prev_pair
pair.next_pair = old_pair
old_pair.prev_pair = pair
pair.prev_pair.next_pair = pair
break
else:
old_pair = old_pair.next_pair
if flag==0:
if old_pair == pair:
#if pair in remainders: remainders.remove(pair)
return 0
if old_pair.score < pair.score:
if old_pair.prev_pair==0:
old_pair.prev_pair = pair
pair.next_pair = old_pair
pair_array[i] = pair
else:
pair.prev_pair = old_pair.prev_pair
pair.next_pair = old_pair
old_pair.prev_pair = pair
pair.prev_pair.next_pair = pair
else:
old_pair.next_pair = pair
pair.prev_pair = old_pair
except KeyError:
pair_array[i] = pair
pair.prev_pair = 0
pair.next_pair = 0
Over the course of the program, I am building up a dictionary of these linked-lists, and taking links off of some and adding them in others. Between being pruned and re-installed, the links are stored in an intermediate array.
Over the course of debugging this program, I have come to realize that my understanding of the way Python passes arguments to functions is flawed. Consider this test case I wrote:
def test_install():
p = Pair(20000, [3, 1, 2, 50], 45)
print p.next_pair
print p.prev_pair
parse_and_get(g)
first_run()
rat = len(juggler_array)/len(circuit_array)
pref_size = get_pref_size()
print pref_size
print install(3, p)
print p.next_pair.name
print p.prev_pair
When I run this test, I get the following result.
0
0
10
None
10108
0
What I don't understand is why the second call to p.next_pair produces a different result (10108) than the first call (0). install does not return a Pair object that can overwrite the one passed in (it returns None), and it's not as though I'm passing install a pointer.
My understanding of call-by-value is that the interpreter copies the values passed into a function, leaving the caller's variables unchanged. For example, if I say
def foo(x):
x = x+1
return x
baz = 2
y = foo(baz)
print y
print baz
Then 3 and 2 should be printed, respectively. And indeed, when I test that out in the Python interpreter, that's what happens.
I'd really appreciate it if anyone can point me in the right direction here.
In Python, everything is an object. Simple assignment stores a reference to the assigned object in the assigned-to name. As a result, it is more straightforward to think of Python variables as names that are assigned to objects, rather than objects that are stored in named locations.
For example:
baz = 2
... stores in baz a pointer, or reference, to the integer object 2 which is stored elsewhere. (Since the type int is immutable, Python actually has a pool of small integers and reuses the same 2 object everywhere, but this is an implementation detail that need not concern us much.)
When you call foo(baz), foo()'s local variable x also points to the integer object 2 at first. That is, the foo()-local name x and the global name baz are names for the same object, 2. Then x = x + 1 is executed. This changes x to point to a different object: 3.
It is important to understand: x is not a box that holds 2, and 2 is then incremented to 3. No, x initially points to 2 and that pointer is then changed to point to 3. Naturally, since we did not change what object baz points to, it still points to 2.
Another way to explain it is that in Python, all argument passing is by value, but all values are references to objects.
A counter-intuitive result of this is that if an object is mutable, it can be modified through any reference and all references will "see" the change. For example, consider this:
baz = [1, 2, 3]
def foo(x):
x[0] = x[0] + 1
foo(baz)
print baz
>>> [2, 2, 3]
This seems very different from our first example. But in reality, the argument is passed the same way. foo() receives a pointer to baz under the name x and then performs an operation on it that changes it (in this case, the first element of the list is pointed to a different int object). The difference is that the name x is never pointed to a new object; it is x[0] that is modified to point to a different object. x itself still points to the same object as baz. (In fact, under the hood the assignment to x[0] becomes a method call: x.__setitem__().) Therefore baz "sees" the modification to the list. How could it not?
You don't see this behavior with integers and strings because you can't change integers or strings; they are immutable types, and when you modify them (e.g. x = x + 1) you are not actually modifying them but binding your variable name to a completely different object. If you change baz to a tuple, e.g. baz = (1, 2, 3), you will find that foo() gives you an error because you can`t assign to elements of a tuple; tuples are another immutable type. "Changing" a tuple requires creating a new one, and assignment then points the variable to the new object.
Objects of classes you define are mutable and so your Pair instance can be modified by any function it is passed into -- that is, attributes may be added, deleted, or reassigned to other objects. None of these things will re-bind any of the names pointing to your object, so all the names that currently point to it will "see" the changes.
Python does not copy anything when passing variables to a function. It is neither call-by-value nor call-by-reference, but of those two it is more similar to call-by-reference. You could think of it as "call-by-value, but the value is a reference".
If you pass a mutable object to a function, then modifying that object inside the function will affect the object everywhere it appears. (If you pass an immutable object to a function, like a string or an integer, then by definition you can't modify the object at all.)
The reason this isn't technically pass-by-reference is that you can rebind a name so that the name refers to something else entirely. (For names of immutable objects, this is the only thing you can do to them.) Rebinding a name that exists only inside a function doesn't affect any names that might exist outside the function.
In your first example with the Pair objects, you are modifying an object, so you see the effects outside of the function.
In your second example, you are not modifying any objects, you are just rebinding names to other objects (other integers in this case). baz is a name that points to an integer object (in Python, everything is an object, even integers) with a value of 2. When you pass baz to foo(x), the name x is created locally inside the foo function on the stack, and x is set to the pointer that was passed into the function -- the same pointer as baz. But x and baz are not the same thing, they only contain pointers to the same object. On the x = x+1 line, x is rebound to point to an integer object with a value of 3, and that pointer is what is returned from the function and used to bind the integer object to y.
If you rewrote your first example to explicitly create a new Pair object inside your function based on the information from the Pair object passed into it (whether this is a copy you then modify, or if you make a constructor that modifies the data on construction) then your function would not have the side-effect of modifying the object that was passed in.
Edit: By the way, in Python you shouldn't use 0 as a placeholder to mean "I don't have a value" -- use None. And likewise you shouldn't use 0 to mean False, like you seem to be doing in flag. But all of 0, None and False evaluate to False in boolean expressions, so no matter which of those you use, you can say things like if not flag instead of if flag == 0.
I suggest that you forget about implementing a linked list, and simply use an instance of a Python list. If you need something other than the default Python list, maybe you can use something from a Python module such as collections.
A Python loop to follow the links in a linked list will run at Python interpreter speed, which is to say, slowly. If you simply use the built-in list class, your list operations will happen in Python's C code, and you will gain speed.
If you need something like a list but with fast insertion and fast deletion, can you make a dict work? If there is some sort of ID value (string or integer or whatever) that can be used to impose an ordering on your values, you could just use that as a key value and gain lightning fast insert and delete of values. Then if you need to extract values in order, you can use the dict.keys() method function to get a list of key values and use that.
But if you really need linked lists, I suggest you find code written and debugged by someone else, and adapt it to your needs. Google search for "python linked list recipe" or "python linked list module".
I'm going to throw in a slightly complicating factor:
>>> def foo(x):
... x *= 2
... return x
...
Define a slightly different function using a method I know is supported for numbers, lists, and strings.
First, call it with strings:
>>> baz = "hello"
>>> y = foo(baz)
>>> y
'hellohello'
>>> baz
'hello'
Next, call it with lists:
>>> baz=[1,2,2]
>>> y = foo(baz)
>>> y
[1, 2, 2, 1, 2, 2]
>>> baz
[1, 2, 2, 1, 2, 2]
>>>
With strings, the argument isn't modified. With lists, the argument is modified.
If it were me, I'd avoid modifying arguments within methods.

Hole-in-scope, dead code or why such output?

Code
def change1(list1):
list1[1] = list1[1] + 5
def change2(number):
number = number + 2
def main():
numbers = [4, 8, 12]
change1(numbers)
variable = 15
change2(variable)
i = 0
while i < 3:
print numbers[i]
i += 1
print variable
main()
When I read it, I thought it will output 4 8 12 15 but it outputs 4 13 12 15. I can see here that Python deals with integer and lists differently, I assumed that the last thing is impossible without global. I cannot understand the output, in such case, why would it not output 4 13 12 17?
You can see here almost identical code with different types and different reference:
$ python test2.py
4
13
12
15
$ python test3.py
4
13
12
17
$ cat test2.py test3.py
Pass-by-reference examples
test2.py: pass-by-reference and mutable data type -example. Table/list is not enough to affect the local variable in main, you need the Reference!
def change1(list1):
list1[1] = list1[1] + 5
def change2(number):
number = [x+2 for x in number]
def main():
numbers = [4, 8, 12]
change1(numbers)
variable = [15]
change2(variable)
i = 0
while i < 3:
print numbers[i]
i += 1
print variable[0]
main()
test3.py: pass-by-reference example, changing a mutable data type list/table outside the main function
def change1(list1):
list1[1] = list1[1] + 5
def change2(number):
number[0] += 2
def main():
numbers = [4, 8, 12]
change1(numbers)
variable = [15]
change2(variable)
i = 0
while i < 3:
print numbers[i]
i += 1
print variable[0]
main()
pass-by-value examples
test4.py: trying to find an example with pass-by-value, why it does not work?
$ cat test4.py
# Not yet a pass-by-value example!
global variable
variable = [15]
def change1(list1):
list1[1] = list1[1] + 5
def change2(number):
number = [x+2 for x in number]
def main():
numbers = [4, 8, 12]
change1(numbers)
#variable = 15
change2(variable)
i = 0
while i < 3:
print numbers[i]
i += 1
print variable[0]
main()
$ python test4.py
4
13
12
15 # I expected 17! Why no 17?
def change1(list1):
# `list1[1] =` means you are changing the object passed in
list1[1] = list1[1] + 5
def change2(number):
# `number = ` means you create a **new local variable**, number,
# based on the `number`you passed in
number = [x+2 for x in number]
So if you want to change existing objects, you have to referene them in some way, for example in
def change3(number):
# `number[:]` is the whole existing list and you overwrite it
number[:] = [x+2 for x in number]
Note the [ .. ] when changing a list.
Python parameters are passed by reference. You mutating only one object in change1.
However, numerical values and Strings are all immutable. You cannot change the value of a passed in immutable and see that value change in the caller. Dictionaries and Lists on the other hand are mutable, and changes made to them by a called function will be preserved when the function returns.
More: http://www.penzilla.net/tutorials/python/functions/
The definitive answer is that Python is actually "call by sharing", also known as "call by object" or "call by object reference".
This has been extensively discussed before. From that article:
From time to time, people who’ve read a little CS but not a lot CS (or too much of just one kind of CS) pop up on comp.lang.python and waste a lot of energy trying to tell everyone that Python’s using some calling model that it doesn’t really use. It always turns out that they don’t really understand Python’s model, and quite often, they don’t understand their favourite model either.
But nevermind, the only thing you need to know is that Python’s model is neither “call by value” nor “call by reference” (because any attempt to use those terms for Python requires you to use non-standard definitions of the words “-value” and “-reference”). The most accurate description is CLU’s “call by object” or “call by sharing“. Or, if you prefer, “call by object reference“.
You should also read this, if you haven’t done so already.
Python's semantics are most similar to the semantics of the language CLU. The CLU Reference Manual by Liskov et al describes the semantics like this:
"We call the argument passing technique call by sharing,
because the argument objects are shared between the
caller and the called routine. This technique does not
correspond to most traditional argument passing techniques
(it is similar to argument passing in LISP). In particular it
is not call by value because mutations of arguments per-
formed by the called routine will be visible to the caller.
And it is not call by reference because access is not given
to the variables of the caller, but merely to certain objects."
In change1 you exchange the value in the list with value + 5.
In change2 you add 5 to number. The result is a new object and is not just applied to the passed variable.
If you come from C++: No there is no int& var in Python.
You get the expected result when doing this:
def change2(number):
return number + 5
variable = 15
variable = change2(variable)
If you still don't want to return a value, you could create a MutableInt class.
class MutableInt(object):
def __init__(self, value = 0):
self._value = int(value)
def __add__(self, other):
self._value += int(other)
return self
def __sub__(self, other):
self._value -= int(other)
return self
...
All the examples show call-by-value. Python only has call-by-value. There is no call-by-reference. All values in python are references (it is not possible to have an "object" as the value). Hence it is references that are copied when passed to the function. Lists are mutable, so it is possible to mutate its contents through a shared reference. In change2 you are reassigning a local variable to point to another object, which, like all assignments to local variables, has no effect on any calling scope, since it is call-by-value.

Categories

Resources