This post originated from an RSS feed registered with Python Buzz
by Dmitry Dvoinikov.
Original Post: This is Python, calling a spade a spade
Feed Title: Things That Require Further Thinking
Feed URL: http://feeds.feedburner.com/ThingsThatRequireFurtherThinking
Feed Description: Once your species has evolved language, and you have learned language, [...] and you have something to say, [...] it doesn't take much time, energy and effort to say it. The hard part of course is having something interesting to say.
-- Geoffrey Miller
Python is a high level programming language, but what does this term mean ? What does it mean for a language to be high level or low level ? Can you compare height levels of different languages ?
The meaning for the term is nebulous and there is no single or final definition. Here is one approach - the more effectively the language allows you to handle things, the higher level it is. And by things I'm not meaning just objects as in classes instances. Things, you know, everything, even if I occasionally call them objects.
Enter the notion of first-class objects. Put simply, something is called first-class object in a programming language if it can be treated just like an instance of primitive type, such as int. For example, when you declare a variable (which is a valuable feature already, to be able to declare a variable of that kind)
int i;
you then can do all sorts of things with it, such as passing it as a parameter:
foo(i);
return it as a result of function:
return i;
and do other things, depending on the language. The point is that first-class objects can be handled more effectively and provide additional flexibility. Thus, the more objects in a language are first-class, the higher level that language is.
In Python pretty much everything is first-class. I won't be digging into language reference to find whether or not it is formally true, but in practice it is just like that. It is partly because Python is an untyped language with referential variables semantics - as soon as something exists, you should be able to get a reference to it, and then, once you have a reference, you pass it around as a primitive, not caring about the nature of the object it points to. The language itself does not care what kind of an object is being referenced by the variable you pass. It is only when it comes to real work, such as access to the object's methods, it may turn out to be incompatible with the operation you throw at it. Such just-in-time type compatibility is a very old idea and is called "protocol compatibility" in Python.
Why is it good ? Because I can call a spade a spade. If I need to pass a class as a parameter, what a heck, I can do it:
def create(c, i): return c(i)
create(int, 0)
See ? Generic programming right there.
Or, why wouldn't I be able to pass in a method ?
def apply(f, x): return f(x)
def mul_by_2(x): return x * 2
print(apply(mul_by_2, 1)) # prints 2
Uhm, was it functional programming ?
One other curious and extremely useful first-class thing, which you wouldn't find in many other languages is the call arguments. Remember, I have said that before, there is no declarations in Python. Compatibility of a called function with the actually supplied arguments is checked just-in-time, just as anything else:
def foo(a): ... foo(1, 2) # this throws at runtime
But nothing stops you from writing a function which accepts any arguments:
def apply(f, *args): return [f(arg) for arg in args]
apply(mul_by_2, 1) apply(mul_by_2, 1, 2) ...
And the point is - inside the apply function args is a variable that references a tuple of the actually passed arguments:
def apply(*args): print(args)
apply(1, 2, 3) # prints (1, 2, 3)
there may be just a little stretch about calling args a first-class object being "arguments to the call", but practically it is just that. Imagine the flexibility of things you can do with it.
Anyway, in conclusion I will demonstrate another situation where calling a spade a spade is good. A state machine. An object with a state, and a set of state transition rules. What would it typically be ?
def simulate(self): while self._state is not None: if self._state == "A": self._state_A() elif self._state == "B": self._state_B()
C().simulate() # prints A->B STOP
This is a quickly drawn together sample, so please don't be too picky. The problem with it, which I will try to eliminate is this - you have two kinds of way to represent the same thing - the state. What is the reason for aliasing _state_A by "A" and _state_B by "B" ? Oh, the last letter matches, I see... And what's the point in having the state-by-state switch in simulate ? Why don't we just call a spade a spade ?
def simulate(self): while self._state is not None: self._state()
C().simulate()
In this second example, I don't have any arbitrary aliases for state, instead I use for a state its own handler. A method which handles a state is a state here. It simplifies things just a bit - the switch is gone, and it is overall more clean and consistent to my taste.
Well, that's about what I had to say.
Python being a high level language... Other factors, such as wide variety of built-in container types and huge standard library also help Python to be higher level than many other languages, but it's another story.