[Tutor] class questions

Steven D'Aprano steve at pearwood.info
Sat Jun 26 15:07:17 CEST 2010


On Sat, 26 Jun 2010 10:17:45 pm Payal wrote:
> Hi,
> Some questions about which I am a bit confused.
>
> 1. I know there is a difference between mro of classic and new style
> classes. but I do not get why we need the new mro, the old one is
> easy to predict and understand?

The old MRO (Method Resolution Order) is broken for classes using 
multiple inheritance with a diamond shape inheritance diagram. Not a 
little bit broken, but horribly, horribly broken.

If you have something like this class hierarchy:

class A:
    pass

class B(A):
    pass

class C(A):
    pass

class D(B, C):
    pass


(only with actual methods, of course) then inheritance with old-style 
classes cannot work correctly in some circumstances. Fortunately this 
sort of inheritance diagram:

    A
   / \
   B C
   \ /
    D

is rare for old-style classes. But for new-style classes, *all* multiple 
inheritance is diamond-shaped, because all classes inherit back to 
object at the top of the diagram:

    object
    /    \
  list   str
    \    /
    myclass 

So in order to allow people to inherit from built-ins, we had a choice:

(1) Keep the old MRO and a bug which used to be rare would become 
common;

(2) Keep the old MRO but prohibit multiple inheritance and mixins (which 
would mean breaking a lot of existing code); or

(3) Change the MRO to one which doesn't have the bug.


The Python developers choose 3. Well, actually, that's not quite true: 
nobody noticed that the MRO was broken until *after* new-style classes 
were added in version 2.2, and so the new MRO was added in version 2.3.

http://www.python.org/download/releases/2.3/mro/


> 2. A friend of mine told me that in C++ abstract classes are classes
> which can not be instatiated. Is she right, do we have them in
> python, if yes can someone give a simple example?

Python doesn't have a special "abstract class" type, but it is easy to 
make one with just two lines of boilerplate:

class AbstractThing:
    def __init__(self):
        # Next two lines are boilerplate.
        if type(self) is Abstract:
            raise TypeError("abstract class, you must subclass this")
        # init code for non-abstract Thing classes goes here


> 3. In http://www.alan-g.me.uk/tutor/index.htm , Alan has given an
> example in "User Defined Exceptions",
>
> >>> class BrokenError(Exception): pass
>
> Even after reading the section a few times over, I fail to get
> utility of such a class. Can someone please explain with better
> example? Alan can you please cleanup that section, maybe make it
> broader and put the stuff about "SystemExit" elsewhere.

The utility of user-defined exceptions is simply to allow your code to 
distinguish between *your* exceptions and other exceptions. Different 
people have different policies. Some people prefer this:


def func(x):
    if x <= 0:
        raise ValueError('x must be positive')
    return sqrt(x) + x**2


Other people prefer this:


class DomainError(ValueError):
    """Used for mathematics domain errors."""
    pass

def func(x):
    if x <= 0:
        raise DomainError('x must be positive')
    return math.sqrt(x) + math.asin(x**2)


Then when you catch an error:


try:
    print func(y)
except DomainError:
    # only catches my own error
except ValueError:
    # catches any other ValueError


But both solutions are equally good. Normally you don't want fifty 
different exception classes in a module, nor do you want every 
exception to be the same generic class. How finely you divide the error 
classes is up to you.



-- 
Steven D'Aprano


More information about the Tutor mailing list