Generate list of copies in Python - python

I want to generate list of 100 copies of the same sentence with one char modified in each copy. I am currently learning python and I don't understand why something like this:
def generate_copy(seq):
copies = [seq for i in range(100)]
for copy in copies:
copy[random.randint(0, len(seq) - 1)] = random.choice(char_list)
print(''.join(copy))
return copies
modifies all copies. I want to get something like this: ['AAAB', 'BAAA', 'ZAAA', ...], like they are independent from each other, but I get: ['AAAB', 'ZAAB', 'ZCAB', ...]. How can I do it?

Not sure how did you get result you pasted - string in python is immutable. copies is list of strings. So copy is string. You cannot type copy[0] = 'a' as you will get TypeError: 'str' object does not support item assignment cause of that.
What do you want to do is build changed sequence when you create copies.
def change_random_letter(seq, char_list):
i = random.randint(0, len(seq) - 1)
new_letter = random.choice(char_list)
return f"{seq[:i]}{new_letter}{seq[i+1:]}"
def generate_copy(seq):
copies = [change_random_letter(seq, char_list) for _ in range(100)]
return copies
EDIT: I noticed you pass list as seq. If so, copies are not a copies! Look
l = [1, 2]
ll = [l, l]
ll[0][0] = "A"
print(ll) # [['A', 2], ['A', 2]]
When type is mutable, by using a variable you use the reference. So all copies elements points to the same place in memory. You need to use copy function of list.
copies = [seq.copy() for _ in range(100)]

Related

How to delete element ONLY from new list

Let suppose I have a list l and I make new list temp from l. When I try to delete element from temp it also deletes element from l too. This is so strange. How can I only delete element from temp?
l = [1,2,3]
temp = l
temp.remove(3)
print(l, temp)
Output: ([1, 2], [1, 2])
Actually both l and temp are names that refer to the same object list in memory. When you do something with any of them, you are doing it to the underlying object list that is pointed by those two names.
You can always create a new object by using the copy method:
temp = l.copy()
And that way, you will be having two names pointing to different objects.
When you create a list using temp=l, python doesn't create new object.
It refers to the same object as temp so whatever changes you make will reflect in original list as well. Use copy method to avoid this
l = [1,2,3]
temp = l.copy()
temp.remove(3)
print(l, temp)
Output:
[1, 2, 3] [1, 2]
If you do this:
l = [1,2,3]
temp = l
You haven't created a new list, but instead you have merely created a new reference to the same list. Changing the list through either variable changes it for both.
To copy the list, you can do:
temp = list(l)
Another way is to do temp = l[:], which is equivalent. In Python 3 you can also do temp = l.copy(), but that won't work in Python 2.
Then you can change either list without affecting the other.
temp = l is simply assigning temp to a reference of l. So whatever you do to one list, the other is affected.
We can check this with the id() function:
print(id(l))
# 1739514774400
print(id(temp))
# 1739514774400
Which returns the same unique address for each object in memory.
We can see this in the documentation:
Return the “identity” of an object. This is an integer which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value.
CPython implementation detail: This is the address of the object in memory.
In this case both l and temp are the same, so they have the same identities.
We can fix this by assigning a copy instead:
l = [1,2,3]
temp = l[:]
Which now gives different identities and only removes 2 from temp:
print(id(l))
# 1822815159168
print(id(temp))
# 1822815159104
temp.remove(3)
print(l, temp)
# [1, 2, 3] [1, 2]

How to update list with function (with return value) in python

