Python

An interpreted language.

Well!! That sounds like an oversimplification, but..

How a python interpreter workswhat that means is, an interpreter software takes the command input to it and after evaluation return the results. The software is known as Python interpreter and the set of commands is stored in the form of plain text file with extension .py for ex. hello_world.py.

Python provides an IDE (called IDLE) with basic functionality like syntax-highlighting , smart-indent, auto-completion (not intellisense) for quick editing and running the code script written in python.

IDLE IDE by Python

Python program preview:

Object Orientation in Python:

Being an object oriented language, one of the key-aspects include classes and their objects. Here is an example:

If it went straight over your head, then let me first introduce the syntax here:-

Key syntax for implementing a class in Python is:

Line 1: just like C++ and JAVA the keyword class  is used to declare a class and here the name of the class is A

NOTE: even if you don’t use a ‘ () ‘ parentheses as a suffix to class name, it will not give you any error.

Line 2: Declares a variable named something_something_variable

You might ask “of what type ?” Being a dynamically typed language, you don’t need to specify the type of variable in python. So, it is evident that if you enclose your text inside ‘ ‘ ‘(single quotes) ‘ or a ‘ “”(double quotes) ‘ then the variable is considered holding a string value.

Declared inside a class, this variable is also known as “instance attribute” because it is owned by specific instances of a class.

Line 3: I have declared a method that begins with keyword def  proceeded by its name something_function  (more about it later on).

Line 4: a print()  statement ;  Line 5: just left blank

Line 6: ob = A() here we instantiated an object of type A

Line 7: took the object of type A to print the value of instance attribute  something_something_variable

And, now lets get back to the code you might have had hard time going through (I’ll break it into chunks for better readability)

 

and if you have declared a method inside a class then the argument must containself“.

What is self here again??

So, just like JAVA has  this keyword, telling you the specific object created from that class. Similarily, self   is a reference to the parent class.

 

 

Grouping

You have a list of number suppose like the one given below:

[64630, 11735, 73429, 67060, 73429, 4978, 73429, 38120, 4978, 67060]

You want to group them in the following manner:

{38120: [1],

4978: [2],

67060: [2,],

73429: [3],

64630: [1],

11735: [1]}

Python has multiple ways to achieve this goal like using a dictionary of lists  or Counter() (which we will come to later)  but first lets see how grouping can be useful.

## Arrange names in a list according to their length

names = ['mark', 'henry', 'matthew', 'paul',
'luke', 'robert', 'joseph', 'carl', 'michael']

names = {4: ['mark', 'paul', 'luke', 'carl'],

5: ['henry'],

6: ['robert', 'joseph'],

7: ['matthew', 'michael']}

[{‘first’: ‘mary’, ‘last’:’smith’}, {‘first’: ‘mark’, ‘last’: ‘davis’}]

d = {}
for name in names:
key = len(name)
if key not in d:
d[key] = []
d[key].append(name)

# result: d = {4: [‘mark’, ‘paul’, ‘luke’, ‘carl’],
# 5: [‘henry’], 6: [‘robert’, ‘joseph’], 7: [‘matthew’, ‘michael’]}

So that is a nice start, loop over the data, create a key value for each. Then we do a check to see if it already there or not. This here is the best way to check if a key is in a dictionary. The alternatives is either doing a try except around a key look up, which is really ugly and slow. Or checking the return value of d.get(key), but that prevents putting None values in the dictionary.

There is a down side to this, and that is there the key has to be hashed two or three times (python dictionaries are internally a kind of hash map, that gives them their almost linear lookup time). First in the if statement, and a possible second in the assignment to a empty list() and finally in the lookup for the append. That python has to hash the value has an overhead. So how might we do better? The following is one possible better solution.


d = {}
for name in names:
key = len(name)
d.setdefault(key, []).append(name)

This uses the setdefault() method. This is a function, that even the developers of Python admit freely is confusingly named. The problem is any descriptive alternatives look like do_a_get_lookup_but_if_not_found_assign_the_second_argument(). So more or less, the same code as we wrote ourselves before, but since it is done by the dictionary itself, the key is only hashed once. It will be faster when we have lots of values.

This is still not the best code that we could do, there is a nicer way. It involves using a data structure called defaultdict that lives in the collections module. If you have not checked out the collections module recently, I recommend you read its docs, there are a number of very useful utilities in it. With that aside, defaultdict lets us create a dictionary like object that is different only in that if a lookup fails, it uses the argument passed to it during creation (in our case list) to fill that key in. It lets us now write code like this:


from collections import defaultdict

d = defaultdict(list)
for name in names:
key = len(name)
d[key].append(name)

So now we can just look up the key and append to it, not worrying about if it exists or not. If it does not, the defaultdict will create the value for us.

Counting

Now we have mastered grouping, counting should be simple. We just have to know that int() when called returns the value 0, so that can be passed to defaultdict. So here we have:


from collections import defaultdict

d = defaultdict(int)
for name in names:
key = len(name)
d[key] += 1

Here a common use case is not even to use a key, but to count just the number of times something appears. In that case, we could do the following simplified version.


from collections import defaultdict

names = [“mark”, “john”, “mark”, “fred”, “paul”, “john”]

d = defaultdict(int)
for name in names:
d[name] += 1

#result: d = {‘mark’: 2, ‘john’: 2, ‘fred’: 1, ‘paul’: 1}

This was considered common enough that there is even a built in way to do this using Counter, so the above can be reduced to this.


from collections import Counter
names = ["mark", "john", "mark", "fred", "paul", "john"]
d = Counter(names)

Counter comes with some nice little extras, such as being able to add, or subtract results. So we could do something like the following.


from collections import Counter
boys = ["mark", "john", "mark", "fred", "paul", "john"]
girls = ["mary", "joan", "joan", "emma", "mary"]
b = Counter(boys)
g = Counter(girls)
c = b + g
#result: c = Counter({'mark': 2, 'joan': 2, 'john': 2, 'mary': 2, 'fred': 1, 'paul': 1, 'emma': 1})

But what happens if you want to use Counter but need to pass the result though some key function first? How would you do it? The solution would be to put a generator inside of it like the following.


from collections import Counter
names = ['mark', 'henry', 'matthew', 'paul',
'luke', 'robert', 'joseph', 'carl', 'michael']
d = Counter(len(name) for name in names)

Useful key functions

Some possible common cases when grouping or counting, is you might want to do so based on some item in or attribute of the items you are grouping. So for examples, your data might be a tuple of first and last names, or a dictionaries with first and last name keys, or a class with first and last name attributes. If that is what you group or count by, there are two built in functions that can help do this, without needing to write our own functions. Those are itemgetter() and attrgetter from the operator module. Some examples might help.

 

Bonus

When I was studying Software Engineering I got a job tutoring for the first year programming course, which was in python and had 200-300 students depending on semester (hence the need for tutors to help with questions during practicals). One the challengers some of more curious students used to ask, is how I would do certain things in one line (I ended up doing their whole first in a single 1500 character line). Often really bad code, but also often rather interesting trying to reduce a problem to a single statement. I had a shot at doing it for this, and this was the solution that I came up with in a few minutes. I leave working out how it works as an exercise to the reader. I would never use it in production code.

 

Python Tutorial: Understanding Python MRO – Class search path: mEtHoD rEsOlUtIoN OrDeR aLgO.

Python Tutorial: Understanding Python MRO – Class search path