1

I currently refactor a class defining a client or a server. The class had a lot of

if client:
    XXXXXXXXXXXX
elif server:
    YYYYYYYYYYYY

So I decided to create a class A with the similar code and one class C for the client and an other one S for the server which inherit A. (they don't have theses names of course ^^)

So class A is some kind of abstract class. But the problem is there is no abstract classes in Python 2.5, it comes with 2.6 version. So I was wondering if there is a way to forbid instantiations of class A.

One solution would have been to raise a NotImplemented error in the constructor of the class A, but C and S have the same code for it so I put it in the "abstract" class A (bad idea ?).

This may seem stupid but I develop in Python only from time to time and I'm a young programmer. What are your advices?

Loïc Lopes
  • 544
  • 8
  • 19
  • Might be a silly question, but why can't you upgrade to Python 2.6 or 2.7? – David Webb Jul 01 '11 at 13:00
  • It's a constraint of the project I'm working on. – Loïc Lopes Jul 01 '11 at 13:02
  • 2
    This is just one of those Python things you will need to become comfortable with. Python assumes developers know the correct way to use the classes they work with instead of enforcing classes to be only used in one way. This is also demonstrated by the fact that private methods don't actually exist. – unholysampler Jul 01 '11 at 13:28
  • @unholysampler Why do they add the Astract Base Class in 2.6 if we should not work like this? It's a real question, I'm not criticizing what you're arguing. – Loïc Lopes Jul 01 '11 at 14:53
  • The abc docs point at PEP 3119, [rationale here](http://www.python.org/dev/peps/pep-3119/#rationale). Now, this rationale explains they added ABCs partly to support the Inspection mechanism (ie, so you can per-class behaviour outside the class). The question above doesn't show any reason why this would be needed: it looks like the OP wants the Invocation mechanism and is just thinking in terms of statically-typed languages. My answer below, and @unholysampler 's comment, assume this is the case and address pythonic Invocation. – Useless Jul 01 '11 at 15:10
  • So the best solution in the pythonic way would be to do not make the instantiation of the class A impossible and leave it like this (with some renaming to show it should not be invoked) ? – Loïc Lopes Jul 01 '11 at 17:17

6 Answers6

2

This approach has the advantage that you do not need to do anything to the subclass to make it non-abstract.

class ABC(object):
    abstract = True
    def __new__(cls, *args, **kwargs):
        if "abstract" in cls.__dict__ and cls.__dict__["abstract"] == True:
            raise RuntimeError(cls.__name__ + " is abstract!")
        return object.__new__(cls)

class Subclass(ABC):
    pass

print Subclass()
print ABC()

Output:

<__main__.Subclass object at 0xb7878a6c>
Traceback (most recent call last):
  File "abc.py", line 14, in <module>
    print ABC()
  File "abc.py", line 6, in __new__
    raise RuntimeError(cls.__name__ + " is abstract!")
RuntimeError: ABC is abstract!

If you want to create an abstract subclass, simply do like this:

class AbstractSubclass(ABC):
    abstract = True
Lauritz V. Thaulow
  • 49,139
  • 12
  • 73
  • 92
2

In statically-typed languages, you use an abstract base class (ABC) because you need some object with a defined size, interface etc. to pass around. Otherwise, the code trying to call methods on that object can't be compiled.

Python isn't a statically-typed language, and the calling code doesn't need to know the type of the object it's calling at all. So, you can "define" your ABC just by documenting the interface requirements, and implementing that interface directly in two unrelated classes.

Eg,

class Server:
    def do_thing(self):
        pass #do that server thing here

class Client:
    def do_thing(self):
        pass #do that client thing here

def do_thing(thingy):
    thingy.do_thing() # is it a Client? a Server? something else?

s=Server()
c=Client()

do_thing(s)
do_thing(c)

Here, you could pass in any object with a do_thing method whose arguments match the call.

Useless
  • 64,155
  • 6
  • 88
  • 132
  • I believe the call s.do_thing() to be more clear (clearer ?) than do_thing(s). The server does something. We don't do something with the server. – Loïc Lopes Jul 01 '11 at 14:48
  • The OP's example code is presumably being invoked on an object which could represent either a server or a client, and the OP is trying to seperate client and server into two seperate classes. The do_thing(thingy) is just to demonstrate that this invocation doesn't need an ABC to work with an object that could be an instance of either the Client or Server classes – Useless Jul 01 '11 at 15:15
  • No problem, I could probably have made the example clearer. Search for 'duck typing' for more on this idea. – Useless Jul 04 '11 at 09:47
  • Ok, thanks. I did some research for duck typing and I now understand the way we should program in Python. I was trying to do it the same way I'm working with C#. – Loïc Lopes Jul 04 '11 at 12:51
1

You can call a method "foo" at the beginning of A constructor. In A, this method raises an exception. In C and in S, you redefine "foo" so there is no more exceptions.

MatthieuW
  • 2,292
  • 15
  • 25
  • +1 for clever & simple. I'd tend to call it something more along the lines of "ensureNotBase", but the idea's a nice one, if python's smart enough to know the runtime type when the constructor runs. – Carl Manaster Jul 01 '11 at 14:04
1

My first question is: why can't you simply avoid to instantiate an object from class A? What I mean is that this is a bit like questions on implementing singletons... As this answerer correctly quoted:

Before the Gang of Four got all academic on us, "singleton" (without the formal name) was just a simple idea that deserved a simple line of code, not a whole religion.

The same - IMO - applies to abstract classes (which in fact have been introduced in Python for other reasons than the one you would intend to use them for.

That said, you could raise an exception in the __init__ method of class A. Something like:

>>> class A():
...     def __init__(self):
...             raise BaseException()
... 
>>> a = A()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
BaseException
>>> class B(A):
...     def __init__(self):
...             pass
... 
>>> b = B()
>>>

Of course this is just an rough idea: if you have - for example - some useful stuff in the A.__init__ you should check the __class__ attribute, etc...

Community
  • 1
  • 1
mac
  • 42,153
  • 26
  • 121
  • 131
  • I'll leave the company in 2 months and I want to make it clear to the next developper he should not instantiate this class A. I thought to add the word abstract in the name of the class A but I'm not sure it's a good solution. – Loïc Lopes Jul 01 '11 at 14:28
  • @Louhike - ...then I would build my own exception class `AbstractOnlyError(BaseException)` and make sure that both the stringdoc of it and the message that it will throw out will be clear enough for your future colleague! :) – mac Jul 01 '11 at 14:38
0

There's an abstract base class module for what you want.

Not applicable for 2.5.

PrettyPrincessKitty FS
  • 6,117
  • 5
  • 36
  • 51
0

The real question is: why do you need an abstract class?

if you make 2 classes, and make the second herit from the first, it is an efficient way to clean your code.

Simon Bergot
  • 10,378
  • 7
  • 39
  • 55
  • Maybe I don't understand your reply...(?) But neither C or S offers a superset of methods of the other class. They share a common base but then they offer each of them their different methods. Making C(S) or S(C) would include in the child methods that shouldn't be there... – mac Jul 01 '11 at 13:21
  • -1. This is a bad idea. Think of inheritance with the term "IS A". If the following is not true: IS A then you don't have an inheritance relationship and representing the relationship you have through inheritance will bring you no end of trouble. – Carl Manaster Jul 01 '11 at 14:01