Why Python class syntax should be different

Saturday 25 May 2019

If you’ve used any programming language for a long enough time, you’ve found things about it that you wish were different. It’s true for me with Python. I have ideas of a number of things I would change about Python if I could. I’ll bore you with just one of them: the syntax of class definitions.

But let’s start with the syntax for defining functions. It has this really nice property: function definitions look like their corresponding function calls. A function is defined like this:

def func_name(arg1, arg2):

When you call the function, you use similar syntax: the name of the function, and a comma-separated list of arguments in parentheses:

x = func_name(12, 34)

Just by lining up the punctuation in the call with the same bits of the definition, you can see that arg1 will be 12, and arg2 will be 34. Nice.

OK, so now let’s look at how a class with base classes is defined:

class MyClass(BaseClass, AnotherBase):

To create an instance of this class, you use the name of the class, and parens, but now the parallelism is gone. You don’t pass a BaseClass to construct a MyClass:

my_obj = MyClass(...)

Just looking at the class line, you can’t tell what has to go in the parens to make a MyClass object. So “def” and “class” have very similar syntax, and function calls and object creation have very similar syntax, but the mimicry in function calls that can guide you to the right incantation will throw you off completely when creating objects.

This is the sort of thing that experts glide right past without slowing down. They are used to arcane syntax, and similar things having different meanings in subtly different contexts. And a lot of that is inescapable in programming languages: there are only so many symbols, and many many more concepts. There’s bound to be overlaps.

But we could do better. Why use parentheses that look like a function call to indicate base classes? Here’s a better syntax:

class MyClass from BaseClass, AnotherBase:

Not only does this avoid the misleading punctuation parallelism, but it even borrows from the English we use to talk about classes: MyClass derives from BaseClass and AnotherBase. And “from” is already a keyword in Python.

BTW, even experts occasionally make the mistake of typing “def” where they meant “class”, and the similar syntax means the code is valid. The error isn’t discovered until the traceback, which can be baffling.

I’m not seriously proposing to change Python. Not because this wouldn’t be better (it would), but because a change like this is impractical at this late date. I guess it could be added as an alternative syntax, but it would be hard to argue that having two syntaxes for classes would be better for beginners.

But I think it is helpful to try to see our familiar landscape as confused beginners do. It can only help with explaining it to them, and maybe help us make better choices in the future.

Comments

[gravatar]
Jean-Pierre Messager 2:16 PM on 25 May 2019
What about the metaclass keyword argument you can use at class definition level in Python3?

class Thing(Parent, metaclass = Whatever):
[gravatar]
Good point. In ECMAScript 2015 they followed this idea, using "extends" instead of "for". From the existing keywords, I think "is" might even be better than "for".
[gravatar]
Simple: make Whatever Thing from Parent.

In that parlance, class is simply a shortcut for "make type".

Yes, make should be a new keyword. But at least we could write the full name of first arguments to classmethods. :-D
[gravatar]
class Thing from Parent as Whatever
[gravatar]
A related thing that bothers me about Python is the overloading of __call__. If it wasn't for old-style types (str(), int(), etc, that aren't even old-style anymore), it could have an object.new() method, eliminating type.__call__() . That would reduce the class confusion you mention as well.
[gravatar]
I don't like the way attributes for a class are defined. Seems too much work to define an __init__, and then mixes declaration of attributes with other code. (Data classes can be an improvement.)
[gravatar]
Maybe because these are not class attributes? :-)
The syntax for defining class attributes is perfectly fine. It's the instance attributes that are the problem, mostly because you don't have an instance until creation time.

Add a comment:

Ignore this:
Leave this empty:
Name is required. Either email or web are required. Email won't be displayed and I won't spam you. Your web site won't be indexed by search engines.
Don't put anything here:
Leave this empty:
Comment text is Markdown.