### Set - 3

Question 1 :

What's a negative index?

Python sequences are indexed with positive numbers and negative numbers. For positive numbers 0 is the first index 1 is the second index and so forth. For negative indices -1 is the last index and -2 is the penultimate (next to last) index and so forth. Think of seq[-n] as the same as seq[len(seq)-n].
Using negative indices can be very convenient. For example S[:-1] is all of the string except for its last character, which is useful for removing the trailing newline from a string.

Question 2 :

How do I iterate over a sequence in reverse order?

If it is a list, the fastest solution is

list.reverse()
try:
for x in list:
"do something with x"
finally:
list.reverse()

This has the disadvantage that while you are in the loop, the list is temporarily reversed. If you don't like this, you can make a copy. This appears expensive but is actually faster than other solutions:

rev = list[:]
rev.reverse()
for x in rev:
<do something with x>

If it's not a list, a more general but slower solution is:

for i in range(len(sequence)-1, -1, -1):
x = sequence[i]
<do something with x>

A more elegant solution, is to define a class which acts as a sequence and yields the elements in reverse order (solution due to Steve Majewski):

class Rev:
def __init__(self, seq):
self.forw = seq
def __len__(self):
return len(self.forw)
def __getitem__(self, i):
return self.forw[-(i + 1)]

You can now simply write:

for x in Rev(list):
<do something with x>

Unfortunately, this solution is slowest of all, due to the method call overhead.

With Python 2.3, you can use an extended slice syntax:

for x in sequence[::-1]:
<do something with x>

Question 3 :

How do you remove duplicates from a list?

If you don't mind reordering the list, sort it and then scan from the end of the list, deleting duplicates as you go:
if List:

``````List.sort()
last = List[-1]
for i in range(len(List)-2, -1, -1):
if last==List[i]: del List[i]
else: last=List[i]``````

If all elements of the list may be used as dictionary keys (i.e. they are all hash able) this is often faster

``````d = {}
for x in List: d[x]=x
List = d.values()``````

Question 4 :

How do you make an array in Python?

Use a list:

``["this", 1, "is", "an", "array"]``

Lists are equivalent to C or Pascal arrays in their time complexity; the primary difference is that a Python list can contain objects of many different types.
The array module also provides methods for creating arrays of fixed types with compact representations, but they are slower to index than lists. Also note that the Numeric extensions and others define array-like structures with various characteristics as well.
To get Lisp-style linked lists, you can emulate cons cells using tuples:

``lisp_list = ("like", ("this", ("example", None) ) ) ``

If mutability is desired, you could use lists instead of tuples. Here the analogue of lisp car is lisp_list[0] and the analogue of cdr is lisp_list[1]. Only do this if you're sure you really need to, because it's usually a lot slower than using Python lists.

Question 5 :

How do I create a multidimensional list?

You probably tried to make a multidimensional array like this:

``A = [[None] * 2] * 3 ``

This looks correct if you print it:

``````>>> A
[[None, None], [None, None], [None, None]]``````

But when you assign a value, it shows up in multiple places:

``````>>> A[0][0] = 5
>>> A
[[5, None], [5, None], [5, None]]``````

The reason is that replicating a list with * doesn't create copies, it only creates references to the existing objects. The *3 creates a list containing 3 references to the same list of length two. Changes to one row will show in all rows, which is almost certainly not what you want.

The suggested approach is to create a list of the desired length first and then fill in each element with a newly created list:

``````A = [None]*3
for i in range(3):
A[i] = [None] * 2``````

This generates a list containing 3 different lists of length two. You can also use a list comprehension:

``````w,h = 2,3
A = [ [None]*w for i in range(h) ]``````

Or, you can use an extension that provides a matrix datatype; Numeric Python is the best known.

Question 6 :

How do I apply a method to a sequence of objects?

Use a list comprehension:

``result = [obj.method() for obj in List]``

More generically, you can try the following function:

``````def method_map(objects, method, arguments):
"""method_map([a,b], "meth", (1,2)) gives [a.meth(1,2), b.meth(1,2)]"""
nobjects = len(objects)
methods = map(getattr, objects, [method]*nobjects)
return map(apply, methods, [arguments]*nobjects)``````

Question 7 :

I want to do a complicated sort: can you do a Schwartzman Transform in Python?

Yes, it's quite simple with list comprehensions.
The technique, attributed to Randal Schwartz of the Perl community, sorts the elements of a list by a metric which maps each element to its "sort value". To sort a list of strings by their uppercase values:

``````tmp1 = [ (x.upper(), x) for x in L ] # Schwartzman transform
tmp1.sort()
Usorted = [ x[1] for x in tmp1 ]``````

To sort by the integer value of a subfield extending from positions 10-15 in each string:

``````tmp2 = [ (int(s[10:15]), s) for s in L ] # Schwartzman transform
tmp2.sort()
Isorted = [ x[1] for x in tmp2 ]``````

Note that Isorted may also be computed by

``````def intfield(s):
return int(s[10:15])
def Icmp(s1, s2):
return cmp(intfield(s1), intfield(s2))
Isorted = L[:]
Isorted.sort(Icmp)``````

but since this method calls intfield() many times for each element of L, it is slower than the Schwartzman Transform.

Question 8 :

How can I sort one list by values from another list?

Merge them into a single list of tuples, sort the resulting list, and then pick out the element you want.

``````>>> list1 = ["what", "I'm", "sorting", "by"]
>>> list2 = ["something", "else", "to", "sort"]
>>> pairs = zip(list1, list2)
>>> pairs

[('what', 'something'), ("I'm", 'else'), ('sorting', 'to'), ('by', 'sort')] >>> pairs.sort()
>>> result = [ x[1] for x in pairs ]
>>> result
['else', 'sort', 'to', 'something']``````

An alternative for the last step is:

result = []
for p in pairs: result.append(p[1])
If you find this more legible, you might prefer to use this instead of the final list comprehension. However, it is almost twice as slow for long lists. Why? First, the append() operation has to reallocate memory, and while it uses some tricks to avoid doing that each time, it still has to do it occasionally, and that costs quite a bit. Second, the expression "result.append" requires an extra attribute lookup, and third, there's a speed reduction from having to make all those function calls.

Question 9 :

What is a class?

A class is the particular object type created by executing a class statement. Class objects are used as templates to create instance objects, which embody both the data (attributes) and code (methods) specific to a datatype.

A class can be based on one or more other classes, called its base class(es). It then inherits the attributes and methods of its base classes. This allows an object model to be successively refined by inheritance. You might have a generic Mailbox class that provides basic accessor methods for a mailbox, and subclasses such as MboxMailbox, MaildirMailbox, OutlookMailbox that handle various specific mailbox formats.

Question 10 :

What is a method?

``````def meth (self, arg):