views:

341

answers:

5

I'm implementig a Composite pattern in this way:

1) the "abstract" component is:

class Component(object):
"""Basic Component Abstraction"""
def __init__(self, *args, **kw):
    raise NotImplementedError("must be subclassed")

def status(self):
    """Base Abstract method"""
    raise NotImplementedError("must be implemented")

2) a leaf:

class Leaf(Component):
"""Basic atomic component
"""
def __init__(self, *args, **kw):
    self.dict = {}

def status(self):
    """Retrieves properties
    """
    return self.dict

The Problem is that pylint generates, of course, this warning:

Leaf.__init__: __init__ method from base class 'Component' is not called

but into my Leaf i cannot call for:

def __init__(self, *args, **kw):
    Component.__init__(self, *args, **kw)
    self.dict = {}

without raise of the exception.

Must I ignore pylint warning or there is some bad coding?

+4  A: 

Abstract initializers are a bad idea. Your code might evolve so that you want to do some initialization in the root component. And even if you don't why require the implementation of the initializer. For some subclasses an empty initializer would be an acceptable choice.

If you don't want any instances of the Component class around, check for that in the initializer:

class Component(object):
    def __init__(self, **kwargs):
        assert type(self) != Component, "Component must not be instantiated directly"

class Leaf(Component):
    def __init__(self, some, args, **kwargs):
        # regular initialization
        Component.__init__(self, **kwargs)
Ants Aasma
You suggest: "Abstract initializers are a bad idea. Your code might evolve so that you want to do some initialization in the root component", but I'm into a Composite Pattern and it must have such abstraction.
DrFalk3n
@DrFalk3n. Let's not fetishize over the Java version of the pattern. Java folks love their abstract classes. Python simply doesn't have this. We're all adults here, we all know the abstract class doesn't work. Leaving off the abstract __init__ that raises NotImplemented is really OK. You don't have to rehash the Java/C++ foolishness in Python.
S.Lott
The composite pattern doesn't mean that you couldn't have common initalization code in the base class. What you want to avoid is instantiation of the base class. So check for that with if type(self) == Component: raise Exception("Component must not be instantiated directly")
Ants Aasma
@S.Lott +1 good comment event thought I didn't undestand why you say"We're all adults here". Did I offend? If so it was not in my intenction
DrFalk3n
Both raise and assert methods you proposed works fine and sounds really good but wich is the best? and why?
DrFalk3n
@DrFalk3n: "We're all adults here" is a standard Python quote. It's a way of deprecating all the silly "private" and "protected" and "abstract" and "interface" techniques from Java and C++. It's Python -- we can all see the source. Forget about "private" it just doesn't mean anything.
S.Lott
assert is just a shorthand for "if not condition: raise AssertionError(...)" in this context. assert has the additional property that the assertion can be turned off via the -O property so you can avoid any runtime overhead in production. Use what ever seems appropriate in your case.
Ants Aasma
@DrFalk3n: (On the private/protected issue, which really goes to trust.) My favorite analogy is that java/c++ programmers stay off your porch because you've got razor wire and a shotgun, while python programmers stay off your porch because that's the right thing to do.
bstpierre
+1  A: 

You want to guarantee, that the base class Component is not instanciated. This is a noble guesture common in other programming languages like C++ (where you can make the constructors private or so to prevent direct usage).

But it is not supported in Python. Python does not support all programming notions and also is more "dynamic". So initialization is done in a "Pythonic" way and your notion is not supported thus.

Python is much more based on trust than other languages -- so, for example static variables are not supported and private ones also only in a limited way.

What you could do (when you distrust the users of your module) -- you could hide the base class by naming it "_Component" -- make it an internal secret. But of course this can create other troubles.

Juergen
+1: Provide some documentation in the docstring. We're all adults here. We're not struggling with Java or C++ where we're forced to tell the compiler how to generate code.
S.Lott
@S.Lott: :-) You are always so relevant thanks
DrFalk3n
+1  A: 

Not bad coding as such, but the __init__ of the component is simply not needed. If you want it, you can ignore pylint, but it's a better idea to simply remove the __init__ from Component.

Embrace the dynamicism!

Lennart Regebro
+1 really good suggestion to me
DrFalk3n
+2  A: 

Another suggestion to complement the idea of Markus:

If you really must, I suggest that you use __new __ and check for the given object type. When it is "Component" you could fire your exception:

class Component(object):
"""Basic Component Abstraction"""

def __new__(objType, *args, **kwargs):
    if objType == Component:
       raise NotImplementedError("must be subclassed")
    return object.__new__(type, *args, **kwargs)

When a subclass is created, objType will be != Component and all will be fine!

Juergen
All right, type(self) should also work fine, since the type is assigned in __new__ before __init__.
Juergen
+1  A: 

Renaming your class Component to AbstractComponent should help. And don't provide an __init__ method in your base class if it's not supposed to be called by subclasses.

gurney alex