0

I have such code:

class Base:
  def do( self ):
    self._member1 = 0
    self._member2 = 1

class Derived1(Base):
  def do(self):
    Base.do(self)
    self._member3 = 0
    self._member4 = 1

class Derived2(Base):
  def do(self):
    Base.do(self)
    self._member3 = 2
    self._member4 = 3

class Derived3(Base):
  def do(self):
    Base.do(self)
    self._member3 = 9
    self._member4 = 3

etc. Here is what I've done:

class Base(object):
  '''Base class.'''
  def do(self):
    self._member1 = 0
    self._member2 = 1

class Derived1(Base): pass
class Derived2(Base): pass
class Derived3(Base): pass
class Derived4(Base): pass

class DerivedFactory(object):
  '''Factory to create derived classes.'''
  members = \
  {
    1: (Derived1, 0, 1),
    2: (Derived2, 4, 5),
    3: (Derived3, 6, 7),
    4: (Derived4, 8, 9),
  }

  def do(self, key=1):
    derived = self.members[key][0]()
    derived.do() # Perform method from Base
    derived._member3 = self.members[key][1]
    derived._member4 = self.members[key][2]
    print(derived)
    print('\t%s' % derived.__dict__)

if __name__ == '__main__':
  factory = DerivedFactory()
  for key in range(1, 5):
    derived = factory.do(key)

However, it is not perfect as you see. I need to declare Derived1, Derived2, etc. Also it seems to be overcomplicated. Do you have any ideas on how to imporve this code? Thanks!

2 Answers 2

4

As far as I can tell you are just interested in reducing the duplicated code, here is one option that is equivalent to your top piece of code:

class Base:
    def do( self ):
        self._member1 = 0
        self._member2 = 1

def make_do(a, b):
    def do(self):
        Base.do(self)
        self._member3 = a
        self._member4 = b
    return do

class Derived1(Base):
    do = make_do(0, 1)

class Derived2(Base):
    do = make_do(2, 3)

class Derived3(Base):
    do = make_do(9, 3)

Shorter version suggested by Oren in comments (note that for this to work, Base needs to be a new-style class):

class Base(object):
    def do( self ):
        self._member1 = 0
        self._member2 = 1

def make_do(a, b):
    def do(self):
        Base.do(self)
        self._member3 = a
        self._member4 = b
    return do

def make_derived(name, a, b):
    return type(name, (Base,), {'do': make_do(a,b)})

Derived1 = make_derived('Derived1', 0, 1)
Derived2 = make_derived('Derived2', 2, 3)
Derived3 = make_derived('Derived3', 9, 3)

And here is a decorator version, since it was asked for in comments:

class Base(object):
    def do( self ):
        self._member1 = 0
        self._member2 = 1

def add_do(a, b):
    def do(self):
        Base.do(self)
        self._member3 = a
        self._member4 = b
    def deco(cls):
        return type(cls.__name__, (Base,), {'do': do})
    return deco

@add_do(0, 1)
class Derived1(Base): pass

@add_do(2, 3)
class Derived2(Base): pass

@add_do(9, 3)
class Derived3(Base): pass

Another decorator version, that overrides the sub-classes' do instead of creating a new type (it also lets you use old-style classes):

class Base:
    def do( self ):
        self._member1 = 0
        self._member2 = 1

def add_do(a, b):
    def deco(cls):
        original_do = cls.do
        def do(self):
            original_do(self)
            self._member3 = a
            self._member4 = b
        cls.do = do
        return cls
    return deco

@add_do(0, 1)
class Derived1(Base): pass

@add_do(2, 3)
class Derived2(Base): pass

@add_do(9, 3)
class Derived3(Base): pass
Sign up to request clarification or add additional context in comments.

4 Comments

You can also use the type function to create the classes dynamically: def make_derived(name, a, b): type(name, (Base,), {'do': make_do(a,b) }) Derived1 = make_derived('Derived1', 0, 1) Derived2 = make_derived('Derived2', 2, 3) Derived3 = make_derived('Derived3', 9, 3)
@Oren Indeed, it's called metaprogramming :)
Added a version with decorators to my answer (metaclasses are not used).
In the last case, it's worth mentioning that the class decorator will ignore any other definition under the original class (if anything else was written instead of the pass)
0

If you are just using the classes as data containers with a method that operates on the data you might be better off with a different pattern:

from collections import namedtuple

Base = namedtuple("Base", ["member1", "member2"])
Derived = namedtuple("Derived", Base._fields + ("member3", "member4"))

def do(base_or_derived):

    print "Member1:", base_or_derived.member1
    print "Member2:", base_or_derived.member2

    if not isinstance(base_or_derived, Base):
        print "Member3:", base_or_derived.member3
        print "Member4:", base_or_derived.member4

if __name__ == "__main__":
    base = Base(0, 1)
    derived1 = Derived(0, 1, 0, 1)
    derived2 = Derived(0, 1, 2, 3)
    derived3 = Derived(0, 1, 9, 3)

    for item in [base, derived1, derived2, derived3]:
        do(item)

1 Comment

No, members must be initialized only in do method.

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.