Two classes with a structurally identical method b

2019-08-29 08:27发布

问题:

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.

回答1:

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.



回答2:

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