Python:重命名超类的方法及其功能范围引用

时间:2015-01-01 09:40:25

标签: python inheritance methods metaclass renaming

考虑以下示例:

class Company():

    def hireEmployee():

    def fireEmployee():

    def promoteEmployee():

    etc...

class EngineeringFirm(Company):
    pass

class PaintingFirm(Company):
    pass

假设Company类有更多方法。如果我想从超类重命名这些方法,那么我可以得到以下内容:

class EngineeringFirm(Company):

    def hireEngineer():
    ...

class PaintingFirm(Company):

    def hirePainter():
    ...

......等等。虽然在这种情况下使用“员工”确实不会有点伤害,但这只是为了说明这个想法。我该怎么办呢?

我的想法是使用classFactory函数,该函数将employee类型作为参数并生成Company类,而元类将通过迭代属性字典并用所述类型替换“Employee”来处理重命名。 p>

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

唯一的问题是:如果公司内部的方法通过默认的“员工”名称相互调用怎么办?这是我难倒的地方。我认为重命名方法所涉及的元类也可以获取每个函数的源(通过检查模块)并搜索是否在其中找到已知方法属性,如果是,则替换该部分并通过以下方法创建新函数exec并将其分配回正确的属性键。

......但这真的看起来有点像黑客。我对替代方案持开放态度,虽然我意识到问题上可能存在与设计相关的问题(我也对这方面的建议持开放态度),但我很想知道这个问题是否有更优雅的解决方案。

谢谢!

编辑:其他解决方案

为了争论,我暂时假设上面的代码就是我正在使用的代码;我想我可以用我想到的另一个解决方案来解决评论中的一些问题,我已经考虑过这个问题,并且由于我将要解释的原因而放弃。

如果Firm类继承自公司并且我希望保持相同的界面(通常在这种情况下允许动态调用hire()promote()等等我可以实现一个__getattribute__接受HirePainter()(通过访问原始的Employee方法),同时仍然允许任何其他接口在必要时使用HireEmployee()

我想知道,假设我可以延长我的问题,如果这样做会被认为是不好的做法,如果我打算这样做,因为我认为PaintingFirm中的代码会在可读性方面受益吗?再一次,我意识到这个例子很可怕,因为这里的可读性似乎并没有以任何方式受益,但是假设它确实如此?

(我首先没有提出这个想法的唯一原因是我的__getattribute__已经处理了很多,并且添加了额外的噪音并没有让人觉得有吸引力。但是,我还能工作它在,但这是一个我不得不问的问题,以防有更多神奇的(但不是hacky)解决方案..)

3 个答案:

答案 0 :(得分:1)

为了后人的缘故,我发布了自己的解决方案,我相信这是一个不错的选择。我不建议将其作为答案,因为事实是我在我的问题中没有提到我不希望添加额外的名称,或者保留将这些属性称为self.hireEngineer而不是ClassDict['HireEngineer']的能力。 。鉴于此,我不能说这些答案中的任何一个都没有回答这个问题。

<强>解决方案:

事后看来,问题比我做的要简单得多。我想我只是为了它而迷上了metaclassery。如果它还不是很明显,我真的只是在学习元类,一时间看起来似乎是一个尝试它们的好机会。唉。

我相信以下解决方案尊重Liskov原则(谢谢Ignacio)的精神,同时赋予派生类以自己的方式引用派生方法的能力。类名称空间保持不变,其他对象可以在必要时使用其真实名称调用这些方法。

# 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

下次概述要求时,我会尽力做得更好。谢谢,伙计们。

答案 1 :(得分:0)

您可以尝试为每个班级添加字典。

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

每当你想要调用该函数时,你都会使用

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

它并不是特别优雅,但它可能会让你接近你所要求的。

答案 2 :(得分:0)

我倾向于同意对这个问题的评论:我怀疑你提出的问题会给代码增加不必要的复杂性,使得阅读更加难以理解。只是为了实施一个未成年人的化妆品&#34;可疑的好处。

但是,如果您真的想要这样做,也许您可​​以创建现有方法的同义词的方法,这样您就可以使用其原始名称调用方法或者使用&#34;定制的&#34;在合适的时候命名。

这是一种相当直接的方式。我想有一些方法可以用类装饰器来做,但我不知道如何使用它们。 :)

#! /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)

<强>输出

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