This question already has answers here:
How do I clone a list so that it doesn't change unexpectedly after assignment?
(24 answers)
Closed 2 years ago.
I want to retain the original list while manipulating it i.e I'm using it in a loop and have to perform some operations each iteration so need to reset the value of a list. Initially, I thought it was a problem with my loops but I have narrowed it down to.
inlist=[1,2,3]
a=inlist
a.pop(0)
print(a)
print(inlist)
gives an output of
[2,3]
[2,3]
Why am I not getting
[2,3]
[1,2,3]
It is applying pop to both a and inlist.
Let me explain with Interactive console:
>>> original_list = [1, 2, 3, 4] # source
>>> reference = original_list # 'aliasing' to another name.
>>> reference is original_list # check if two are referencing same object.
True
>>> id(reference) # ID of referencing object
1520121182528
>>> id(original_list) # Same ID
1520121182528
To create new list:
>>> copied = list(original_list)
>>> copied is original_list # now referencing different object.
False
>>> id(copied) # now has different ID with original_list
1520121567616
There's multiple way of copying lists, for few examples:
>>> copied_slicing = original_list[::]
>>> id(copied_slicing)
1520121558016
>>> import copy
>>> copied_copy = copy.copy(original_list)
>>> id(copied_copy)
1520121545664
>>> copied_unpacking = [*original_list]
>>> id(copied_unpacking)
1520123822336
.. and so on.
Image from book 'Fluent Python' by Luciano Ramalho might help you understand what's going on:
Rather than 'name' being a box that contains respective object, it's a Post-it stuck at object in-memory.
Try doing it this way
a = [1,2,3]
b=[]
b.extend(a)
b.pop(0)
Although what you are doing makes sense but what is happening is that you are just assigning another variable to the same list, which is why both are getting affected. However if you define b(in my case) as an empty list and then assign it, you are then making a copy as compared to another variable pointing to the same list.
Related
a = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]
for i in range(1, 27):
for j in range(1,27):
if j!=i:
lst = a
print(lst)
print(a)
lst.remove(i)
lst.remove(j)
print(lst)
print(a)
List 'a' is getting smaller coz i change list 'lst', wtf is this?
I just started to perform codewars kata.
By default, python creates a reference for an object. If you want to make an actual copy with new memory what you can do is:
from copy import deepcopy
lis_copy = deepcopy(lis)
I would like to create a pandas dataframe using the names from a list and then appending '_df' to the end of it but I seem to have two issues. Here is my code below.
read_csv = ['apple', 'orange', 'bananna']
for f in read_csv:
print('DEBUG 7: Value of f inside the loop: ', f)
##!!! ERROR HERE - We have reassigned the csv file to f
##!!! ERROR HERE - f now contains contents of f.csv(e.g. apple.csv)
f = pd.read_csv(f + '.csv')
##!!! ERROR HERE - Fix above error and the spice shall flow.
#print('DEBUG 8: Inside read_csv \n', f)
The for loop runs and reads in the first item in my list 'apple' and assigns it to f.
We drop into the loop. The first print statement, DEBUG 7, returns the value of f as 'apple'. So far so good.
Next, we run on to the pd.read_csv which is where my first issue is. How do I append '_df' to f? I have read a few answers on here and tried them but it's not working as I expect. I would like to have the loop run and create a new dataframe for apple_df, orange_df and bananna_df. But we can come back to that.
The second error I get here is "ValueError: Wrong number of items passed 8, placement implies 1" The CSV file has 8 columns and that is getting assigned to f instead of the dataframe name.
I can't for the life of me work out what's occurring to make that happen. Well, I can. If I fix the apple_df issue I believe the dataframe will read in the csv file fine.
Still learning so all help is appreciated.
Thanks
Tom
Use locals() to create local variables (apple_df, orange_df, ...)
read_csv = ['apple', 'orange', 'bananna']
for f in read_csv:
locals()[f"{f}_df"] = pd.read_csv(f"{f}.csv")
>>> type(apple_df)
pandas.core.frame.DataFrame
ValueError: Wrong number of items passed 8, placement implies 1
You got that error because you can't assign DataFrame to f variable which is a string in that loop. You have to store it into new variable, for exaple df
df = pd.read_csv(f + '.csv')
If you want to create new variable by f and "_df" you need to use exec
exec(f + "_df" + " = pd.read_csv(f + '.csv')")
This question already has answers here:
Iterate over all lists inside a list of varied lengths [closed]
(3 answers)
How does one merge a list, but maintain the previous lists order?
(3 answers)
Closed 2 years ago.
I want to do something called lacing.
I have 2 lists:
list1 = ['4,5', '5,3', '1,2', '0,4', '6,2']
list2 = ['1,3', '6,4', '8,8']
I want my output to look like this
list3 = ['4,5','1,3','5,3','6,4','1,2','8,8','0,4', '6,2']
So the new list should have the first element of the first list, then the first element of the second list followed by the second element of the first list, then the second element in the second list.
Finally, if they are not equal, I would like to dump the rest at the end of the new list.
I tried using
for l1,l2 in zip(one_final,three_final):
list3.append(l1+l2)
but I got this back
['4,50,6', '5,33,1', '1,27,8', '0,46,6', '6,27,7']
How can I do this?
Thank you.
Given:
list1 = ['4,5', '5,3', '1,2', '0,4', '6,2']
list2 = ['1,3', '6,4', '8,8']
You can do:
import itertools as it
>>> [e for t in it.zip_longest(list1, list2) for e in t if e is not None]
['4,5', '1,3', '5,3', '6,4', '1,2', '8,8', '0,4', '6,2']
If there is a possibility of None as legit data in your lists, use a sentinel object instead:
sentinel=object()
[e for t in it.zip_longest(list1, list2, fillvalue=sentinel) for e in t if e is not sentinel]
I am trying to make a program that finds out how many integers in a list are not the integer that is represented the most in that list. To do that I have a command which creates a dictionary with every value in the list and the number of times it is represented in it. Next I try to create a new list with all items from the older list except the most represented value so I can count the length of the list. The problem is that I cannot access the most represented value in the dictionary as I get an error code.
import operator
import collections
a = [7, 155, 12, 155]
dictionary = collections.Counter(a).items()
b = []
for i in a:
if a != dictionary[max(iter(dictionary), key=operator.itemgetter(1))[0]]:
b.append(a)
I get this error code: TypeError: 'dict_items' object does not support indexing
The variable you called dictionary is not a dict but a dict_items.
>>> type(dictionary)
<class 'dict_items'>
>>> help(dict.items)
items(...)
D.items() -> a set-like object providing a view on D's items
and sets are iterable, not indexable:
for di in dictionary: print(di) # is ok
dictionary[0] # triggers the error you saw
Note that Counter is very rich, maybe using Counter.most_common would do the trick.
i'm very new to python so please forgive me if my error is blatantly obvious.
The issue I am having is with line 15. I am struggling to check if the input value is NOT in the list "list"
All values currently entered (in and not in the list "list") will return the response "srry".
I know line 15 is at fault because if I create a separate list with no nested lists for the countries: England, French and Mandarin to be called just for line 15, the appropriate response based on input is printed as expected.
Any help would be highly appreciative.
#input
lang = str(input("Please Type in the Language you speak and press enter
('make sure you use a capital letter')"))
#list of countries
list = [["English", "english"], ["French", "french"], ["Mandarin",
"mandarin"]]
#list of responses
lls = ["Hello,", "Bonjour,", "Ni Hao"]
srry = "Sorry, but I don't speak that"
welcmsg = "Welcome to Coventry"
# check if input is not in list
if str(lang) not in list:
print(srry)
#provide appropriate response based on input
elif str(lang) in list[0]:
print(lls[0] + " " + welcmsg)
elif str(lang) in list[1]:
print(lls[1] + " " +welcmsg)
elif str(lang) in list[2]:
print(lls[2])
TLDR; change line 15 to search the list-of-lists:
# check if input is not in list
if lang not in [item for sublist in mylist for item in sublist]:
print(srry)
Details:
The problem is that "list" is a nested-list (a list-of-lists), and so when we check if the input value is not in the list, we're checking if the value is only in the outer-most level of the list, which are still lists.
Consider the following:
list of lists:
>>> mylist = [['a', 'b'], ['x', 'y']]
>>> 'x' in mylist
False
vs. a single-level, flat list:
>>> mylist = ['a', 'b', 'x', 'y']
>>> 'x' in mylist
True
To fix it we can either convert the initial list-of-lists to one "flat" list (single-level), or we'll need to search each sub-list within the "list".
Keeping the code as close to the original and only changing the line 15 list search:
# check if input is not in list
if lang not in [item for sublist in mylist for item in sublist]:
print(srry)
Also, as mentioned in the comment - consider using a different variable name for the "list" that doesn't overwrite the built-in keyword list to prevent unexpected behavior. Here I've used mylist for the "list" name.
Another thing you might consider is using str.lower() when taking user input:
>>> mylist = ["english", "french", "mandarin"]
>>> lang = str(input("language? ").lower())
language? French
>>> lang in mylist
True
The user can still use a capital-letter for the input, but it makes the input validation a bit easier to check with fewer strings to check. Just a side-tip.
Hope this helps!