0

Just doing a review of my Python class and noticed that I forgot how to do this.

def outsideIn2(lst):

'''(list)->list

Returns a new list where the middle two elements have been
removed and placed at the beginning of the result. Assume all lists are an even
length

>>> outsideIn2(['C','a','r','t','o','n']) 
['r','t','C','a','o','n'] # rt moves to front
>>> outsideIn2(['H','i']) 
['H','i'] # Hi moves to front so output remains the same.
>>> outsideIn2(['B','a','r','b','a','r','a',' ','A','n','n','e']) 
['r','a','B','a','r','b,','a',' ','A','n','n','e'] # ra moves to front.
'''
length = len(lst)
middle1 = lst.pop((len(lst) / 2) - 1)
middle2 = lst.pop((len(lst) / 2) + 1)

lst.insert([0], middle1)
lst.insert([1], middle2)                  

return lst

I'm getting this error:

middle1 = lst.pop((len(lst) / 2) - 1)

TypeError: integer argument expected, got float

What am I doing wrong?

Community
  • 1
  • 1
jlee
  • 2,835
  • 3
  • 16
  • 16
  • 1
    As a side note, after you pop `len(lst)/2-1`, everything else shifts up one slot. It makes your math a lot easier if you pop the higher one first. Or, alternatively, if you pop and reinsert one before popping and reinserting the other (because popping `len(lst)/2-1` and inserting `0` leaves the other one in its original place). – abarnert Dec 12 '13 at 20:56

4 Answers4

5

When you upgraded to Python 3, the '/' operator changed from giving you integer division to real division. Switch to '//' operator.

mgilson
  • 300,191
  • 65
  • 633
  • 696
PaulMcG
  • 62,419
  • 16
  • 94
  • 130
  • 2
    One very minor quibble: Python explicitly calls its type `float` rather than `real` to hammer home the fact that it's not _really_ a real number, but a floating-point base-2 rational, and it calls `/` "true division" rather than "real" or "float" division. – abarnert Dec 12 '13 at 21:02
1

You can use // operator:

middle1 = lst.pop((len(lst) // 2) - 1)
ndpu
  • 22,225
  • 6
  • 54
  • 69
1

The other answers explained why you are getting the error. You need to use // instead of / (also, just for the record, you need to give list.insert integers, not lists).


However, I'd like to suggest a different approach that uses Explain Python's slice notation:

def outsideIn2(lst):
    x = len(lst)//2
    return lst[x-1:x+1]+lst[:x-1]+lst[x+1:]

This method should be significantly faster than usinglist.pop and list.insert.

As proof, I made the below script to compare the two methods with timeit.timeit:

from timeit import timeit

def outsideIn2(lst):

    length = len(lst)
    middle1 = lst.pop((len(lst) // 2) - 1)
    middle2 = lst.pop((len(lst) // 2) + 1)

    lst.insert(0, middle1)
    lst.insert(1, middle2)

    return lst

print(timeit("outsideIn2(['B','a','r','b','a','r','a',' ','A','n','n','e'])", "from __main__ import outsideIn2"))

def outsideIn2(lst):
     x = len(lst)//2
     return lst[x-1:x+1]+lst[:x-1]+lst[x+1:]

print(timeit("outsideIn2(['B','a','r','b','a','r','a',' ','A','n','n','e'])", "from __main__ import outsideIn2"))

The results were as follows:

6.255111473664949
4.465956427423038

As you can see, my proposed method was ~2 seconds faster. However, you can run more tests if you would like to validate mine.

Community
  • 1
  • 1
  • You took the words right out of my brain! I thought this would be faster, but timeit proves it, nice! – PaulMcG Dec 12 '13 at 21:32
  • 1
    @PaulMcGuire - Thank you. Unfortunately for you however, my method is faster than yours for some reason. ^_^ You can see for yourself by copying the test script I made and replacing the OP's function with yours. I bet you you could fix this though by putting the last two lines of yours into one so that you don't make a tuple. –  Dec 12 '13 at 21:36
  • @PaulMcGuire - Yup, that worked. Putting those two lines into one makes our solutions neck and neck for speed. –  Dec 12 '13 at 21:38
0

Using pop and insert (especially inserting at positions 0 and 1) can be fairly slow with Python lists. Since the underlying storage for the list is an array, inserting at position 0 means that the element at position n-1 has to be moved to position n, then the element at n-2 has to be moved to n-1 and so on. pop has to do the same in reverse. So imagine in your little method how many element moves must be done. Roughly:

pop #1 - move n/2 elements
pop #2 - move n/2 elements
insert 0 - move n elements
insert 1 - move n elements

So approximately 3n moves are done in this code.

Breaking the list into 3 slices and reassembling a new list may be more optimal:

def outsideIn2(lst):
    midstart = len(lst)//2-1
    left,mid,right = lst[0:midstart], lst[midstart:midstart+2], lst[midstart+2:]
    return mid+left+right

Plus you won't run into any weird issues by pop changing the length of the list between the first and second call to pop. And the slices implicitly guard against index errors when you get a list that is shorter than 2 characters.

PaulMcG
  • 62,419
  • 16
  • 94
  • 130