ABCs

So Python 3000 will have abstract base classes.

As far as I know, in the Python world, an abstract base class used to mean, a base class that cannot be instantiated. Kind of like:

class Foo:
    def __init__(self):
        raise NotImplementedError, "Foo cannot be instantiated"

It would, however, be perfectly possible for an abstract base class to have valid, working methods, to be used by classes inheriting from Foo.

Apparently this isn't the case any longer. If I read PEP 3119 correctly, in Py3K-speak, an abstract base class is a way to trick isinstance() and issubclass() into believing that a given class derives from another class, when this actually isn't the case. For example:

>>> list.__bases__
(<class 'object'>,)
>>> object.__bases__
()

list derives from object, which in turn derives from nothing else. But:

>>> issubclass(list, collections.Sequence)
True

In other words, list *pretends* to derive from Sequence, but it really doesn't, and so any methods defined in Sequence will not be inherited by list.

Filtered through my old-school-Python-riddled brain, this implies the following:

  • abstract base classes mean something different now, and
  • they break isinstance and issubclass, and
  • methods defined in the ABC cannot be used by the "subclass".

Seriously, the more I read about Python 3.0, the harder it becomes for me not to be appalled. The language makes a heroic effort at fixing warts and problems and trimming fat, but at the same time it's becoming more and more complex.

I'd love to be proven wrong though. I like Python a lot, and I am (and have been) worried about what it's evolving into. But maybe Python 3.0 will actually be easy and intuitive to use, like the Python of old, and all the overly complex stuff will stay in the back rather than get in the way. I guess time will tell. (Or maybe I'm misunderstanding things... feel free to point that out. :-)

(This is mostly a matter of personal preference, of course -- for the last few years I've been gravitating toward languages with little syntax, that allow powerful new constructs to be written in the language itself. Think Scheme and Io. Python, however, has steadily been moving in the other direction.)

:: Comments (6)

list(dict)

The other day there was a thread on comp.lang.python that started off by discussing a common problem: deleting keys from a dict while iterating over it causes an exception. In other words, the following code fails:

>>> d = {1: 2, 3: 4, 5: 6}
>>> for i in d: del d[i]
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: dictionary changed size during iteration

This is easily solved by replacing for i in d with for i in d.keys(). The former iterates over the keys one by one, while the later creates a list of keys first, then iterates over *that*; the list does not change during the loop or after deleting keys from the dictionary, so the error above does not occur.

However. In Python 3000 this is going to change. d.keys() will then return an iterator, and effectively produce the same result as for i in d; that is, an exception. This can be solved (in both 2.5 and 3.0) by forcing a list; e.g. for i in list(d.keys()).

Then someone pointed out that this can be written as for i in list(d) as well. I had not given this much thought before, but this would work as well, of course. It's just that list(d) strikes me as very counter-intuitive. I mean, it makes sense, in a way; if you can iterate over a dictionary's keys by doing for i in d, then list(d) would do just that: iterate over the keys, collect them in a list, and return that list. Except that list(d) kind of reads to me as, "convert a dictionary to a list", which is not what it does. The reverse operation does not work because of this; dict(list(d)) doesn't fly. Maybe iterating over a dictionary's items would have been a better choice, after all. :-/

:: Comments

Python 3000 in 60 seconds

After studying the relevant PEPs and other documents, I've come to the following conclusions:

  • Python 3000 used to be the mythical, revolutionary new release, implemented from scratch (possibly in C++), that would solve all of Python's problems, and then some.
  • However, over time, features that were originally intended for Python 3000 were added to the 2.x branch instead. (Actually, many of these were more like cleanups than new features. Fixing the class/type dichotomy, nested scopes, unifying longs and ints, division operator, etc.)
  • So, in 2008, Python 3000 is not quite so revolutionary anymore, compared to 2.5. As PEP 3000 says, "Python 3000 will be implemented in C, and the implementation will be derived as an evolution of the Python 2 code base. [...] Since Python 3000 as a language is a relatively mild improvement on Python 2, we can gain a lot by not attempting to reimplement the language from scratch."
  • In spite of that, Python 3000 is still special, because it introduces a number of changes that will break backward compatibility. Again, many of these deal with the fixing of warts and inconsistencies. For example: print will become a function; as and with will be keywords, and so will True, False and None; some functions (like map) now return iterators rather than lists; exception handling is different; etc. More here. (I will probably write about some of these changes in more detail later.)
  • Don't count on changes that will change the face of Python completely: explicit self and limited lambda are here to stay, not to mention indentation-based syntax and case sensitivity. (See PEP 3099.)

:: Comments (3)