Take the following code
#module functions.py
def foo(input, new_val):
input = new_val
#module main.py
input = 5
functions.foo(input, 10)
print input
I thought input would now be 10. Why is this not the case?
Everything is passed by value, but that value is a reference to the original object. If you modify the object, the changes are visible for the caller, but you can't reassign names. Moreover, many objects are immutable (ints, floats, strings, tuples).
Inside foo, you're binding the local name input to a different object (10). In the calling context, the name input still refers to the 5 object.
Assignment in Python does not modify an object in-place. It rebinds a name so that after input = new_val, the local variable input gets a new value.
If you want to modify the "outside" input, you'll have to wrap it inside a mutable object such as a one-element list:
def foo(input, new_val):
input[0] = new_val
foo([input])
Python does not do pass-by-reference exactly the way C++ reference passing works. In this case at least, it's more as if every argument is a pointer in C/C++:
// effectively a no-op!
void foo(object *input, object *new_val)
{
input = new_val;
}
Python is neither call-by-value, or call-by-reference, it is Call By Object.
"Arguments are passed by call-by-sharing, similar to
call-by-value, except that the arguments are objects
and can be changed only if they are mutable."
Well, python functions are neither call-by-reference nor call-by-value, they are call-by-object.
before applying this concept on functions, let's take a look at those code snippets:
1-
listA = [0]
listB = listA
listB.append(1)
print (listA) # [0, 1]
2-
listA = [0]
listB = listA
listB = [2, 3]
print(listA) # [0]
In the first code snippet, listB was able to access the objects stored in listA and modify them BUT IT COULDN'T MODIFY listA in the second code snippet, as they are not the same thing! you passed a container of objects and were able to modify the objects in that container but, you can't modify that container itself.
in your code:
def foo(input, new_val):
input = new_val
input = 5
foo(input, 10)
print (input)
you can't modify the variable 'input' because you don't have it!
Passing by object is a very confusing concept and reminds me with Quantum Physics but, try playing around with the code and you will understand it well.
you can check the following links for more illustrations:
geeks for geeks
ROBERT HEATON blog
Related
def test(lister):
lister.append('why am i being shown in output?')
def pass_the_list():
x = []
test(x)
print(x)
pass_the_list()
Output
['why am i being shown in output?']
Process finished with exit code 0
how to pass string as a reference without return?
The list x is passed as a reference not value, therefore changes made in the function will preserve also when the function finishes.
You can pass a string as a refence, but it doesnt matter, because string, unlike list, is immutable. So any change you do to the string will cause creation of a new object.
test_string = 'Test my immutability'
test_string[0]='B'
>> TypeError: 'str' object does not support item assignment
You can modify string like this though (by putting it into something mutable like list)
test_string = 'Test my immutability'
test_list = [test_string]
def reference_test(test_list):
test_list[0]=test_list[0].replace('Test','Fest')
reference_test(test_list)
print(test_list)
>> ['Rest my immutability']
I know they practically do the same thing, but if you were to lets say do something like...
curpop = this_other_ndarray
i = 0;
while i<20:
curpop[:] = select(curpop, parameter, parameter1)
stuff
more stuff
curpop[:] = some_stuff_I_did
i += 1;
So the above code is just saying, before I enter a generational loop I am going to take an initial generation of populations from 'this other ndarray'.
Then I am planning on changing that array over and over and everytime I restart the loop I will only select some from itself but I will declare that as what it is equal to now. Is this okay to do in Python3?
Is the declaration of
'array[:] = some of it self'
versus
'array = some of itself'
different at all?
These are two totally different things.
The first is simple assignment.
foo = bar
This assignment statement merely says that the name on the left-hand side now refers to the same object as the name on the right-hand side. These statements do not modify either object.
Objects are neither created nor necessarily destroyed. If you lose the last name of an object, however, you will have lost the object. The CPython runtime uses reference counting as a memory management strategy, and will automatically reclaim objects that have a zero reference count.
In Python, variables act simply like object names that you can create, destroy, and change what they reference. Think of them like name-tags.
Now, a statement like:
foo[:] = bar
Is actually a method call. It can be translated to:
foo.__setitem__(slice(None, None, None), bar)
Observe:
>>> class Foo:
... def __setitem__(self, key, value):
... print("Key:", key, "Value:", value)
...
>>> class Bar: pass
...
>>> foo = Foo()
>>> bar = Bar()
>>> foo[:] = bar
Key: slice(None, None, None) Value: <__main__.Bar object at 0x104aa5c50>
So, really, the type of the objects control the ultimate effects of this statement. In the case of numpy.ndarray objects, slice-based assignment works similarly to list slice based assignment in that it mutates the array object in-place, with a few more caveats like broadcasting to throw into the mix. See the relevant docs:
https://docs.scipy.org/doc/numpy-1.13.0/user/basics.indexing.html#assigning-values-to-indexed-arrays
In many cases
curpop[:]= iterable_value_as_tuple_string_dictionary_and_list_etc
do the same thing as
curpop=iterable_value_as_tuple_string_dictionary_and_list_etc
of course assign a string first or at any step will remove the ability in the next steps to use [:] to assign something again
note that
curpop[:]= notiterable_value != curpop=notiterable_value
as the first assign notiterable_value to each element of curpop and the second assign the value notiterable_value to curpop itself
Say we have a function in a file, something like:
..... other fucntions
def add_to_list(l = []):
l.append("foo")
..... other functions
When will def be called? What exactly does def do?
[] creates a list and, for efficiency reasons, when a list is passed, it isn't duplicated or passed by value; it is sent by reference. A similar thing is happening with this. So, firstly all def's are initialized by Python. Then your code is run. However, the initializer has already created the list.
>>> id(add_to_list())
12516768
>>> id(add_to_list())
12516768
>>> id(add_to_list())
12516768
Excerpt from http://effbot.org/zone/default-values.htm
Notice how they have the same id? That's because it's returning the same instance of list every time. Thus, l = [] isn't creating a new instance at run time. So, we keep on appending to the same instance.
That is why we don't experience this behavior with ints, floats, and etc.
Consider your code:
def add_to_list(l = []):
l.append("foo")
print(l)
add_to_list()
add_to_list()
Then it's output:
['foo']
['foo', 'foo']
Now consider this similar function:
def add_to_list():
l = []
l.append("foo")
print(l)
add_to_list()
add_to_list()
The output will be:
['foo']
['foo']
That's because the l = [] is run after the constructors are initialized (in live time). When you consider the first code, you will see that the constructor is ran first, then the code executes live time.
def is executed whenever you hit it in parsing the source file. It defines the function. This means that the body of the function is assigned to the name, the parameters are included in the "signature" (calling format), etc. IN other words, the function is now ready to call from anywhere below that point, within the scope of the containing program.
Since l is a list, it's bound to whatever you pass into the function call. If you pass in a list, then it's bound by reference to that list, a mutable object.
As to your specific case, there's really no use case that will make sense: since you have to pass an object with an append method, anything you supply will have a value: it will never use the default value. The only way to get that default value into play is to call with empty parentheses, in which case there's no way to get the value back to the calling program (since you return nothing).
Thus, when you call the routine a second time, it appears that you're using the same list you passed the first time. That list already has the "foo" element you added that first time. Here's some test code and the output to illustrate the effect:
def add_to_list(l = []):
l.append("foo")
print "l =", l
empty = []
add_to_list(empty)
print "empty #1", empty
add_to_list(empty)
print "empty #2", empty
add_to_list()
Output:
l = ['foo']
empty #1 ['foo']
l = ['foo', 'foo']
empty #2 ['foo', 'foo']
l = ['foo']
Does that clarify things?
When I assign a value into an array the scope of the variable remain local (see loc()).
However if I access the element of an array the scope becomes global ( see glob())
import numpy as np
M = np.array([1])
def loc():
M = 2
return 0
def glob():
M[0] = 3
return 0
loc()
print M
>>> [1]
glob()
print M
>>> [3]
Why does this happen ? How can i locally modify the elements of an array without modifying the array globally? I need to have a loop inside my function changing one element at a time.
You're mixing several things here.
First of all, M = 2 creates a local variable named M (you can see it in locals()) and prevents you from accessing the original M later on (although you're not doing it... But just to make a point). That's sometimes referred to as "shadowing".
Second of all, the np.array is a mutable object (the opposite of an immutable object), and changes to it will reflect in any reference to it. What you have in your glob function is a reference to M.
You can look at an np.array as a piece of memory that has many names, and if you changed it, the changes will be evident no matter what name you're using to access it. M[0] is simply a reference to a specific part of this memory. This reflects the object's "state".
If you'd do something like:
M = np.array([1])
def example()
another_name_for_M = M
M = 2
another_name_for_M[0] = 2
you would still see the global M changing, but you're using a new name to access it.
If you would use a string, a tuple, a frozenset and the likes, which are all immutable objects that can not be (easily) changed, you wouldn't be able to actually change their state.
Now to your question, if you don't want the function to mutate the array just send a copy of it using np.copy, and not the actual one:
import numpy as np
my_array = np.array([1])
def array_mutating_function(some_array):
some_array[0] = 1337
print some_array # prints [1337]
# send copy to prevent mutating the original array
array_mutating_function(np.copy(my_array))
print my_array # prints [1]
This will effectively make it immutable on the outer scope, since the function will not have a reference to it unless it's using it's name on the outer scope, which is probably not a good idea regardless.
If the function should never change any array, move the copy to be made on inside the function no matter what array is sent, preventing it from changing any array that was sent to it:
def array_mutating_function(some_array):
some_array = np.copy(some_array)
some_array[0] = 1337
SImply explaining.:
cannot update a global varaible inside a funcion unless access it as global inside function.
But it can modify
Check:
import numpy as np
M = np.array([1])
def loc():
global M
M = 2
return 0
def glob():
M[0] = 3
return 0
loc()
print M
>>>2
From this link: How do I pass a variable by reference?, we know, Python will copy a string (an immutable type variable) when it is passed to a function as a parameter, but I think it will waste memory if the string is huge. In many cases, we need to use functions to wrap some operations for strings, so I want to know how to do it more effective?
Python does not make copies of objects (this includes strings) passed to functions:
>>> def foo(s):
... return id(s)
...
>>> x = 'blah'
>>> id(x) == foo(x)
True
If you need to "modify" a string in a function, return the new string and assign it back to the original name:
>>> def bar(s):
... return s + '!'
...
>>> x = 'blah'
>>> x = bar(x)
>>> x
'blah!'
Unfortunately, this can be very inefficient when making small changes to large strings because the large string gets copied. The pythonic way of dealing with this is to hold strings in an list and join them together once you have all the pieces.
Python does pass a string by reference. Notice that two strings with the same content are considered identical:
a = 'hello'
b = 'hello'
a is b # True
Since when b is assigned by a value, and the value already exists in memory, it uses the same reference of the string. Notice another fact, that if the string was dynamically created, meaning being created with string operations (i.e concatenation), the new variable will reference a new instance of the same string:
c = 'hello'
d = 'he'
d += 'llo'
c is d # False
That being said, creating a new string will allocate a new string in memory and returning a reference for the new string, but using a currently created string will reuse the same string instance. Therefore, passing a string as a function parameter will pass it by reference, or in other words, will pass the address in memory of the string.
And now to the point you were looking for- if you change the string inside the function, the string outside of the function will remain the same, and that stems from string immutability. Changing a string means allocating a new string in memory.
a = 'a'
b = a # b will hold a reference to string a
a += 'a'
a is b # False
Bottom line:
You cannot really change a string. The same as for maybe every other programming language (but don't quote me).
When you pass the string as an argument, you pass a reference. When you change it's value, you change the variable to point to another place in memory. But when you change a variable's reference, other variables that points to the same address will naturally keep the old value (reference) they held.
Wish the explanation was clear enough
In [7]: strs="abcd"
In [8]: id(strs)
Out[8]: 164698208
In [9]: def func(x):
print id(x)
x=x.lower() #perform some operation on string object, it returns a new object
print id(x)
...:
In [10]: func(strs)
164698208 # same as strs, i.e it actually passes the same object
164679776 # new object is returned if we perform an operation
# That's why they are called immutable
But operations on strings always return a new string object.
def modify_string( t ):
the_string = t[0]
# do stuff
modify_string( ["my very long string"] )
If you want to potentially change the value of something passed in, wrap it in a dict or a list:
This doesn't change s
def x(s):
s += 1
This does change s:
def x(s):
s[0] += 1
This is the only way to "pass by reference".
wrapping the string into a class will make it pass by reference:
class refstr:
"wrap string in object, so it is passed by reference rather than by value"
def __init__(self,s=""):
self.s=s
def __add__(self,s):
self.s+=s
return self
def __str__(self):
return self.s
def fn(s):
s+=" world"
s=refstr("hello")
fn(s) # s gets modified because objects are passed by reference
print(s) #returns 'hello world'
Just pass it in as you would any other parameter. The contents won't get copied, only the reference will.