Objects & Procedures: A Python and Java Comparison

Earlier today, I had a discussion with another hackNY fellow about whether Java was more strictly object-oriented than Python. While neither language is truly object-oriented in the pure sense, it’s clear to me that Python is hands-down more consistently object-oriented, whereas Java is a mixture of object-oriented and procedural programming.

Put another way:

Java forces you to write class-based code using both objects and non-objects, requiring you to juggle both objects and non-objects simultaneously.

Python forces you to write object-based code, so you only need to handle objects, ever.

Like a good citizen of the OO world, Python makes objects so transparent and seamless that you don’t even realize that basic calls like addition involve objects.

a = 5
a += 2

As we shall see, Python lets you break out of OO strictness, but only if you really, really want to (which means you also really know what you’re doing.). Python also lets you take advantage of this object-oriented abstraction, by redefining integer addition on odd numbers to perform division instead (for example) – but again, only if you know what you’re doing!

(Python’s type system is a bit complex, so I will have to gloss over some of the details here. If you’re interested in more, I would recommend reading this StackOverflow question about metaclasses in Python and the book Python Types and Objects (available for free online). If anybody know similarly good resources for Java, I will be happy to add them here.)

Object-Oriented programming is just functional programming in disguise

Yes, you heard me! And just so you don’t take my word for it, here’s a 2003 email from Alan Kay (the ‘inventor’ of object-orientation). He closes with the essence:

OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things. It can be done in Smalltalk and in LISP. There are possibly other systems in which this is possible, but I’m not aware of them.

(Note that he does not mention inheritance, classes, or polymorphism – having these does not imply object-orientation!

Also note that this email is from 2003, around the height of Java’s popularity.)

Understanding Messages

This is the most important one. You can spend a lot of time studying messaging, but I’m going to cut to the chase and tell you that (spoiler alert!) messages is basically just currying.

In the Smalltalk sense, you have an object, and you can pass messages to it. The result will be another value, which may be another object that you want to pass messages to.

This is no different from functional programming; a method is a higher-order message which takes other parameters (messages) and returns values. Calling a method and providing a parameter to a method are both just messages.

Understanding local retention, protection, and hiding

This could be a blog post in itself, but it’s essentially a comment on implementing encapsulation using scope and closure. An oversimplification: closures are a way to ‘export’ access to local variables while still maintaining control over them (ie, how they can be access/modified, and by whom).

Understanding late binding

This is the sticking point. Python is dynamically typed and uses duck typing. In other words, the following call:

$ foo.bar

does not need to know the type of ‘foo’ – all it does is look up the list of attributes that ‘foo’ has and looks for something called ‘bar’. You can do this manually with the getattr() function or the __dict__ attribute. If that attribute is a callable (a function), you can also do

$ foo.bar()

or

$ foo.bar(5)

This is more or less the same as passing foo the message ‘bar’, then passing the message ’(’ telling it to access the hidden ’__call__’ attribute, then passing it the message ‘5’ and the message ’)’ telling it to end the call.

What happens if ‘bar’ is not found? This is where things get interesting:

If an attribute is not found in the object, it then looks up the attribute in the class. But classes are objects in Python, so it’s looking up the attribute in another object. This is because Python classes are really just syntactic sugar for prototypical inheritance.
If getattr() doesn’t see the attribute listed in the dictionary of attributes, it then looks up the .__class__ attribute and then looks in the __dict__ associated with that. (The docs provide more precise information).

Don’t be fooled by the name .__class__ – this doesn’t actually have to be a class (at least, not the way you’re probably thinking of a ‘class’!).

This means you can do wacky things like this:

(I’d encourage you to experiment with this yourself! Try modifying the attributes).

In other words, “class Foo(object):” is syntactic sugar that creates an object of class ‘type’ with a ’__name__’ attribute of  ‘Foo’, then binds it to the external variable name Foo. We could do this without a ‘class’ keyword altogether, by writing:

Foo = type(‘Foo’, (object,), {‘apple’ : ‘red’})

Note that the external name and the ’__name__’ attribute are completely independent. 

(In fact, not only can Python objects be created without the ‘class’ keyword, they don’t even need to have classes, as long as they are capable of handling all of the messages that an object with a class can.)

Note: the reason this is possible is not that Python is a dynamic language; the reason this is possible is that Python objects don’t ‘inherit’ from classes; they simply contain a ’__class__’ attribute that is a reference to another object, which can be accessed, mutated, and referenced just like any object.

Objects all the way down…. almost

What happens when you get to the prototype of a prototype of an object (or equivalently, the class of a class of an object)?

To avoid getting into higher-order type theory, I’m going to wave my hands a bit and point you towards the concept of a kind; that is, the type of a type constructor.

It gets down to this: all of Python can essentially be derived from one kind – if you follow the turtles (prototypes) all the way down, you’ll eventually reach some common ground. And you don’t even have to go that far to see the benefits – you can access attributes (functions and values) of objects, classes, and values interchangeably, because they’re all objects. Classes included.

Which brings us to Java….

Everything is an object (except if it’s not)

Java fails to be an object-oriented language for one reason: In Java, all objects have a class, but not all typed values have classes, so some values can have a type without being an object.

This is another way of saying that Java supports (and requires) multiple kinds. This is illustrated very easily by the need for the object autoboxing for primitive types. The following code will fail with an interesting error code:

because an int is not an object. You need to provide an Integer, which behaves the same most of the time…. except when it doesn’t. Many people incorrectly refer to Integer as a ‘wrapper object’, but while it’s not a primitive, it’s not an object either; it can be used interchangeably with objects 98% of the time, but not the remaining 2%.

That 2%, ‘except when it doesn’t’ seems small, but it’s the key to understanding why Java is not actually object-oriented. It’s not quite class-oriented either, but it’s more class-oriented than object-oriented, because you rarely (never?) create objects directly; instead, you create classes (named or anonymous) which you later instantiate as objects (sometimes only once).

The fact that Java is (almost) class-oriented doesn’t make it any more object-oriented; in fact, it makes Java less object-oriented, because classes prevent many of the abstraction properties that make OO programming powerful.

If there’s interest, I could illustrate some of those limitations in Java specifically, though for now, I think I’ll save that for a separate post!

Leave a Reply