I am implementing few list methods manually like append(), insert(), etc. I was trying to add element at the end of list (like append method). This the working code i am using:
arr = [4,5,6]
def push(x, item):
x += [item]
return x
push(arr,7)
print(arr) #Output: [4,5,6,7]
But when I am implementing same code with little difference. I am getting different output.
arr = [4,5,6]
def push(x, item):
x = x + [item]
return x
push(arr,7)
print(arr) #Output: [4,5,6]
And I am facing same for insert method. Here is code for insert method:
arr = [4,5,7,8]
def insert(x, index, item):
x = x[:index] + [item] + x[index:]
return x
insert(arr,2,6)
print(arr) #Output: [4,5,7,8]
I know I can store return value to the list by arr=insert(arr,2,6) but I want an alternative solution, that list automatically gets update after calling function like in my first code sample.
Edit 1:
I think x[index:index] = [item] is better solution for the problem.
x += [item] and x = x + [item] are not a little difference. In the first case, you are asking to make a change to the list referenced by x; this is why the result reflects the change. In the second, you are asking to have x reference a new list, the one made by combining x's original value and [item]. Note that this does not change x, which is why your result is unchanged.
Also note that your return statements are irrelevant, since the values being returned are ignored.
In your first example you mutated(a.k.a changed) the list object referred to by x. When Python sees x += [item] it translates it to:
x.__iadd__([item])
As you can see, we are mutating the list object referred to by x by calling it's overloaded in-place operator function __iadd__. As already said, __iadd__() mutates the existing list object:
>>> lst = [1, 2]
>>> lst.__iadd__([3])
[1, 2, 3]
>>> lst
[1, 2, 3]
>>>
In your second example, you asked Python to assign x to a new reference. The referenced now referrer to a new list object made by combining (not mutating) the x and [item] lists. Thus, x was never changed.
When Python sees x = x + [item] it can be translated to:
x = x.__add__([item])
The __add__ function of lists does not mutate the existing list object. Rather, it returns a new-list made by combing the value of the existing list and the argument passed into __add__():
>>> lst = [1, 2]
>>> lst.__add__([3]) # lst is not changed. A new list is returned.
[1, 2, 3]
>>>
You need to return the the result of the version of push to the arr list. The same goes for insert.
You can assign to a slice of the list to implement your insert w/o using list.insert:
def insert(x, index, item):
x[:] = x[:index] + [item] + x[index:]
this replaces the contents of the object referenced by x with the new list. No need to then return it since it is performed in-place.
The problem is that you haven't captured the result you return. Some operations (such as +=) modify the original list in place; others (such as x = x + item) evaluate a new list and reassign the local variable x.
In particular, note that x is not bound to arr; x is merely a local variable. To get the returned value into arr, you have to assign it:
arr = push(arr, 7)
or
arr = insert(arr, 2, 6)
class DerivedList(list):
def insertAtLastLocation(self,obj):
self.__iadd__([obj])
parameter=[1,1,1]
lst=DerivedList(parameter)
print(lst) #output[1,1,1]
lst.insertAtLastLocation(5)
print(lst) #output[1,1,1,5]
lst.insertAtLastLocation(6)
print(lst) #output[1,1,1,5,6]
You can use this code to add one element at last position of list
class DerivedList(list):
def insertAtLastLocation(self,*obj):
self.__iadd__([*obj])
parameter=[1,1,1]
lst=DerivedList(parameter)
print(lst) #output[1,1,1]
lst.insertAtLastLocation(5)
print(lst) #output[1,1,1,5]
lst.insertAtLastLocation(6,7)
print(lst) #output[1,1,1,5,6,7]
lst.insertAtLastLocation(6,7,8,9,10)
print(lst) #output[1,1,1,5,6,7,8,9,10]
This code can add multiple items at last location

What does this notation do for lists in Python: "someList[:]"?

I sometimes get across this way of printing or returning a list - someList[:].
I don't see why people use it, as it returns the full list.
Why not simply write someList, whithout the [:] part?
[:] creates a slice, usually used to get just a part of a list. Without any minimum/maximum index given, it creates a copy of the entire list. Here's a Python session demonstrating it:
>>> a = [1,2,3]
>>> b1 = a
>>> b2 = a[:]
>>> b1.append(50)
>>> b2.append(51)
>>> a
[1, 2, 3, 50]
>>> b1
[1, 2, 3, 50]
>>> b2
[1, 2, 3, 51]
Note how appending to b1 also appended the value to a. Appending to b2 however did not modify a, i.e. b2 is a copy.
In python, when you do a = b, a doesn't take the value of b, but references the same value referenced by b. To see this, make:
>>> a = {'Test': 42}
>>> b = a
>>> b['Test'] = 24
What is now the value of a?
>>> a['Test']
24
It's similar with lists, so we must find a way to really copy a list, and not make a reference to it. One way could be to recreate the list copy = list(list1), or use the functions of the copy module. But, after all, the easiest way, the prettiest, the best way ( ;) ) for doing this, is to copy each value of the first list to the other, by doing copy = list1[:]. It uses the slices, here list1 is sliced from index 0 to index len(list1), so the whole list1 is returned!
Moreover, the slice method is slightly faster: using the time.clock() method to measure the mean execution time of 1000 assignment of lists, each one containing 10000 random integers, with slices, constructor and deepcopy, the results show that the slices are 15% faster than the constructor method, and deepcopy is 4 times slower. However, this gain of time is negligible while using small lists: thus, using copy = list(list_to_copy) or copy = list_to_copy[:] is up to the developer's preferences.
Finally, we often forget the list.copy method, which seems to be the faster! In fact, it's even 13% faster than the slice method!
To create a copy of a list instead of passing by reference, as Python does. Use next two example to understand the difference.
Example:
# Passing by reference
SomeListA = [1, 2, 3]
SomeListB = [2, 3, 4]
SomeListB = SomeListA
SomeListA[2] = 5
print SomeListB
print SomeListA
# Using slice
SomeListA = [1, 2, 3]
SomeListB = [2, 3, 4]
SomeListB = SomeListA[:]
SomeListA[2] = 5
print SomeListB
print SomeListA
When you need to modify the list and you don't want to change the list and create another list you use
y = [1,2,3]
x = y[:]
and you can do a lot of changes to the list but the origin list will be in (y) and the modified in (x)
#Here is a simpler way for beginners to understand:
list16 = [1,2,3,4,5,6]
list17 = list16[:]
#^Identifying that 'list17' is a copy of 'list16' and not list16 directly
list17[0] = 10
#^ Making an alteration in the new copied list
print(list17)
print(list16)
= [10,2,3,4,5,6]
= [1,2,3,4,5,6]
#Printing the lists so that you can see what is happening. I created a copy of the list and altered it without changing the original list at all.
There are 2 copies available. 1) Deep Copy 2) Shallow Copy.
1) Deep Copy is you just copy the values
list = ['abc',123,'xyz']
list1 = copy.deepcopy(list) or list1 = list[:]
2) Shallow Copy is you just reference to the varible
list2 = copy.copy(list) or list2 = list
When you modify something on list2 it get effected in list also as it is referenced.
list1.append(456)
list2.append('789')
print "list: %s" %list
print "list1: %s" %list1
print "list2: %s" %list2
ans:
list : ['abc',123,'xyz','789']
list1 : ['abc',123,'xyz',456]
list2 : ['abc',123,'xyz','789']

