What is the difference between
def delete_head1(t):
#t[:] = t[1:]
t = t[1:]
and
def delete_head2(t):
t[:] = t[1:]
#t = t[1:]
Why does the second one modify the input but not the first? That is, if I do
k=[1,2,3]
delete_head1(k)
print(k)
versus
k=[1,2,3]
delete_head2(k)
print(k)
The results are different.
As I understand, the first one, in its body, creates and refers to a local variable t. Why isn't the second one also referring to a local variable t?
If in the body of the function I have:
t = t[1:]
the interpreter recognizes the second t (the one after the equals sign) but not the first t (the one before the equals sign)?
But this is not the case if I write
t[:] = t[1:]
In this last statement it recognizes both t? What is the guiding principle here for Python?
Assignment statements behave differently depending on what the target it. You are correct that assignment to a name in a function create a local variable, but only one of your examples actually assigns to a name.
When the target is a simple name,
t = ...
this simply makes t refer to whatever object ... produces, and leaves the old value of t with one less reference.
When the target is a slice,
t[:] = ...
this is really syntactic sugar for
t = t.__setitem__(slice(None, None, None), ...)
t might refer to a new object afterwards, depending on what t.__setitem__ returns. But usually, this method returns the same object, and as a side effect alters the object in some way.
For lists, t[:] = some_list is basically equivalent to
t.clear()
t.extend(some_list)
t refers to the same list before and after, but the contents of that list change.
The full details of the syntax are in section 7.2, Assignment Statements of the language documentation. The semantics of list.__setitem__ in particular aren't spelled out in great detail, but can be inferred from the description of the syntax and the description of s[i:j:k] = t.
Related
Hope you're all well with the caotic world we're living...
This might be a very beginner level question, but I'd like to understand why It is like that.
Let's say I have a list of complex:
myList = [(1.231 +2.254j), (2.875 +23.543j), ...]
I've been trying to round the values with this function:
def round_complex(x, digits):
return complex(round(x.real, digits), round(x.imag, digits))
And for doing so, I've tried this:
for item in myList:
item = round_complex(item, 2)
Expecting that myList values get changed, for example:
myList = [(1.23 +2.25j), (2.88 +23.54j), ...]
But, It does not work.
I've also tried with a more simple example, like a list of floats and the base round function from python. It also does not work.
Is there a way for me to change a value of an iterable object with this kind of for loop (for-in)?
Or do I really have to do this:
for i in range(len(myList)):
myList[i] = round_complex(myList[i], 2)
The simple answer is: NO.
Python uses a mechanism, which is known as "Call-by-Object", sometimes also called "Call by Object Reference" or "Call by Sharing" when pass function parameters.
If you pass immutable arguments like integers, strings or tuples to a function, the passing acts like call-by-value. The object reference is passed to the function parameters. They can't be changed within the function, because they can't be changed at all, i.e. they are immutable. It's different, if we pass mutable arguments. They are also passed by object reference, but they can be changed in place within the function.
So, after your iterate the list, the value (1.231 +2.254j) would be a immutable argument which your change won't affect the outside variable. But if you pass the value like [1.231 +2.254j] to function, then it will make effect like next:
test.py:
myList2 = [[(1.231 +2.254j)], [(2.875 +23.543j)]]
print(myList2)
def round_complex(x, digits):
return complex(round(x.real, digits), round(x.imag, digits))
for item2 in myList2:
item2[0] = round_complex(item2[0], 2)
print(myList2)
Execution:
$ python3 test.py
[[(1.231+2.254j)], [(2.875+23.543j)]]
[[(1.23+2.25j)], [(2.88+23.54j)]]
In a word, for you scenario, if you insist organize your input data as that & iterate with that way, you can't change the outside value directly inside the function.
You may refers to this to learn more.
The thing I understand by reading for question that you want to Assign the values of i in myList. For doing so you can use append i.e
for i in mylist:
mylist.append(round_complex(i, 2))
This question already has answers here:
Modifying a list inside a function
(4 answers)
Closed 2 years ago.
I am currently new to python and I'm still learning the basics, but there is one thing I just can't wrap my head around. Why is the code in Q.1 giving the out-print 3, while Q.2 is giving the out-print [4]?
When asked, I was told that the f(x)-line at the bottom of Q.1 isn't given any variable or box to hold the new return-value given from the def. and that's the reason why the out-print of x remain 3.
This made sense to me, but then why would the out-print in Q.2 equal the return-value of g(x)? There isn't any variable or box to contain the return-value of x in this code either..
Q.1:
def f(x):
x += 1
return x
x=3
f(x)
print(x)
Q.2:
def g(x):
x[0] = x[0] + 1
return x
x = [3]
g(x)
print(x)
A Python function takes arguments by reference (something that points to the real object) whenever that argument is "complex", and takes the argument by value (a copy of the item) when it's a simple thing.
Q.1 is taking an int as an argument, then the function creates an internal copy of it and thus does not modify the value of x outside the function.
Q.2 is taking a list as an argument, which is considered complex, in which case the function takes a reference to the original and whatever happens to that reference will also happen to the original.
You can find an explanation of pass-by-reference and pass-by-value with images here
In Python, lists are mutable objects, and as a result they are passed-by-reference when used in a function call. This means that when g(x) adds 1 to the first element of x, after the function exits, the original list x will contain those changes.
Note that the specific term used is not "pass-by-reference", but rather "pass-by-name", which comes with a couple different semantics, which you can learn more about if you wish.
The function defined is Q1 is returning a value of for. x contains 3 and is passed into the function, by calling it with f(x). It gets incremented and returned to the function call. But, the function call was not stored in a variable, so it was not saved into memory. Nothing was done with the returned value. Calling the function is only editing the variable x within the local scope (within the function). When you're using print(x) it is referencing the global variable of x, which still contains 3.
In Q2, lists are mutable. Editing them within a function, the changes persist in the global scope. Because that list is mutated in the global scope, using print(x) uses the updated global variable.
I hope this makes sense. Look into scope of variables in the documentation for more.
Q1
def f(x):
x += 1
return x
x=3
f(x)
print(x)
The reason this is returning 3 and not 4 is because you haven't rebound your variable to reference this new value. Instead of f(x) you can do x = f(x).
Q2
def g(var):
var[0] = var[0] + 1
return var
x = [3]
g(x)
print(x)
To answer this without making it confusing I've changed the local variable used in the function to var so you can see what I'm trying to explain to you easier.
First you are creating a list with a integer value 3 in the first spot in the list (element 0) and you make x reference this list.
When you call g() function and pass the list, the function sets var to reference the same list (not a different one, the same one). You then tell the function to increase the integer value in the first element by 1. Since lists are mutable with certain methods, you have already changed the list for both the local variable var, and the global variable x. This means that you actually don't need to use any return of the function because the list has been mutated in place.
Have a look at this video https://youtu.be/_AEJHKGk9ns where Ned Batchelder explains more about this.
Is it legal to do
a = b = 3
in python? If so, is it a bad practice?
Yes, it is legal to do so. No, it is not bad practice.
Just take into account that the right-hand side, the value expression, is evaluated first, and assignment then takes place from left to right; 3 is assigned to a first, then to b.
From the assignment statement documentation:
An assignment statement evaluates the expression list (remember that this can be a single expression or a comma-separated list, the latter yielding a tuple) and assigns the single resulting object to each of the target lists, from left to right.
You assign the same value to all targets. That means that each variable refers to one value only. This is important when that value is mutable, like a list or a dictionary.
Yes, just watch out for stuff like this:
a = b = []
a.append(2)
print a
print b
Prints:
[2]
[2]
But other than that, it's fine. #Martijn has a lot of information in his answer, so check it out :).
I am a beginner in python and I find this about mutabilty quite confusing and non intuitive. Given a list:
lst = [[1, 2, 3], [4, 5, 6]]
And trying to change the list within a for-loop.
for i in lst:
i = "test"
I understand that this does not change the list. But:
for i in lst:
i[1] = "test"
I was surprised that referring to the sublist led to following outcome:
[[1, 'test', 3], [4, 'test', 6]]
I tried to understand with a visualizer but I don't get it. Would anybody please explain this in plain words? Thank you.
In the first case, you simply have a copied reference to the element.
i ---> lst[n]
In the latter case, you are dereferencing the reference, and changing data (not in a copy):
i[i] ---> lst[n][i]
Therefore assigning to i[n] will point to the actual mutable object, not a copy of it.
Assignment (which is what the = operator does) assigns a value to a name.
i is a variable name in the local namespace. At the time it runs inside the for loop, it refers to a list. By assigning a string to it, you cause the name to no longer refer to a list, but instead refer to the string. The list is unaffected -- the only thing that changes is the value associated with the name.
i[1] is a name that specifies a specific location inside one of the two lists, depending on what i is set to at the time. By assigning a string to it, you cause the name to no longer refer to the object that previously occupied that space inside the list (an integer, 2 or 5 depending on the list) and instead refer to the string. The integer is unaffected -- the only thing that changes is the value associated with the name.
So in each case, assignment is doing the same thing -- it's making a name refer to a new thing instead of the old thing it referred to. The difference is that the second case is a special name in that it refers to a property of a mutable object.
for does not make copies of each element it yields. As such, the yielded object retains all the properties of the original (since it is the original), including mutability.
since for loop in case of string is called in different way
like
for i in lst:
it means if lst is list or array of string then i is referenced to the value of x[i] directly that is why in first case result was "test " two times because length of "lst" was just 2
while in second case i[1] means
i ------lst[i]
i[1]--------`-lst[i][1] means
first value equal lst[0][1]---[1,'test',3]
second value equal lst[1][1]--[4,'test',6]
This question already has answers here:
How do I pass a variable by reference?
(39 answers)
Closed 9 months ago.
My code :
locs = [ [1], [2] ]
for loc in locs:
loc = []
print locs
# prints => [ [1], [2] ]
Why is loc not reference of elements of locs ?
Python : Everything is passed as reference unless explicitly copied [ Is this not True ? ]
Please explain.. how does python decides referencing and copying ?
Update :
How to do ?
def compute(ob):
if isinstance(ob,list): return process_list(ob)
if isinstance(ob,dict): return process_dict(ob)
for loc in locs:
loc = compute(loc) # What to change here to make loc a reference of actual locs iteration ?
locs must contain the final processed response !
I don't want to use enumerate, is it possible without it ?
Effbot (aka Fredrik Lundh) has described Python's variable passing style as call-by-object: http://effbot.org/zone/call-by-object.htm
Objects are allocated on the heap and pointers to them can be passed around anywhere.
When you make an assignment such as x = 1000, a dictionary entry is created that maps the string "x" in the current namespace to a pointer to the integer object containing one thousand.
When you update "x" with x = 2000, a new integer object is created and the dictionary is updated to point at the new object. The old one thousand object is unchanged (and may or may not be alive depending on whether anything else refers to the object).
When you do a new assignment such as y = x, a new dictionary entry "y" is created that points to the same object as the entry for "x".
Objects like strings and integers are immutable. This simply means that there are no methods that can change the object after it has been created. For example, once the integer object one-thousand is created, it will never change. Math is done by creating new integer objects.
Objects like lists are mutable. This means that the contents of the object can be changed by anything pointing to the object. For example, x = []; y = x; x.append(10); print y will print [10]. The empty list was created. Both "x" and "y" point to the same list. The append method mutates (updates) the list object (like adding a record to a database) and the result is visible to both "x" and "y" (just as a database update would be visible to every connection to that database).
Hope that clarifies the issue for you.
Everything in Python is passed and assigned by value, in the same way that everything is passed and assigned by value in Java. Every value in Python is a reference (pointer) to an object. Objects cannot be values. Assignment always copies the value (which is a pointer); two such pointers can thus point to the same object. Objects are never copied unless you're doing something explicit to copy them.
For your case, every iteration of the loop assigns an element of the list into the variable loc. You then assign something else to the variable loc. All these values are pointers; you're assigning pointers; but you do not affect any objects in any way.
It doesn't help in Python to think in terms of references or values. Neither is correct.
In Python, variables are just names. In your for loop, loc is just a name that points to the current element in the list. Doing loc = [] simply rebinds the name loc to a different list, leaving the original version alone.
But since in your example, each element is a list, you could actually mutate that element, and that would be reflected in the original list:
for loc in locs:
loc[0] = loc[0] * 2
When you say
loc = []
you are rebinding the loc variable to a newly created empty list
Perhaps you want
loc[:] = []
Which assigns a slice (which happens to be the whole list) of loc to the empty list
Everything is passed by object. Rebinding and mutating are different operations.
locs = [ [1], [2] ]
for loc in locs:
del loc[:]
print locs
Why is loc not reference of elements of locs ?
It is. Or at least, it is in the same sense that every other variable in Python is. Python variables are names, not storage. loc is a name that is used to refer to elements of [[1,2], [3,4]], while locs is a name that refers to the entire structure.
loc = []
This does not mean "look at the thing that loc names, and cause it to turn into []". It cannot mean that, because Python objects are not capable of such a thing.
Instead, it means "cause loc to stop being a name for the thing that it's currently a name for, and start instead being a name for []". (Of course, it means the specific [] that's provided there, since in general there may be several objects in memory that are the same.)
Naturally, the contents of locs are unchanged as a result.