Sunday 6 April 2014

Classism in Classes

Here's an interesting problem I came upon while playing around with classes.

Let's say I have a class A, and I have a method that takes an object of class A and returns an instance of itself. That'll look something like this:

class A:

    def method(self: 'A') -> 'A':
        return A() 

So that's cute and all, but what if I want to create a class B that inherits all the methods of class A?

class B(A):
    pass

Turns out when I call method() of an object B, it'll return an instance of an object of class A, not B. That's pretty obvious if you look at the code for method(). So what can be done to method() in A so that I can have an instance of B returned when I use method() on B?
Well, after some googling I found a suggestion, to replace the return statement of method() with:

        return self.__init__()

Unfortunately, that won't work. Because all I'm doing is initialising self again. Something I would've already done before I used method(). What I need is to create a new instance, not re-initialise an existing instance.

There is also another suggestion, again changing the return statement to:

        return self.class.__name__.__init__()

This is still wrong. The object self.class.__name__() is a string containing the name of the class. But wait, that's sort of close to what we want right? Just that we don't a string, but an instance of the class itself.

        return self.class.__init__()

There we go. This beauty here will work. It calls on the class of self and initialises another object of the same class. And this does support the inheritance by B. The fact is, when it comes to inheritance, there should be an easy way to avoid replicating code from a superclass to a subclass. If not, then what's the point of inheritance?

Honestly, we could have worked with self.class.__name__(). We could've somehow extracted the class name from the string and use it to initialise a new instance.
And honestly, we could've just copied the entire method(), pasted it in B() and just change the return statement to B() instead of A().
So why didn't we? The simple answer is that I'm just too goddamn lazy. It's too tedious and complicated to workaround with those simple solutions. I'd rather take the time to search for an elegant solution that will straight up work exactly as I want it to.

I think that's one of the most important lessons we can take away as students of computer science. That it isn't about just getting your code to work; it's about how it works.

Wednesday 5 March 2014

Cutting down Trees.... with ANARCHY!!, erm, I mean, Recursion!

Just had a midterm last week. One of the questions presents a useful opportunity to talk about recursion and what it's all about.
Recursion is more or less the programming equivalent of a fractal. Basically it's a function that calls on itself until it eventually(hopefully) converges on a base case. This is an especially useful technique when working with objects that refer to themselves. So before we look at a recursive function, let's look at a recursive data structure, Trees.

class Tree: 
    """Bare-bones Tree ADT"""
    
    def __init__(self: 'Tree', value: object =None, children: list =None):
        """Create a node with value and any number of children"""
        
        self.value = value
        
        if not children:
            self.children = []
        else: 
            self.children = children[:] # quick-n-dirty copy of list

    def __repr__(self: 'Tree') -> str:
        """Return representation of Tree as a string"""

        return ('Tree(' + repr(self.value) + ', ' + repr(self.children) + ')') 


Trees are a class of objects with a node that contains a value. And like normal trees, Trees can branch out to other nodes that contain their own value. These branches are called children and they are a list of nodes i.e. Tree objects.

Beside is a pictorial representation of a Tree. In code, this Tree would be represented as:

Tree(2, [Tree(7, [Tree(2), Tree(6, [Tree(5), Tree(11)])]),
         Tree(5, [Tree(9, [Tree(4)])])])

If this looks like a nightmare to read, then rest assured, many feel the same way. Lists of objects within lists of objects within objects. All referring to the same data structure. Hence the need for recursion if we wish to manipulate Trees.

So let's say I wish to check if all the nodes in a Tree had the same given value.
I would build a recursive function like this:

def check_all(t: 'Tree', value: object =None, c: int =0) -> bool:
    """
    Returns True if and only if all nodes in Tree object have the same given value.
    If no value is given, then returns True if and only if all nodes in Tree object have the     same value.
    """   

    return (True if (value is None and not c)
            else t.value == value) and all([check_all(x, t.value, 1) for x in t.children]) 

Notice how I call the function check_all inside of the return statement itself? That's recursion.
Let's say, I want to check if all the nodes in a Tree have a value of 5.  I would check if the value of the individual node of the Tree was indeed 5 using the highlighted code, then I would check the children to see if the value of the other nodes that branched off were 5.

