views:

161

answers:

3

I know that checking types in Python is bad and you should probably never do it. But I can't seem to find the disadvantage to this.

class O(object):         
    def __init__(self, name):
        '''Can only be called in derived classes.'''
        if type(self) is O:
            message = "%(class)s cannot be instantiated, it must be derived."
            raise TypeError, message % { "class" : O }
        self.name = name

    def fn(self):
        '''Must be populated in derived classes.'''
        raise NotImplementedError

Now if someone tries to instantiate O, a class I never meant to be instantiated, they know immediately.

Is this still bad form?

+4  A: 

Look at Abstract Base Classes as they will provide more fine grained control over how the subclasses are instantiated if this is something that you really want to do.

All in all, this might be a valid use because you are not preventing me from passing whatever I want to your code but I still wouldn't consider it pythonic. You are telling me that I can't instantiate your class. What If I want to?

Using ABC's, it looks like:

import abc

class O(object):
    __metaclass__ = abc.ABCMeta     
    def __init__(self, name):
        self.name = name

    @abc.abstractmethod
    def fn(self):
        pass

This has the advantage of not breaking super on the fn method. As you have it with the raise NotImplementedError, you are breaking super for multiple inheritance. If a class derives from two classes that subclass O and both call super (as they should to allow for multiple inheritance) then it will create the exception that you raise.

So now, you are not just telling me that I can't instantiate your class, you are telling me that I can't use multiple inheritance when subclassing from your class.

aaronasterling
I edited to show the intent more clearly. I do this often to make the purpose of such a class obvious upon reading the code or trying to use it without understanding it.
cory
@Cory, based on your update you really do want an ABC. see my update.
aaronasterling
Your example is great, and you've sparked my interest in the abc module. But you've forgotten the last two underscores on __metaclass
cory
+4  A: 

Don't check at all, we're are all adults here. Just add a note that O shouldn't be instantiated directly, either as a comment and/or in the documentation.

It's the same as if someone would call a method that requires an int as its parameter with a string instead. If the program crashes, they screwed it up. Otherwise you would need to add type checks to just about everything.

Ivo Wetzel
+1  A: 

What is that you are trying to achieve with the above code.

In this case self is of type O and will always result in raising the exception.

Look at this snippet to understand it a little better

class O(object):         
    def __init__(self, name):
        self.name = name      

o = O("X")

print type(o)
print isinstance(o, O)
if type(o) is O:
    print "Yes"

This will output

<class '__main__.O'>
True
Yes
pyfunc
it won't raise an exception if you subclass it which is the point
aaronasterling
My code prevents any instances of O from being created with O(), but any class derived from O will be able to instantiate itself.
cory
+1 I didn't understand it at all. Pretty smooth though.
pyfunc