0

I have a base class Base and two sub classes Foo and Bar. In Base I define a function with an optional argument. This argument is either given or fetched at runtime (not at definition time). The argument is always fetched in the same exact way, which leads to subclasses having a boilerplate line of code (sure it's one line, but it's just an example). Normally this would look like this:

class Base(object):
    def greet(self, name=None):
        pass

class Foo(Base):
    def greet(self, name=None):
        name = name or get_name_at_runtime()
        print('Hello {name} this is Foo.'.format(name=name))

class Bar(Base):
    def greet(self, name=None):
        name = name or get_name_at_runtime()
        print('Hello {name} this is Bar.'.format(name=name))

Now I came up with a hack that solves this problem, but I don't find this to be a solid solution. The solution involves defining a private method that wraps the overridden method and supplies it with the correct value. Both methods are switched in the __init__ so calling/overriding the method feels 'normal':

class Base(object):
    def __init__(self):
        self.greet, self.__greet = self.__greet, self.greet

    def greet(self, name):
        pass

    def __greet(self, name=None):
        self.__greet(name or get_name_at_runtime())

class Foo(Base):
    def greet(self, name):
        print('Hello {name} this is Foo.'.format(name=name))

class Bar(Base):
    def greet(self, name):
        print('Hello {name} this is Bar.'.format(name=name))

The 'solution' I came up with poses the problem that it's not clear that name is optional since it looks like it doesn't have a default value.

In some cases Base is an abstract base class and in some cases it's not, so I'm looking for a solution that supports both.

1
  • Why not the metaclasses solution ? Commented Nov 22, 2014 at 11:19

2 Answers 2

0

How about using a decorator? This is the sort of thing they are meant for. Something like:

def get_at_runtime(func):
    def wrapped(*args, **kwargs):
        if kwargs.get('name', None) is None:
            kwargs['name'] = get_name_at_runtime()
        func(*args, **kwargs)

and wrap your methods:

@get_at_runtime
def greet(self, name=None):
    print('Hello {name} this is Foo.'.format(name=name))
Sign up to request clarification or add additional context in comments.

1 Comment

This way you would still have the decorator on Bar.great() and Foo.great(). Now the decorator is the boilerplate, this is just moving the problem imo.
0

Hi You can use metaclasses

class DefaultNameType(type):
    def __new__(cls, name, bases, attrs):
        if 'greet' in attrs:
            attrs['_greet'] = attrs.pop('greet')
            def greet(self, name=None):
                name = name or self.get_name_at_runtime()
                return self._greet(name)
            attrs['greet'] = greet
        print attrs
        return super(DefaultNameType, cls).__new__(cls, name, bases, attrs)

class Base(object):
    __metaclass__ = DefaultNameType

    def get_name_at_runtime(self):
        return 'foo'

    def greet(self, name):
        pass

class Foo(Base):
    def greet(self, name):
        print('Hello {name} this is Foo.'.format(name=name))

class Bar(Base):
    def greet(self, name):
        print('Hello {name} this is Bar.'.format(name=name))    

    def get_name_at_runtime(self):
        return 'foo1'

In this case any class derived from Base will be modified so that method greet will be renamed to _greet and greet method will be created which will run _greet

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.