How do I check the children? Well, I'd call on check_all again but this time applying it to each individual node in the list, and in doing so, I'm running the same highlighted code on each node. And once I've checked all the nodes that were branched off, then I can conclude if all the nodes possess the same given value of 5.
In this example, you can say the highlighted code was the base case where the function eventually converged on.

If it's not too clear yet, just let it sink in, maybe use a few examples and see how it goes. The best way to learn recursion is by trusting where the code goes and having a good sense of mathematical intuition.

It is therefore with great dismay that I admit to a lack of either qualities..... And yes, I know my code isn't completely correct. There is a special case where it fails. See if you can spot it.

Tuesday 28 January 2014

Class-ception

A friend posed an interesting question the other day.
Is it possible to create a class within a class in Python?
Honestly, I had no idea. It might be possible but what would I gain from doing so. Just off the the top of my head, I can imagine the code looking like this:

class Shape:

    def __init__(self):
        ...
    ...
    class Square:

        def__init__(self):
            ...
        ...

And I can imagine that you'd have to input Shape.Square() to create a Square object, which essentially seems the same as a method or a module.

Of course the true scientist would explore this possibility.
And turns out, you can create a class in a class. I will now coin the term describing such a practice as class-ception.
It also turns out that this is particularly useless; the only reason anyone might do this is because they only need their nested class for this one program and it doesn't require variables outside of the nested class.

What do I mean? Well, taking the example above, you might think that any variable defined in Shape or Square exists in the counterpart's namespace. It doesn't. You can't call Shape methods if the object is a Square and to call Square methods when the object is a Shape requires the Square prefix. So basically it has all the limitations of a module plus a bit of added confusion.

Moral of story: Don't bother nesting classes in Python.

Sunday 26 January 2014

Commence Object Instatiation

Object-oriented programming sounds more ominous than it actually is. With terms like polymorphism, encapsulation and instantiation, it's not hard to see why people get turned off from computers and programming. Frankly, I don't really understand all those terms, but I do have words that I use to describe what OOP is to me. (And hopefully, others understand how I use them)

From the Wikipedia entry of Object-Oriented Programming:

Object-oriented programming (OOP) is a programming paradigm that represents concepts as "objects" that have data fields (attributes that describe the object) and associated procedures known as methods.

If looking at the definition in the box doesn't really explain things to you, then that means you suffer from the affliction known as "Conversational English". Don't worry, it's a condition that afflicts many people, myself included. I have to make a constant effort to adapt my condition to the technical jargon that people expect me to know. It's not easy but with adequate support, I'm sure you can overcome it too.

Basically, a long long time ago, programmers used to do what is called Procedural Programming. That means that they would put in a programme that commanded the computer to do things in sequence. So a typical programme would look like this:

1. Do this action on this file
2. Do this on that
3. Do this on that....


It's like your mother telling you to your chores or to sit up right or to behave, with each instruction coming one after the other.

Then one day but still pretty long ago, programmers started to switch from thinking of code as instructions to thinking of them as representing objects. Instead of writing instructions for the computer to perform, programmers created objects and told the computer what can be done to the object. For example, I'm a programmer who wants to create a ball in my computer and use it like how I would use a ball in real life so my programme would look like this:

Create Ball:
    Ball is...
    bouncy, round, smooth, filled with air

    Ball can...
    bounce

    Ball can...
    roll

    Ball can...
    deflate


First I created the object Ball. Then, I assigned "attributes" (bouncy, round, smooth, filled with air) to Ball. After I set out what the Ball can do (bounce, roll, deflate), these actions are called "methods".

The idea behind OOP is to create a whole bunch of objects that you might want to make use of. And then tell the computer how to use them. Which in some ways can make life a lot easier for programmers, because a lot of programmes use the same methods on the same pieces of data so instead of telling the computer every single thing it has to do, you could just build an object, set some rules to it and tell the computer to do the same thing over again.

It's a neat idea. But let's face it, there will be times when we're going to find that it's much easier to tell people what to do. If I encounter one of those times, I'll try to have it up on this blog as an example.

Thursday 23 January 2014

SLOG Post 1

Let it be stated for the record that this SLOG was created as an assignment for CSC148. That's a first-year computer science course at the University of Toronto. The purpose is to write about my experience in programming, so this should be fun...