如何以编程方式创建对象方法?

时间:2017-04-05 15:04:03

标签: python

是否可能以编程方式在python中创建对象方法?

由于使用了命令行解析器的现有结构,我需要任意数量(N)的方法,其中(N)是在运行时建立的。

def activatetool_>N<():
  print 'activating tool number: >N<'

我设法接近:

class TestClass:
    def __init__(self, toolcount):
        for i in range(toolcount):
            exec('def activatetool_{}(): print \'activating tool {}\''.format(i,i)) in globals()

然而,这定义了全局函数,而不是类方法。通过设计我正在使用的现有代码,我需要能够以下面的形式调用它们:

obj=TestClass(5)
obj.activatetool3()
obj.activatetool1()

澄清:由于我正在使用的现有解析器的结构,重构为obj.activatetool(N)形式的解决方案是行不通的。

2 个答案:

答案 0 :(得分:3)

python方法只是该类的一个属性,它恰好是一个接受该类实例作为其第一个参数的函数。因此,您只需使用setattr将新方法绑定到现有类。

从您的示例中,您可以创建一个添加工具的功能:

def addTool(cls, n):
    def tool(self):
        print ('activating tool number >{}<'.format(n))
    setattr(cls, "activatetool{}".format(n), tool)

然后,您可以创建一个类,它的实例,添加工具并成功使用该工具:

class TestClass:
    pass
t = TestClass()
addTool(TestClass, 3)
t.activatetool3()

你按预期得到了:

activating tool number >3<

Python的神奇之处在于,由于动态方法是类的一个属性,所以即使在添加方法之前创建了它们,它也可以被所有类实例访问。

答案 1 :(得分:1)

只需将N作为参数传递给activatetool

class TestClass:
    def activatetool(self, N):
        print "activating tool number: {}".format(N)

obv=TestClass()
obv.activatetool(3)
obv.activatetool(1)

结果:

activating tool number: 3
activating tool number: 1

如果您完全没有将数字保留在括号之外,您可以通过覆盖__getattr__来获得所需的行为:

import re

class TestClass:
    def __getattr__(self, name):
        m = re.match("activatetool(\d*)$", name)
        if m:
            N = int(m.group(1))
            def activatetoolN():
                print("Activating tool number: {}".format(N))
            return activatetoolN
        else:
            raise AttributeError

obv=TestClass()
obv.activatetool3()
obv.activatetool1()

结果:

Activating tool number: 3
Activating tool number: 1

但是,obv.activatetool3的类型为function,而普通实例方法的类型为instancemethod。大多数情况下,您无法区分,但是您班级中异常严格的用户可能会注意到这种差异。

由于每次访问activatetoolN时都会从头开始创建obv.activatetool<N>,因此性能损失也会从小到中等。