Can someone explain to me why this function returns different results:
def g(x, z):
x.append(z)
return x
y = [1, 2, 3]
g(y, 4).extend(g(y[:], 4))
y = [1, 2, 3]
g(y[:], 4).extend(g(y, 4))
The first returns
[1, 2, 3, 4, 1, 2, 3, 4, 4]
and the second
[1, 2, 3, 4]
In both cases, None is returned, because list.extend() extends the list in-place. So you must be looking at what y ends up as. And that's where the rub is; you didn't extend y itself in the second example.
In the first example, you essentially do this:
y.append(4) # y = [1, 2, 3, 4]
temp_copy = y[:] # temp_copy = [1, 2, 3, 4]
temp_copy.append(4) # temp_copy = [1, 2, 3, 4, 4]
y.extend(temp_copy) # y = [1, 2, 3, 4, 1, 2, 3, 4, 4]
del temp_copy
print(y)
The temp_copy name is never really created; the list is only available on the stack and briefly as x inside g(), which is why I delete temp_copy again at the end to make this clear.
So y is first appended to, then extended with another list (which happens to be a copy of y with another element added).
In your second example, you do this instead:
temp_copy = y[:] # temp_copy = [1, 2, 3]
temp_copy.append(4) # temp_copy = [1, 2, 3, 4]
y.append(4) # y = [1, 2, 3, 4]
temp_copy.extend(y) # temp_copy = [1, 2, 3, 4, 1, 2, 3, 4]
del temp_copy
print(y)
You appended one element to y, and all other manipulations apply to a copy. The copy is discarded again, because in your code there is no reference to it.
You made kind of a mess with assignments and copies there. Note that:
append() modifies the list in-place, without creating a new one
so does extend()
y[:] does create a new list
Your expressions return None. You only effect modifications to the lists, you don't save references to new ones.
Let me "unroll" your code, to show the difference:
# First snippet:
y = [1, 2, 3]
y.append(4)
y_copy = list(y)
y_copy.append(4)
y.extend(y_copy)
# Second snippet:
y = [1, 2, 3]
y_copy = list(y)
y_copy.append(4)
y.append(4)
y_copy.extend(y)
As you can see, in the second example, you apply most modifications to the copy, not the original. In the first, all changes go to the original.
On a subjective note, that code piece was very hard to understand. You wrote it yourself and couldn't follow it, and I have years of experience in Python and still had to pull the "unrolling" trick. Try to keep your code simpler, so that objects can be followed and reasoned about.
On the first call you pass the list by reference, on the second call you made a copy of the list(sublisting it).
explain :
>>> one = [1,2,3]
>>> ref = one
>>> copy = one[:]
>>> one
[3, 2, 3]
>>> ref
[3, 2, 3]
>>> copy
[1, 2, 3]
Related
case 1:
a=[1,2,3,4,5]
index k=2
a[:k],a[k:]=a[k:],a[:k]
When I swap array elements like this. I got this output.
**OUTPUT:[3, 4, 1, 2]
case 2:
b=[1,2,3,4,5]
b[k:],b[:k]=b[:k],b[k:]
but when I swap array elements like this i got this.The only difference is the order of swapping.
OUTPUT:[3, 4, 5, 1, 2]
If we swap two variables, the order of swapping doesn't make a difference.
i.e a,b=b,a is same as b,a=a,b.
Why this is not working in the case of lists/array?
The right hand side is evaluated fully before any assignments are done. Subsequently, the assignments are performed in left to right order.
So the first case evaluates to:
a[:2], a[2:] = [3, 4, 5], [1, 2]
The first assignment replaces the first two elements [1, 2] by the three elements [3, 4, 5], so now we have a == [3, 4, 5, 3, 4, 5]. The second assignment then replaces element 2 onwards by [1, 2], so it results in a == [3, 4, 1, 2].
In the second case, we have:
b[2:], b[:2] = [1, 2], [3, 4, 5]
The first assignment replaces from element 2 onwards by [1, 2] resulting in b == [1, 2, 1, 2]. The second assignment replaces the first two elements by [3, 4, 5], giving b == [3, 4, 5, 1, 2].
You may have noticed by now that this is rather tricky to think about and sometimes works mostly by accident, so I'd recommend simplifying the whole thing to:
a[:] = a[k:] + a[:k]
If you swap two variables, there's no relation between two variables, then ok.
You can find the steps as following:
>>> a=[1,2,3,4,5]
>>> k=2
>>> # a[:k], a[k:] = a[k:], a[:k]
>>> x, y = a[k:], a[:k]
>>> a[:k] = x
>>> x, a
([3, 4, 5], [3, 4, 5, 3, 4, 5])
>>> a[k:] = y
>>> y, a
([1, 2], [3, 4, 1, 2])
sorry I'm new to python and still trying to wrap my head around few fundamentals. I know lists are mutable objects in python, but can't understand how the two functions below handle lists, and why one changes the list itself and the other doesn't
def f(x, y):
x.append(x.pop(0))
x.append(y[0])
return x
>>> a=[1,2,3]
>>> b=[1,2]
>>> f(a,b)
>>> [2, 3, 1, 1]
>>> a
>>> [2, 3, 1, 1]
def f(x, y):
y = y + [x]
return y
>>> a=[1,2,3]
>>> f(4,a)
>>> [1, 2, 3, 4]
>>> a
>>> [1, 2, 3]
Thank you
The + operation stores the result in a new list, while append operation appends the new value into the existing list
For example,
def f(x, y):
y = y + [x]
print(y)
y.append(x)
print(y)
a=[1,2,3]
f(4,a)
print(a)
gives
[1, 2, 3, 4]
[1, 2, 3, 4, 4]
[1, 2, 3]
y = y + [x] creates a new list y & hence the change is not reflected to the original list a & also the later append changes the new list y
but
def f(x, y):
y.append(x)
print(y)
y = y + [x]
print(y)
a=[1,2,3]
f(4,a)
print(a)
output
[1, 2, 3, 4]
[1, 2, 3, 4, 4]
[1, 2, 3, 4]
Here the append operation changes the original list a after that new list y is created
Simply put, the difference is in the function of append and +. append adds a new item to the list without creating a new list. + merges two lists together and creates a new list.
For more information, see DNS's answer here.
I am wondering, how i can shorten this:
test = [1, 2, 3]
test[0] = [1, 2, 3]
test[1] = [1, 2, 3]
test[2] = [1, 2, 3]
I tried something like this:
test = [1[1, 2, 3], 2 [1, 2, 3], 3[1, 2, 3]]
#or
test = [1 = [1, 2, 3], 2 = [1, 2, 3], 3 = [1, 2, 3]] #I know this is dumb, but at least I tried...
But it's not functioning :|
Is this just me beeing stupid and trying something that can not work, or is there a proper Syntax for this, that I don't know about?
The simplest way is
test = [[1, 2, 3], [1, 2, 3], [1, 2, 3]]
But, if you want to have more number of lists to be created then you might want to go with list comprehension, like this
test = [[1, 2, 3] for i in range(100)]
This will create a list of 100 sub lists. The list comprehension is to create a new list and it can be understood like this
test = []
for i in range(100):
test.append([1, 2, 3])
Note: Instead of doing test[0] = ..., you can simply make use of list.append like this
test = []
test.append([1, 2, 3])
...
If you look at the language definition of list,
list_display ::= "[" [expression_list | list_comprehension] "]"
So, a list can be constructed with list comprehension or expression list. If we see the expression list,
expression_list ::= expression ( "," expression )* [","]
It is just a comma separated one or more expressions.
In your case,
1[1, 2, 3], 2[1, 2, 3] ...
are not valid expressions, since 1[1, 2, 3] has no meaning in Python. Also,
1 = [1, 2, 3]
means you are assigning [1, 2, 3] to 1, which is also not a valid expression. That is why your attempts didn't work.
Your code: test = [1 = [1, 2, 3], 2 = [1, 2, 3], 3 = [1, 2, 3]] is pretty close. You can use a dictionary to do exactly that:
test = {1: [1, 2, 3], 2: [1, 2, 3], 3: [1, 2, 3]}
Now, to call test 1 simply use:
test[1]
Alternatively, you can use a dict comprehension:
test = {i: [1, 2, 3] for i in range(3)}
It's a list comprehension:
test = [[1, 2, 3] for i in range(3)]
If you want, you can do this:
test = [[1,2,3]]*3
#=> [[1, 2, 3], [1, 2, 3], [1, 2, 3]]
===== Edited ====.
However, Note that all elements refer to the same object
# 1. -----------------
# If one element is changed, the others will be changed as well.
test = [[1,2,3]]*3
print(test)
#=>[[1, 2, 3], [1, 2, 3], [1, 2, 3]]
test[0][1] = 4
print(test)
#=>[[1, 4, 3], [1, 4, 3], [1, 4, 3]] # because it refer to same object
# 2. -----------------
# Of course, those elements have the same value.
print("same value") if test[0] == test[1] else print("not same value")
#=> same value
# 3. -----------------
# Make sure that All refer to the same object
print("refer to the same object") if test[0] is test[1] else print("not refer to the same object")
#=> refer to the same object
# 4. -----------------
# So, Make sure that All have same id
hex(id(test[0]))
#=>e.g. 0x7f116d337820
hex(id(test[1]))
#=>e.g. 0x7f116d337820
hex(id(test[2]))
#=>e.g. 0x7f116d337820
Im a bit baffled how this works.
x = []
y = []
for i in range(5):
y.append(i) # Why does this create full copies of sub lists?
x.append(y)
#x.extend(y) # This works normal
print x
Why is x.append(y) cauusing the final result to be the following? Could you please explain if there is some background reference values going on?
[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]
There is only one object pointed to by y. It starts as an empty list. Each time through the loop, you are making that single object longer. The list x is essentially the same as [y, y, y, y, y], which gives you the result you describe.
When you use x.extend(y), then the current elements of y are copied onto the end of the list x. This is a totally different operation.
Can't figure out how to do this in a pretty way :
I have a list of n elements,
I want to access every m elements of the list.
For example : [1, 2, 3, 4, 5] and m = 2 would give
[2, 4]
I can do it simply with a loop, but ins't there a more "pythonic" way?
Thanks by advance !
EDIT :
Seems like I forgot something.
I want, not only get those values but modify them.
I tried slicing a[::2] = 3, but it doesn't work. . .
I'm searching for something similar
Slicing syntax does this for you:
>>> my_list = range(10)
>>> my_list
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> my_list[::2]
[0, 2, 4, 6, 8]
>>> my_list[1::2]
[1, 3, 5, 7, 9]
Here's a way to wrap a list to get the original assignment behavior you wanted, but I'm not sure I'd recommend it:
class AssignableSlice(list):
def __setitem__(self, i, v):
if isinstance(i, slice):
for ii in xrange(*i.indices(len(self))):
self[ii] = v
else:
super(AssignableSlice, self).__setitem__(i, v)
a = AssignableSlice(range(10))
print a
a[::2] = 3
print a
a[1::3] = 99
print a
produces:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[3, 1, 3, 3, 3, 5, 3, 7, 3, 9]
[3, 99, 3, 3, 99, 5, 3, 99, 3, 9]
Ned's answer shows how to use slices to access a portion of the list. You can also assign to a slice, but you need to assign a list to the slice, for example:
>>> my_list = range(5)
>>> my_list
[0, 1, 2, 3, 4]
>>> my_list[::2]
[0, 2, 4]
>>> my_list[::2] = [0, 0, 0]
>>> my_list
[0, 1, 0, 3, 0]
Note that when the step in your slice is anything besides the default of 1 the list that you assign needs to be the same length, however with a default step you can actually change the size of the list with slice assignment:
>>> my_list = range(5)
>>> my_list
[0, 1, 2, 3, 4]
>>> my_list[:1]
[0]
>>> my_list[:1] = [4, 3, 2] # replace the first item with 3 new items
>>> my_list
[4, 3, 2, 1, 2, 3, 4]
>>> my_list[2:5]
[2, 1, 2]
>>> my_list[2:5] = [] # remove the middle three items from the list
>>> my_list
[4, 3, 3, 4]
I juste found a way to do what I want using slicing.
The following :
candidates[::2] = [1] * len(candidates[::2])
will replace every 2 elements of candidates by 1