Python: renaming superclass methods and their references to functionality

Consider the following example:

class Company():

    def hireEmployee():

    def fireEmployee():

    def promoteEmployee():

    etc...

class EngineeringFirm(Company):
    pass

class PaintingFirm(Company):
    pass

      

Let's say there are many more methods in the company class. What if I want to rename these methods from the superclass so that I can get the following:

class EngineeringFirm(Company):

    def hireEngineer():
    ...

class PaintingFirm(Company):

    def hirePainter():
    ...

      

... etc. While using "Employee" in this scenario really wouldn't hurt, it really just illustrates the idea. How can i do this?

My idea was to use the classFactory function, which will take the employee type as an argument and generate the Company class, while the metaclass will handle renaming, iterating through the attribute dictionary and replacing "Employee" with the specified type.

class EngineeringFirm(companyFactory('Engineer'))
    ...

      

The only problem is this: what if methods within the Company make calls to each other by default "Employee"? This is where I am stumped. I got the idea that the metaclass involved in renaming the methods can also get the source of each function (via the validator module) and search if a known attribute of the method is found inside and, if so, replace that part and create a new function via exec and assign it to the correct attribute key.

... But it really looks like hacks. I am open to alternatives, and although I understand that there might be design issues with the question (I am also open to suggestions on this front). I would be interested to know if this problem has a more elegant solution.

Thank!

Edit: another solution

For the sake of argument, I'll assume for a moment that the code above is really what I'm working with; I figured I could address some issues in the comments with another solution that I had in mind, which I had already reviewed and removed for reasons that I will explain.

If the classes Firm

inherited from the company and I wanted to maintain an identical interface (as usual, in that case, in order to allow dynamic calls hire()

or promote()

etc.), I could implement __getattribute__

that accepts HirePainter()

(by referring to the original Employee method), but however, it allows you to use any other interface HireEmployee()

, if necessary.

I wonder, assuming it's okay to extend my question, if this is what is considered bad practice, if, say, I was planning to do this because I thought the code inside PaintingFirm

would be helpful in readability? Again, I realize this example is terrible in that the readability is really not helpful in any way here, but suppose it happened?

(The only reason I didn't come up with this idea in the first place is that mine __getattribute__

already handles quite a lot, and adding extra noise to it doesn't seem attractive. Still, I could work in it, but that's a question which I should have asked if there were magic (but not hackers) solutions out there ..)

+3


source to share


3 answers


For posterity, I am posting my solution which I believe is a worthy alternative. I am not suggesting this as an answer because, in truth, I did not mention in my question that I prefer not to add unnecessary names, or to keep the ability to name those attributes as self.hireEngineer

rather than ClassDict['HireEngineer']

. With that in mind, I can't say that none of these answers answer the question.

Decision:

In hindsight, the problem was much simpler than what I did. I guess I fell for the metaclass just for this. If it's not obvious yet, I'm really just learning about metaclasses, and for a moment it seemed like a good opportunity to try them out. Alas.



I believe the following solution is in the spirit of Liskov's principle (thanks Ignacio) by giving the derived class the ability to refer to derived methods in its own way. The class namespace remains the same, and other objects can call these methods with their real names as needed.

# superclass...

def __getattribute__(self, attr):

    # Early exit (AttributeError) if attribute not found.
    obj = object.__getattribute__(self, attr)

    # All the extra code...

def __getattr__(self, attr):

    # Ex. self.type == 'Engineer'
    # Replacing titled-cased and lower-cased 
    # versions just to be safe (ex. self.employeeNames)

    attr = (attr
        .replace(self.type, 'Employee')
        .replace(self.type.lower(), 'employee')
    )

    if attr in self.attributes:
        return self.__getattribute__(attr)
    else:
        raise AttributeError

      

Next time I will try to do a better job as I outline the requirements. Thanks guys.

+1


source


You can try adding a dictionary for each class.

class EngineeringFirm(Company):
  ClassDict = {'HireEngineer':self.HireEmployee,
               ...
               };

      

Whenever you want to call a function you would use



<EngineeringFirmInstanc>.ClassDict['HireEngineer'](<arguments>)

      

It's not particularly elegant, but it might come close to what you're asking.

0


source


I tend to agree with the comments on this point: I suspect that what you are asking will add unnecessary complexity to the code, making it difficult to read and maintain just to implement a minor "cosmetic" feature of dubious benefit.

However, if you really want to do this, perhaps you can create methods that are synonymous with existing methods, so you can call the method with its original name or with a "customized" name whenever it seems appropriate.

Here's one pretty simple way to do it. I think there is some slick way to do it with cool decorators, but I don't know how to use them. :)

#! /usr/bin/env python

''' Class synonym demo

From http://stackoverflow.com/q/27729681/4014959

Written by PM 2Ring 2015.01.01
'''

class Foo(object):
    def __init__(self, data):
        self.foo_set(data)

    def foo_set(self, data):
        self.data = data

    def foo_add(self, n):
        self.data += n
        return self.data

    def foo_mul(self, n):
        self.data *= n
        return self.data

    def foo_mul_add(self, n, m):
        self.foo_mul(n)
        return self.foo_add(m)


def make_synonyms(cls, old, new):
    class newclass(cls):
        pass

    d = cls.__dict__
    for k in d:
        if k.startswith(old):
            newname = k.replace(old, new)
            #print k, d[k], newname
            setattr(newclass, newname, d[k])
    return newclass

#--------------------------------------

Bar = make_synonyms(Foo, 'foo', 'bar')

a = Foo(5)
print a.data
print a.foo_add(10)
print a.foo_mul(4)
print a.foo_mul_add(2, 1)

print '-' * 20

a = Bar(6)
print a.data
print a.foo_add(10)
print a.foo_mul(4)
print a.foo_mul_add(2, 1)

print '-' * 20

a.bar_set(5)
print a.data
print a.bar_add(10)
print a.bar_mul(4)
print a.bar_mul_add(2, 1)

      

Output

5
15
60
121
--------------------
6
16
64
129
--------------------
5
15
60
121

      

0


source







All Articles