0

I have two classes with a common function f, like here:

class ClassA(object):

    def f(self, var):
        self.A = g(var, self.A)

    # specific code for ClassA, also changing self.A

class ClassB(object):

    def f(self, var):
        self.B = g(var, self.B)

    # specific code for ClassB, also changing self.B

In both classes, f does the same on two variables called A and B, which are structurally equivalent. I would like to put f it into an abstract class to avoid code duplication. One way to do this is

class ClassX(object):

    def f(self, var):
        self.X = g(var, self.X)

class ClassA(ClassX):

    # specific code for ClassA, also changing self.X

class ClassB(ClassX):

    # specific code for ClassB, also changing self.X

In this solution the variables A and B have been renamed X. However, to make my code more self-explaining, I would like to keep those specific names (say A,B) for X in the special classes.

Is there a way to do this?

Also please comment if you can suggest a more meaningful and descriptive title, so it becomes more valuable to the community.

Edit: The solution should also work if the variables A, B take a type which is pass-by-value and should assume that both their value and type might be changed from outside the class during the program execution.

flonk
  • 3,726
  • 3
  • 24
  • 37

2 Answers2

2

First, a caveat: if your case actually calls for inheritance of the kind you describe, then the variable name should probably be the same. In most cases, giving different names to attributes that are identical in two related classes doesn't make code more self-explaining -- it makes it less self-explaining. It makes the attributes look different when they're really the same, adding complexity and potential for confusion.

However, I can imagine a few cases where you might want to do something like this -- it sounds a bit like you want to define a common interface to two different kinds of objects. One simple approach would be to use properties to define aliases; so in each class, you'd have different attributes (A and B), but you'd also define a property, X, that would access the appropriate attribute in either case. So in ClassA, you'd do something like this in the class definition:

@property
def X(self):
    return self.A
@x.setter
def X(self, value):
    self.A = value
@x.deleter
def X(self):
    del self.A

And in ClassB:

@property
def X(self):
    return self.B
@x.setter
def X(self, value):
    self.B = value
@x.deleter
def X(self):
    del self.B

Then, you could define a common function that would work only with X, which subclasses could inherit, overriding property X in whatever way is appropriate.

This is rarely worth the additional layer of indirection though. Most of the time, if there's a good reason for two classes to share a method, then they should also share the attributes that the method changes, name and all.

senderle
  • 145,869
  • 36
  • 209
  • 233
  • 1
    I would add that if you do have an attribute that is different enough to have a different name, but you want to perform the same processing on them, you should use a standalone function for the processing. Not everything has to belong to a class. – Darrick Herwehe Jan 31 '14 at 15:03
  • Thanks, it's a bit much structure but it seems as using `@property` is still the easiest strategy. However, I have changed your approach and put this into the base class, like `@property def X(self): return self.__getattribute__[self.Xname]` and assigning an attribute `self.Xname` which is `'A'` or `'B'` to the concrete classes on initialization. This has the advantage of hiding the structure in the abstract base class and keeping the code simple in the more concrete, derived classes. Furthermore it would save work, if I decided to write another `ClassC`. – flonk Feb 03 '14 at 09:39
  • Well, if it saves you so much work to do it that way, then I really think all your classes should have the same attribute name. Speaking heuristically, this is a strong signal that you aren't thinking abstractly _enough_ about your classes. But without seeing specific code, I can't tell for sure. Also, I would encourage you to use `getattr` as Silas Ray suggested, instead of `__geattribute__`, which is for overriding, not for use (see [here](http://stackoverflow.com/questions/1944625/what-is-the-relationship-between-getattr-and-getattr) for more). – senderle Feb 03 '14 at 14:21
  • @senderle Your first statement is too general, you don't want to generalize `elephant` and `butterfly` as `animal_with_a_probiscis` in your code, when this common feature is irrelevant for its major part. However thanks for the hint on `getattr`! – flonk Feb 04 '14 at 12:33
1

If all you want to do is alias an instance attribute, you could just use a property.

class A(object):

    def f(self, var):
        self.x = g(var, self.x)

    @property
    def x(self):

        return getattr(self, self.x_alias)

    @x.setter
    def x(self, val):

        setattr(self, self.x_alias, val)

class AA(A):

    x_alias = 'aval'

    def __init__(self, aval):

        self.aval = aval

class AB(A):

    x_alias = 'bval'

    def __init__(self, bval):

        self.bval = bval
Silas Ray
  • 25,682
  • 5
  • 48
  • 63