Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How exactly does Python check through a list?

I was doing one of the course exercises on codeacademy for python and I had a few questions I couldn't seem to find an answer to:

For this block of code, how exactly does python check whether something is "in" or "not in" a list? Does it run through each item in the list to check or does it use a quicker process?

Also, how would this code be affected if it were running with a massive list of numbers (thousands or millions)? Would it slow down as the list size increases, and are there better alternatives?

numbers = [1, 1, 2, 3, 5, 8, 13]

def remove_duplicates(list):
  new_list = []
  for i in list: 
    if i not in new_list:
      new_list.append(i)
  return new_list

remove_duplicates(numbers)

Thanks!

P.S. Why does this code not function the same?

numbers = [1, 1, 2, 3, 5, 8, 13]

def remove_duplicates(list):
  new_list = []
  new_list.append(i for i in list if i not in new_list)
  return new_list
like image 396
xCubit Avatar asked May 06 '18 07:05

xCubit


People also ask

How does Python in list work?

In Python, a list is created by placing elements inside square brackets [] , separated by commas. A list can have any number of items and they may be of different types (integer, float, string, etc.). A list can also have another list as an item. This is called a nested list.

How do you check if a word is in a list Python?

I would suggest a function along the following: def check(word, list): if word in list: print("The word is in the list!") else: print("The word is not in the list!")

How do you check if a string is present in list Python?

We can also use count() function to get the number of occurrences of a string in the list. If its output is 0, then it means that string is not present in the list. l1 = ['A', 'B', 'C', 'D', 'A', 'A', 'C'] s = 'A' count = l1. count(s) if count > 0: print(f'{s} is present in the list for {count} times.

How do you check if something is not in a list Python?

“not in” operator − This operator is used to check whether an element is not present in the passed list or not. Returns true if the element is not present in the list otherwise returns false.


Video Answer


4 Answers

In order to execute i not in new_list Python has to do a linear scan of the list. The scanning loop breaks as soon as the result of the test is known, but if i is actually not in the list the whole list must be scanned to determine that. It does that at C speed, so it's faster than doing a Python loop to explicitly check each item. Doing the occasional in some_list test is ok, but if you need to do a lot of such membership tests it's much better to use a set.

On average, with random data, testing membership has to scan through half the list items, and in general the time taken to perform the scan is proportional to the length of the list. In the usual notation the size of the list is denoted by n, and the time complexity of this task is written as O(n).

In contrast, determining membership of a set (or a dict) can be done (on average) in constant time, so its time complexity is O(1). Please see TimeComplexity in the Python Wiki for further details on this topic. Thanks, Serge, for that link.

Of course, if your using a set then you get de-duplication for free, since it's impossible to add duplicate items to a set.

One problem with sets is that they generally don't preserve order. But you can use a set as an auxilliary collection to speed up de-duping. Here is an illustration of one common technique to de-dupe a list, or other ordered collection, which does preserve order. I'll use a string as the data source because I'm too lazy to type out a list. ;)

new_list = []
seen = set()
for c in "this is a test":
    if c not in seen:
        new_list.append(c)
        seen.add(c)
print(new_list)

output

['t', 'h', 'i', 's', ' ', 'a', 'e']

Please see How do you remove duplicates from a list whilst preserving order? for more examples. Thanks, Jean-François Fabre, for the link.


As for your PS, that code appends a single generator object to new_list, it doesn't append what the generate would produce.

I assume you alreay tried to do it with a list comprehension:

new_list = [i for i in list if i not in new_list]

That doesn't work, because the new_list doesn't exist until the list comp finishes running, so doing in new_list would raise a NameError. And even if you did new_list = [] before the list comp, it won't be modified by the list comp, and the result of the list comp would simply replace that empty list object with a new one.


BTW, please don't use list as a variable name (even in example code) since that shadows the built-in list type, which can lead to mysterious error messages.

like image 102
PM 2Ring Avatar answered Oct 17 '22 19:10

PM 2Ring


You are asking multiple questions and one of them asking if you can do this more efficiently. I'll answer that.

Ok let's say you'd have thousands or millions of numbers. From where exactly? Let's say they were stored in some kind of txtfile, then you would probably want to use numpy (if you are sticking with Python that is). Example:

import numpy as np

numbers = np.array([1, 1, 2, 3, 5, 8, 13], dtype=np.int32)
numbers = np.unique(numbers).tolist()

This will be more effective (above all memory-efficient compared) than reading it with python and performing a list(set..)

numbers = [1, 1, 2, 3, 5, 8, 13]
numbers = list(set(numbers))
like image 30
Anton vBR Avatar answered Oct 17 '22 19:10

Anton vBR


You are asking for the algorithmic complexity of this function. To find that you need to see what is happening at each step.

You are scanning the list one at a time, which takes 1 unit of work. This is because retrieving something from a list is O(1). If you know the index, it can be retrieved in 1 operation.

The list to which you are going to add it increases at worst case 1 at a time. So at any point in time, the unique items list is going to be of size n.

Now, to add the item you picked to the unique items list is going to take n work in the worst case. Because we have to scan each item to decide that.

So if you sum up the total work in each step, it would be 1 + 2 + 3 + 4 + 5 + ... n which is n (n + 1) / 2. So if you have a million items, you can just find that by applying n = million in the formula.


This is not entirely true because of how list works. But theoretically, it would help to visualize this way.

like image 1
Nishant Avatar answered Oct 17 '22 19:10

Nishant


to answer the question in the title: python has more efficient data types but the list() object is just a plain array, if you want a more efficient way to search values you can use dict() which uses a hash of the object stored to insert it into a tree which i assume is what you were thinking of when you mentioned "a quicker process".

as to the second code snippet: list().append() inserts whatever value you give it to the end of the list, i for i in list if i not in new_list is a generator object and it inserts that generator as an object into the array, list().extend() does what you want: it takes in an iterable and appends all of its elements to the list

like image 1
AntiMatterDynamite Avatar answered Oct 17 '22 21:10

AntiMatterDynamite