4

I have a parent class and two child class. The parent class is an abstract base class that has method combine that gets inherited by the child classes. But each child implements combine differently from a parameter perspective therefore each of their own methods take different number of parameters. In Python, when a child inherits a method and requires re-implementing it, that newly re-implemented method must match parameter by parameter. Is there a way around this? I.e. the inherited method can have dynamic parameter composition?

4
  • 4
    Please show some code demonstrating your problem. There is no requirement for a subclass method to have the same argument signature as the superclass version (but of course, if it calls the superclass version, it has to call it with the right arguments). Commented Nov 2, 2014 at 2:31
  • Why ask a detailed question, but not provide the code? -_- Commented Nov 2, 2014 at 2:46
  • 1
    While there's certainly no requirement that a subclass method maintain the same signature, it seems symptomatic of bad design. In particular, it definitely violates the liskov substitution principle. Commented Nov 2, 2014 at 4:32
  • 1
    It doesn't necessarily violate Liskov. For example, you could use a command pattern, or some other form of abstracting away the work of injecting arguments into induced method calls. For example, suppose you had Shape and Square inheriting Shape. Square's perimeter function may have a different signature, but if the program obtains the arguments that should be injected into Square.perimeter, and then there is a dispatcher with a method like Dispatcher.requestPerimeter(shape, some_arg_container) you could still achieve substitutability. It would be a cumbersome design though. Commented Nov 2, 2014 at 4:43

2 Answers 2

4

This code demonstrates that signature of overridden method can easily change.

class Parent(object):
    def foo(self, number):
        for _ in range(number):
            print "Hello from parent"



class Child(Parent):
    def foo(self, number, greeting):
        for _ in range(number):
            print greeting


class GrandChild(Child):
    def foo(self):
        super(GrandChild,self).foo(1, "hey")


p = Parent()
p.foo(3)

c = Child()
c.foo(2, "Hi")

g = GrandChild()
g.foo()
Sign up to request clarification or add additional context in comments.

Comments

3

As the other answer demonstrates for plain classes, the signature of an overridden inherited method can be different in the child than in the parent.

The same is true even if the parent is an abstract base class:

import abc

class Foo:
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def bar(self, x, y):
        return x + y

class ChildFoo(Foo):
    def bar(self, x):
        return super(self.__class__, self).bar(x, 3)

class DumbFoo(Foo):
    def bar(self):
        return "derp derp derp"

cf = ChildFoo()
print cf.bar(5)

df = DumbFoo()
print df.bar()

Inappropriately complicated detour

It is an interesting exercise in Python metaclasses to try to restrict the ability to override methods, such that their argument signature must match that of the base class. Here is an attempt.

Note: I'm not endorsing this as a good engineering idea, and I did not spend time tying up loose ends so there are likely little caveats about the code below that could make it more efficient or something.

import types
import inspect

def strict(func):
    """Add some info for functions having strict signature.
    """
    arg_sig = inspect.getargspec(func)
    func.is_strict = True
    func.arg_signature = arg_sig
    return func


class StrictSignature(type):
    def __new__(cls, name, bases, attrs):
        func_types = (types.MethodType,) # include types.FunctionType?

        # Check each attribute in the class being created.
        for attr_name, attr_value in attrs.iteritems():
            if isinstance(attr_value, func_types):

                # Check every base for @strict functions.
                for base in bases:
                    base_attr = base.__dict__.get(attr_name)
                    base_attr_is_function = isinstance(base_attr, func_types)
                    base_attr_is_strict = hasattr(base_attr, "is_strict")

                    # Assert that inspected signatures match.
                    if base_attr_is_function and base_attr_is_strict:
                        assert (inspect.getargspec(attr_value) == 
                                base_attr.arg_signature)

        # If everything passed, create the class.
        return super(StrictSignature, cls).__new__(cls, name, bases, attrs)



# Make a base class to try out strictness
class Base:
    __metaclass__ = StrictSignature

    @strict
    def foo(self, a, b, c="blah"):
        return a + b + len(c)

    def bar(self, x, y, z):
        return x


#####
# Now try to make some classes inheriting from Base.
#####
class GoodChild(Base):

    # Was declared strict, better match the signature.
    def foo(self, a, b, c="blah"):
        return c

    # Was never declared as strict, so no rules!
    def bar(im_a_little, teapot):
        return teapot/2


# These below can't even be created. Uncomment and try to run the file
# and see. It's not just that you can't instantiate them, you can't
# even get the *class object* defined at class creation time.
#
#class WrongChild(Base):
#    def foo(self, a):
#        return super(self.__class__, self).foo(a, 5)
#
#class BadChild(Base):
#    def foo(self, a, b, c="halb"):
#        return super(self.__class__, self).foo(a, b, c)

Note, like with most "strict" or "private" type ideas in Python, that you are still free to monkey-patch functions onto even a "good class" and those monkey-patched functions don't have to satisfy the signature constraint.

# Instance level
gc = GoodChild()
gc.foo = lambda self=gc: "Haha, I changed the signature!"

# Class level
GoodChild.foo = lambda self: "Haha, I changed the signature!"

and even if you add more complexity to the meta class that checks whenever any method type attributes are updated in the class's __dict__ and keeps making the assert statement when the class is modified, you can still use type.__setattr__ to bypass customized behavior and set an attribute anyway.

In these cases, I imagine Jeff Goldblum as Ian Malcolm from Jurassic Park, looking at you blankly and saying "Consenting adults, uhh, find a way.."

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.