Iteratively add elements to a list

I am trying to add elements of a list in Python and thereby generate a list of lists. Suppose I have two lists a = [1,2] and b = [3,4,5]. How can I construct the following list:
c = [[1,2,3],[1,2,4],[1,2,5]] ?
In my futile attempts to generate c, I stumbled on an erroneous preconception of Python which I would like to describe in the following. I would be grateful for someone elaborating a bit on the conceptual question posed at the end of the paragraph. I tried (among other things) to generate c as follows:
c = []
for i in b:
temp = a
temp.extend([i])
c += [temp]
What puzzled me was that a seems to be overwritten by temp. Why does this happen? It seems that the = operator is used in the mathematical sense by Python but not as an assignment (in the sense of := in mathematics).
You are not creating a copy; temp = a merely makes temp reference the same list object. As a result, temp.extend([i]) extends the same list object a references:
>>> a = []
>>> temp = a
>>> temp.extend(['foo', 'bar'])
>>> a
['foo', 'bar']
>>> temp is a
True
You can build c with a list comprehension:
c = [a + [i] for i in b]
By concatenating instead of extending, you create a new list object each iteration.
You could instead also have made an actual copy of a with:
temp = a[:]
where the identity slice (slicing from beginning to end) creates a new list containing a shallow copy.

Add an object to a python list

I am trying to add an object to a list but since I'm adding the actual object when I try to reset the list thereafter, all the values in the list are reset.
Is there an actual way how I can add a monitor object to the list and change the values and not affect the ones I've already saved in the list?
Thanks
Code:
arrayList = []
for x in allValues:
result = model(x)
arrayList.append(wM)
wM.reset()
where wM is a monitor class - which is being calculated / worked out in the model method
Is your problem similar to this:
l = [[0]] * 4
l[0][0] += 1
print l # prints "[[1], [1], [1], [1]]"
If so, you simply need to copy the objects when you store them:
import copy
l = [copy.copy(x) for x in [[0]] * 4]
l[0][0] += 1
print l # prints "[[1], [0], [0], [0]]"
The objects in question should implement a __copy__ method to copy objects. See the documentation for copy. You may also be interested in copy.deepcopy, which is there as well.
EDIT: Here's the problem:
arrayList = []
for x in allValues:
result = model(x)
arrayList.append(wM) # appends the wM object to the list
wM.reset() # clears the wM object
You need to append a copy:
import copy
arrayList = []
for x in allValues:
result = model(x)
arrayList.append(copy.copy(wM)) # appends a copy to the list
wM.reset() # clears the wM object
But I'm still confused as to where wM is coming from. Won't you just be copying the same wM object over and over, except clearing it after the first time so all the rest will be empty? Or does model() modify the wM (which sounds like a terrible design flaw to me)? And why are you throwing away result?
You need to create a copy of the list before you modify its contents. A quick shortcut to duplicate a list is this:
mylist[:]
Example:
>>> first = [1,2,3]
>>> second = first[:]
>>> second.append(4)
>>> first
[1, 2, 3]
>>> second
[1, 2, 3, 4]
And to show the default behavior that would modify the orignal list (since a name in Python is just a reference to the underlying object):
>>> first = [1,2,3]
>>> second = first
>>> second.append(4)
>>> first
[1, 2, 3, 4]
>>> second
[1, 2, 3, 4]
Note that this only works for lists. If you need to duplicate the contents of a dictionary, you must use copy.deepcopy() as suggested by others.
while you should show how your code looks like that gives the problem, i think this scenario is very common. See copy/deepcopy
If i am correct in believing that you are adding a variable to the array but when you change that variable outside of the array, it also changes inside the array but you don't want it to then it is a really simple solution.
When you are saving the variable to the array you should turn it into a string by simply putting str(variablename). For example:
array.append(str(variablename))
Using this method your code should look like this:
arrayList = []
for x in allValues:
result = model(x)
arrayList.append(str(wM)) #this is the only line that is changed.
wM.reset()

Categories