Python: 2 Index Location Brackets for a List - python

list1 = [1, 2, 3, 4]
element = list1[1:][1]
print(element)
Why does it print 3?
How is this evaluated? Does it first take the elements of list1 that are from index 1: then it takes the 1 index of that?

Python's grammar specifies how it is evaluated, since it constructs a syntax tree of your program.
Without going much into technical detail, such indexing is left recursive. This means that:
foo[bar][qux]
is short for:
(foo[bar])[qux]
so such indices are evaluated left-to-right.
It is evaluated like:
list1 = [1, 2, 3, 4]
temp = list1[1:] # create a sublist starting from the second item (index is 1)
element = temp[1] # obtain the second item of temp (index is 1)
(of course in reality, no temp variable is created, but the list itself is a real object stored in memory, and thus also might change state, etc.).
So first we slice starting from the second item, this results in a list [2, 3, 4], and then we obtain the second item of that list, so 3.

Related

How remove() function was executed in python?

Actually I was working with this code:
m=[0,1,2,3,4,5,6,7]
for i in m:
m.remove(i)
print(m)
This looks pretty simple.
for i in m:
print(i)
will print every elements in the list m. So the for part in our code will extract every elements of the list and remove it one by one. So the output that I except is [].
But I got [1,3,5,7].
How it can be the output and where I was wrong?
when you use (for i in m), i starts from index zero so i is the element of index 0 which its value is 0 then in for loop you'll delete zero value.
now your list is [1,2,3,4,5,6,7] and i is the element of index 1 which its value is 2.
after deleting 2 your list would be like [2,3,4,5,6,7], now i is the element of index 2 which its value is 4 and ....
here is how this for loop works and why the output is like that.
to remove all element in a list you can use:
for i in range(len(m)):
m.remove(m[0])
or
m.clear()
If you know how to use a debugger, that would be the ideal way to diagnose this, otherwise, try running this code to see what's going on:
x = [0, 1, 2, 3, 4, 5, 6, 7]
for i in x.copy():
x.remove(i)
print(x)
print(x)
m = [0, 1, 2, 3, 4, 5, 6, 7]
for i in m:
m.remove(i)
print(m)
print(m)
During a given execution, the for loop accesses an element and then deletes it. For example, it first deletes the element at index 0, with value 0, leaving you with [1, 2, 3, 4, 5, 6, 7]. Then it moves on to the element at index 1, value 2, and deletes that, leaving you with [1, 3, 4, 5, 6, 7].
In general, avoid messing around with a list in Python. If you have to delete all elements, use m.clear(). If you have to delete some elements or do some other stuff, prefer to use list comprehension to create a new object.
What happens here is, you're removing items in the list on the go.
Eg,
On the first remove() call, it removes 0 from the list (current
index is 0).
On the second remove() call, the current index is 1,
but the list contains [1,2,3,...]. Now the the index 1 contains "2"
instead of "1". Because 0 has been removed and the size of list is
just changed.
So to clear list in python, you can use,
m.clear()
i don`t know why it is not remove each item, but i worked on it and it works like this:
m=[0,1,2,3,4,5,6,7]
while m != []:
[m.remove(i) for i in m]
print(m)

Issues with list index after swapping first and last number in the list

I am new to Python and with the swapping exercise, I cannot swap the first and last element of the list using the following code (the line with issue noted below).
def swapList(list):
first = list.pop(0)
last = list.pop(-1)
list.insert(0, last)
list.insert(-1, first) # this line is the issue.
# It does not swap first to the last position in the list,
# but instead place it just before the last number. If I change
# it to list.append(first) then the issue is solved.
# I cannot understand why.
return list
Can you please help?
The Python documentation says this about list.insert:
list.insert(i, x)
Insert an item at a given position. The first argument is the index of the element before which to insert, so a.insert(0, x) inserts at the front of the list, and a.insert(len(a), x) is equivalent to a.append(x).
So this is what happens to the list:
>>> xs = [1, 2, 3, 4, 5]
>>> first = xs.pop()
>>>
>>> xs = [1, 2, 3, 4, 5]
>>> first = xs.pop(0)
>>> last = xs.pop(-1)
>>>
>>> xs
[2, 3, 4]
>>>
>>> xs.insert(0, first)
>>> xs
[1, 2, 3, 4]
>>> xs.insert(-1, last)
>>> xs
[1, 2, 3, 5, 4]
>>>
The index you specify is "the index of the element before which to insert", so insert(-1, 5) inserts 5 before the last element. So if you change the line to list.insert(len(list), last), your function will work as expected
However, there's a better way to do this. If you want to swap the first and the last item in the list, you can use tuple unpacking:
xs[0], xs[-1] = xs[-1], xs[0]
The insert method will insert the element to the left of the index you provide - my_list.insert(0, 1) would insert the value 1 to left of index 0 in my_list.
To add an item to the end of your list, use the append method instead: my_list.append(1).
Also, don't call your list list - list is a reserved keyword in Python and should not be used to name a variable. You could instead use my_list, or , preferably, a name that describes what the list is for/the data it contains.
list.pop removes elements from the list, making the list shorter. You can avoid this:
tmp = list[0]
list[0] = list[-1]
list[-1] = tmp
If you are required to use pop, then using insert to re-add that index to the list is needed:
tmp = list.pop(0) # Remove 0 element from list, assign to tmp
list.insert(0, list[-1]) #Shifts elements by +1 for new 0th element
list[-1] = tmp # Assign tmp to last element in list
Try this
def swaplist(list):
first = list.pop(0)
last = list.pop(-1)
list.insert(0, last)
list.append(first)
return list
list = [10 , 20 , 30 , 40 , 50 , 60]
print(swaplist(list))

Writing Python code that works like the reverse() function

I'm looking to break down the reverse() function and write it out in code for practice. I eventually figured out how to do it (step thru the original list backwards and append to the new 'reversed' list) but wondering why this doesn't work.
def reverse(list):
newlist = []
index = 0
while index < len(list):
newlist[index] = list[(len(list)) - 1 - index]
index = index + 1
return newlist
list = [1, 2, 3, 4, 5]
print(reverse(list))
In Python, you cannot access/update an element of a list, if the index is not in the range of 0 and length of the list - 1.
In your case, you are trying to assign to element at 0, but the list is empty. So, it doesn't have index 0. That is why it fails with the error,
IndexError: list assignment index out of range
Instead, you can use append function, like this
newlist.append(list[(len(list)) - 1 - index])
Apart from that, you can use range function to count backwards like this
for index in range(len(list) - 1, -1, -1):
newlist.append(list[index])
you don't even have to increment the index yourself, for loop takes care of it.
As suggested by #abarnert, you can actually iterate the list and add the elements at the beginning every time, like this
>>> def reverse(mylist):
... result = []
... for item in mylist:
... result.insert(0, item)
... return result
...
>>> reverse([1, 2, 3, 4, 5])
[5, 4, 3, 2, 1]
If you want to create a new reversed list, you may not have to write a function on your own, instead you can use the slicing notation to create a new reversed list, like this
>>> mylist = [1, 2, 3, 4, 5]
>>> mylist[::-1]
[5, 4, 3, 2, 1]
but this doesn't change the original object.
>>> mylist = [1, 2, 3, 4, 5]
>>> mylist[::-1]
[5, 4, 3, 2, 1]
>>> mylist
[1, 2, 3, 4, 5]
if you want to change the original object, just assign the slice back to the slice of the original object, like this
>>> mylist
[1, 2, 3, 4, 5]
>>> mylist[:] = mylist[::-1]
>>> mylist
[5, 4, 3, 2, 1]
Note: reversed actually returns a reverse iterator object, not a list. So, it doesn't build the entire list reversed. Instead it returns elements one by one when iterated with next protocol.
>>> reversed([1, 2, 3, 4, 5])
<list_reverseiterator object at 0x7fdc118ba978>
>>> for item in reversed([1, 2, 3, 4, 5]):
... print(item)
...
...
5
4
3
2
1
So, you might want to make it a generator function, like this
>>> def reverse(mylist):
... for index in range(len(mylist) - 1, -1, -1):
... yield mylist[index]
...
...
>>> reverse([1, 2, 3, 4, 5])
<generator object reverse at 0x7fdc118f99d8>
So the reverse function returns a generator object. If you want a list, then you can create one with list function, like this
>>> list(reverse([1, 2, 3, 4, 5]))
[5, 4, 3, 2, 1]
if you are just going to process it one by one, then iterate it with a for loop, like this
>>> for i in reverse([1, 2, 3, 4, 5]):
... print(i)
...
...
5
4
3
2
1
First off don't override build-ins (list in your case) second newlist has a len of 0 therefore cannot be accessed by index.
def reverse(mylist):
newlist = [0] * len(mylist)
index = 0
while index < len(mylist):
newlist[index] = mylist[(len(mylist)) - 1 - index]
index = index + 1
return newlist
mylist = [1, 2, 3, 4, 5]
print(reverse(mylist))
you can create a list with values of the same lenght as your input list like so
newlist = [0] * len(mylist)
You need to use list.append. newlist[0] is a valid operation, if the list has atleast one element in it, but newlist is empty in this very first iteration. Also, list is not a good name for a variable, as there is a python builtin container with the same name:
def reverse(lst):
newlist = []
index = 0
while index < len(lst):
newlist.append(lst[(len(list)) - 1 - index])
index += 1
return newlist
list = [1, 2, 3, 4, 5]
print(reverse(list))
You can't assign to an arbitrary index for a 0-length list. Doing so raises an IndexError. Since you're assigning the elements in order, you can just do an append instead of an assignment to an index:
newlist.append(l[(len(l)) - 1 - index])
Append modifies the list and increases its length automatically.
Another way to get your original code to work would be to change the initialization of newlist so that it has sufficient length to support your index operations:
newlist = [None for _ in range(len(l))]
I would also like to note that it's not a good idea to name things after built-in types and functions. Doing so shadows the functionality of the built-ins.
To write the function you're trying to write, see thefourtheye's answer.
But that isn't how reverse works, or what it does. Instead of creating a new list, it modifies the existing list in-place.
If you think about it, that's pretty easy: just go through half the indices, for each index N, swap the Nth from the left and the Nth from the right.*
So, sticking with your existing framework:
def reverse(lst):
index = 0
while index < len(lst)/2:
lst[index], lst[len(lst) - 1 - index] = lst[len(lst) - 1 - index], lst[index]
index = index + 1
As a side note, using while loops like this is almost always a bad idea. If you want to loop over a range of numbers, just use for index in range(len(lst)):. Besides reducing three lines of code to one and making it more obvious what you're doing, it removes multiple places where you could make a simple but painful-to-debug mistake.
Also, note that in most cases, in Python, it's easier to use a negative index to mean "from the right edge" than to do the math yourself, and again it will usually remove a possible place you could easily make a painful mistake. But in this particular case, it might not actually be any less error-prone…
* You do have to make sure you think through the edge cases. It doesn't matter whether for odd lists you swap the middle element with itself or not, but just make sure you don't round the wrong way and go one element too far or too short. Which is a great opportunity to learn about how to write good unit tests…
probably check this out:
def reverse(lst):
newList = []
countList = len(lst) - 1
for x in range(countList,-1,-1):
newList.append(lst[x])
return newList
def main():
lst = [9,8,7,6,5,4,2]
print(reverse(lst))
main()

Find and remove least common element in array (Python)

I am trying to find the least common element in an array of integers and remove it, and return the list in the same order.
This is what I have done, but when the list is [1, 2, 3, 4, 5], my function should return [], but returns [2, 4] instead.
def check(data):
for i in data:
if data.count(i) <= 1:
data.remove(i)
return data
data = [1, 2, 3, 4, 5]
print check(data)
Deleting items from a list you are iterating over causes you to skip items (ie the next item following each one you delete).
Instead, make a new list containing only the values you want to keep.
from collections import Counter
def check(data):
ctr = Counter(data)
least = min(ctr.values())
return [d for d in data if ctr[d] > least]
You shouldn't modify (especially delete) elements from a list while you are iterating over it.
What happened is:
Initially the iterator is at the 1st element, i.e. i = 1
Since d.count(1) is 1, so you delete 1 from the list.
The list is now [2,3,4,5], but the iterator advances to the 2nd element which is now the 3.
Since d.count(3) is 1 you delete it making the list [2,4,5]
The iterator advances to the 3rd element which is now 5.
Again you delete the 5 making the list [2,4].
Your algorithm should:
Get a count of all elements
Find the smallest count.
Find the elements with the smallest count.
Remove the elements found in step 3 from the list.
You shouldn't check data.count(i) <= 1. What happens in this case: [1, 1, 2, 2, 3, 3, 3]? 1 and 2 are the least common elements but you will never delete them. Likewise it is a bad idea to mutate a list in a for loop.
One thing you can do is use the Counter class.
Take an appropriate slice of the tail of the most_common() method (they entries get less frequent as you go down the list, so this is why you take the tail as opposed to the head).
Then you can repeatedly search the list for these occurrences and remove them until their are no occurrences left.
one another try:
def check(data):
ctr = Counter(data)
keys = ctr.keys()
vals = ctr.values()
least = []
m = min(vals)
for i in range(0,len(vals)):
if vals[i] == m:
least.append(keys[i])
print least
data = [1, 2, 3, 4, 5,1]
result = check(data)

Why does list comprehension not filter out duplicates?

I have a workaround to the following question. That workaround would be a for loop with a test for inclusion in the output like the following:
#!/usr/bin/env python
def rem_dup(dup_list):
reduced_list = []
for val in dup_list:
if val in reduced_list:
continue
else:
reduced_list.append(val)
return reduced_list
I am asking the following question, because I am curious to see if there is a list comprehension solution.
Given the following data:
reduced_vals = []
vals = [1, 2, 3, 3, 2, 2, 4, 5, 5, 0, 0]
Why does
reduced_vals = = [x for x in vals if x not in reduced_vals]
produce the same list?
>>> reduced_vals
[1, 2, 3, 3, 2, 2, 4, 5, 5, 0, 0]
I think it has something to do with checking the output (reduced_vals) as part of an assignment to a list. I am curious, though as to the exact reason.
Thank you.
The list comprehension creates a new list, while reduced_vals points to the empty list all the time during the evaluation of the list comprehension.
The semantics of assignments in Python are: Evaluate the right-hand side and bind the resulting object to the name on the left-hand side. An assignment to a bare name never mutates any object.
By the way, you should use set() or collections.OrderedDict.fromkeys() to remove duplicates in an efficient way (depending on whether you need to preserve order or not).
You are testing against an empty list.
The expression is evaluated in full first before assigning it as the new value of reduced_vals, which thus remains empty until the full list expression has been evaluated.
To put it differently, the expression [x for x in vals if x not in reduced_vals] is executed in isolation. It might help if you view your code in a slightly modified fashion:
temp_var = [x for x in vals if x not in reduced_vals]
reduced_vals = temp_var
del temp_var
The above is the moral equivalent of directly assigning the result of the list expression to reduced_vals, but I have more clearly separated assigning the result by using a second variable.
In this line: [x for x in vals if x not in reduced_vals] there's not a single value that is not in reduced_vals, as reduced_vals is the empty list []. In other words, nothing gets filtered and all the elements in vals get returned.
If you try this:
[x for x in vals if x in reduced_vals]
The result is the empty list [], as all the values are not in reduced_vals (which is empty). I believe you have a confusion with how the filtering part works in a list comprehension: you see, the filter only selects those values which make the condition True, but it won't prevent duplicate values.
Now, if what you need is to filter out duplicates, then a list comprehension is not the right tool for the job. For that, use a set - although it won't necessarily preserve the order of the original list, it'll guarantee that the elements are unique:
vals = [1, 2, 3, 3, 2, 2, 4, 5, 5, 0, 0]
list(set(vals))
> [0, 1, 2, 3, 4, 5]
Because the elements in the list comprehension are not assigned to reduced_vals until the entire list has been constructed. Use a for loop with .append() if you want to make this work.
Because reduced_vals is not changing during evaluation of the list comprehension.

Categories

Resources