Python:将函数动态添加到类,其名称包含在字符串中

时间:2013-10-23 02:41:59

标签: python python-2.7

创建函数名包含为字符串的类的新成员函数的最佳方法是什么?此外,这个新函数仅仅是另一个对象(辅助类)的传递,它具有相同的函数名但具有可变参数。我使用lambda来实现这一点,但我不知道如何处理这个场景,我的传递包装器将不止一个语句(这是我的要求)

# This is a helper class
class Compensation:
   def bonus(self):
       return 10000
   def salary(self):
       # Do something
   def stack(self):
       # Do something

# This is a employer class
class employee:
   def __init__(self):
       self.compensation = Compensation()

# This is a wrapper that creates the function
def passThru(funcName):
    fn = "employee."+funcName+"=" + "lambda self, *arg: self.compensation." + funcName +"(*arg)"
    exec(fn)

fnNames = ["bonus", "salary", "stocks"]
for items in fnNames: passThru(items)

emp = employee()
emp.bonus() # returns 1000

3 个答案:

答案 0 :(得分:5)

exec的所有诡计让我很头疼;-)我不清楚你想做什么,但添加一个名字由字符串给出的新方法真的很容易。例如,

class employee:
    pass

# Some multiline-function.
def whatever(self, a, b):
    c = a + b
    return c

e = employee()

# Make `whatever` an `employee` method with name "add".
setattr(employee, "add", whatever)
print e.add(2, 9)

每当你到达exec时,你可能错过了一种直截了当的方式。

编辑:这里的一个奇怪之处是,如果有人试图显示e.add,他们会得到一个声称其名称为whatever的字符串。如果这困扰你,你可以添加,例如,

whatever.__name__ = "add"

充实它

这更接近你想要的吗?请注意,@gnibbler的建议大致相同,但更多的是电报:

class Compensation:
    def bonus(self, a):
        return 10000 + a
    def salary(self):
        return 20000
    def stack(self, a=2, b=3):
        return a+b

class employee:
    def __init__(self):
        self.comp = Compensation()


e = employee()

for name in "bonus", "salary", "stack":
    def outer(name):
        def f(self, *args, **kw):
            return getattr(self.comp, name)(*args, **kw)
        f.__name__ = name
        return f
    setattr(employee, name, outer(name))

print e.bonus(9)
print e.salary()
print e.stack(b="def", a="abc")

显示:

10009
20000
abcdef

所有这些,你可能想重新考虑你的架构。它很紧张。

答案 1 :(得分:2)

你想要setattr。假设你有:

>>> inst = Foo(10)
>>> class Foo(object):
    def __init__(self, x):
        self.x = x

>>> inst = Foo(10)
>>> inst2 = Foo(50)

如果要向该类的所有实例添加方法,则在类上添加setattr。这个函数最终将成为类中的一个未绑定方法,在每个实例中都会绑定,因此它将采用self参数:

>>> setattr(inst.__class__, "twice_x", lambda self: self.x * 2)
>>> inst.twice_x()
20
>>> inst2.twice_x()
100

如果要将该函数仅添加到该类的一个实例中,则在实例本身上添加setattr。这将是一个常规函数,它不会采用隐式self参数:

>>> setattr(inst, "thrice_x", lambda: inst.x * 3)
>>> inst.thrice_x()
30
>>> inst2.thrice_x()

Traceback (most recent call last):
  File "<pyshell#16>", line 1, in <module>
    inst2.thrice_x()
AttributeError: 'Foo' object has no attribute 'thrice_x'    

答案 2 :(得分:2)

您正在寻找setattr / getattr

for func_name in fnNames:
    setattr(employee, func_name, (lambda self, *args:getattr(self.compensation, func_name)(*args)))

这仍然有问题,因为你需要lambda函数closed而不是func_name。虽然您可以使用另一个lambda创建一个闭包,但我会将其拉出到另一个函数中以便于阅读

for func_name in fnNames:
    def f(func_name):  # close the lambda over "func_name"
        return lambda self, *args:getattr(self.compensation, func_name)(*args)

    setattr(employee, items, f(func_name))