如何根据参数向类添加函数

时间:2013-04-03 21:07:24

标签: python class function

我有一个班级(node)我要为其分配一组特定的功能(在这种情况下为a1a2b1,以及b2)基于类的参数(operatingMode)。

情况是我的电机有许多不同的操作模式。每种操作模式允许执行某些功能,但不允许执行其他功能。将函数分配给各种模式的方式不能很好地为每种操作模式创建类。

以下是我在解决方案中的通过,但它不起作用。

有什么想法吗?

def a1(self):
    return 'a1'

def a2(self):
    return 'a2'

def b1(self):
    return 'b1'

def b2(self):
    return b2




class node(object):
    def __init__(self,operatingMode):
    self.operatingMode=operatingMode

    if self.operatingMode=='A':
        self.a1function=a1
        self.a2function=a2
        print 'Operating Mode \'A\' functions loaded'

    if self.operatingMode=='B':
        self.b1function=b1
        self.b2function=b2
        print 'Operating Mode \'B\' functions loaded'

    def setOperatingMode(self,operatingMode):
        self.operatingMode=operatingMode
        self.__init__(self,operatingMode)

在我的终端中运行这个让我打电话,但我必须说两次手肘:

In [65]: elbow=node('A')
Operating Mode 'A' functions loaded

In [66]: elbow.a1function(elbow)
Out[66]: 'a1'

尝试运行elbow.setOperatingMode('B')会产生错误。

4 个答案:

答案 0 :(得分:2)

使用types.MethodType__getattr__

import types

def a1(self):
    return 'a1'

def a2(self):
    return 'a2'

def b1(self):
    return 'b1'

def b2(self):
    return 'b2'

class Node(object):
    def __init__(self, operatingMode):
        self.operatingMode = operatingMode
    def __getattr__(self, attr):
        if self.operatingMode=='A':
            if attr == 'a1function':
                return types.MethodType(a1, self)
            if attr == 'a2function':
                return types.MethodType(a2, self)

        elif self.operatingMode=='B':
            if attr == 'b1function':
                return types.MethodType(b1, self)
            if attr == 'b2function':
                return types.MethodType(b2, self)
        else:
            raise AttributeError()

然后

elbow = Node('A')
print(elbow.a1function())
elbow.operatingMode = 'B'
print(elbow.b2function())

产量

a1
b2

答案 1 :(得分:1)

也许使用checkMode装饰器会更清晰 - 这可以避免__getattr__type.MethodType魔术:

def checkMode(mode):
    def deco(func):
        def wrapper(self):
            if self.operatingMode == mode:
                return func(self)
            else:
                raise TypeError('Wrong operating Mode')
        return wrapper
    return deco

class Node(object):
    def __init__(self, operatingMode):
        self.operatingMode = operatingMode

    @checkMode('A')
    def a1(self):
        return 'a1'

    @checkMode('A')
    def a2(self):
        return 'a2'

    @checkMode('B')
    def b1(self):
        return 'b1'

    @checkMode('B')
    def b2(self):
        return 'b2'

使用上面的代码,我们可以这样做:

elbow = Node('A')
print(elbow.a1())
# a1

knee = Node('A')
print(knee.a1())
# a1

elbow.operatingMode = 'B'
print(knee.a1())  # knee is still in operatingMode A
# a1

print(elbow.b2())
# b2

elbow.a1()
# TypeError: Wrong operating Mode

<强>解释

装饰器语法的工作原理如下:

@deco
def func(): ...

相当于

def func(): ...
func = deco(func)

上面,checkMode是一个返回装饰器deco的函数。 deco然后装饰方法a1a2等,以便

a1 = deco(a1)

因此,a1是传递给deco的函数。反过来,deco(a1)会返回一种新方法,通常称为wrapper。此新方法由语句a1分配给a1 = deco(a1)。所以a1现在是方法wrapper。因此,当您致电elbow.a1()时,wrapper中的代码会被执行。

答案 2 :(得分:1)

回应此评论:

  

这是用于电机控制,所以我试图限制开销......   操作模式将比个人更频繁地改变   命令将被调用。

Python实例可以更改其类。您可以使用它来更改操作模式,而无需使用if - 子句来检查模式:

class Base(object):
    # Put any methods shared by ANode and BNode here.
    pass

class ANode(Base):
    def a1(self):
        return 'a1'

    def a2(self):
        return 'a2'

class BNode(Base):
    def b1(self):
        return 'b1'

    def b2(self):
        return 'b2'


elbow = ANode()
print(elbow.a1())
# a1

knee = ANode()
print(knee.a1())
# a1

elbow.__class__ = BNode
print(knee.a1())
# a1
print(elbow.b2())
# b2

elbow.a1()
# AttributeError: 'BNode' object has no attribute 'a1'

从积极的方面来看,这是我发布的最快建议。 请注意,上面的代码中没有if - 语句。一旦类发生变化,所有可用的方法都会“立即”改变,纯粹是由于普通的Python方法调用语义。

如果在装饰器解决方案中定义Node

In [33]: elbow = Node('A')

In [34]: %timeit elbow.a1()
1000000 loops, best of 3: 288 ns per loop

如果使用knee定义ANode

In [36]: knee = ANode()

In [37]: %timeit knee.a1()
10000000 loops, best of 3: 126 ns per loop

所以这个解决方案在调用方法时的速度是装饰器解决方案的2倍。

开关速度可比:

In [38]: %timeit elbow.operatingMode = 'B'
10000000 loops, best of 3: 71.7 ns per loop

In [39]: %timeit knee.__class__ = BNode
10000000 loops, best of 3: 78.7 ns per loop

警告:有一件事会困扰我发布的所有解决方案,在切换名称后,可用的方法会发生变化。这意味着当您使用这些类进行编程时,您必须先跟踪实例的状态,然后才能知道可用的方法。这很尴尬。

如果ANodeBNode 完全相同的界面 - 所有相同的属性和方法名称 - 只有定义,您的程序会简单得多那些改变的方法(当模式 - 或类 - 改变时)。


关于这个评论:

  

我有大约一百个功能和七种操作模式。那些   在所有操作模式之间共享大约10个函数,75个是   由多种模式共享,15种是特定模式独有的。   问题是75没有很好地分配给模式:一些   可能在模式1,4和7中,其他在2,4,5和7中,其他在1和1中   5。

您可以在类之外定义方法,然后将它们“连接”到基于模式的类,如下所示:

def a1(self):
    return 'a1'

def a2(self):
    return 'a2'

def b1(self):
    return 'b1'

def b2(self):
    return 'b2'

class Base(object):
    # Put the 10 methods share by all modes here
    def common1(self): 
        pass

class ANode(Base):
    a1 = a1
    a2 = a2

class BNode(Base):
    b1 = b1
    b2 = b2

class CNode(Base):
    a1 = a1
    b2 = b2

答案 3 :(得分:0)

您的整个设计非常奇怪,您可能需要退后一步并解释您正在尝试做什么,以便有人可以帮助您进行更好的设计。

但是如果你只是想让你的设计工作,你当前的代码有两个问题:

self.a1function = a1

这会将self.a1function设置为常规函数,而不是绑定方法。您可以显式创建这样的绑定方法:

self.a1function=types.MethodType(a1, self, self.__class__)

或者您可以将a1function设置为a1的包装器。或者,更简单地说,动态地进行包装,这意味着您可以使用闭包来伪装bound-method-ness,这可以说是更具可读性:

def __getattr__(self, attr):
    if attr == 'a1function' and self.operating_mode == 'A':
        return lambda: a1(self)

同时

  

尝试运行elbow.setOperatingMode('B')会产生错误。

你真的需要发布回溯,或者至少发布错误字符串,而不是仅仅说“产生错误”。在这种情况下,它会非常明确地告诉您错误是什么,因此您无需猜测:

TypeError: __init__() takes exactly 2 arguments (3 given)

问题在于这一行:

self.__init__(self,operatingMode)

...你传递self两次。它是你调用方法的对象,它也是第一个参数。

无论如何从另一种方法调用__init__是一个坏主意,但是如果你真的想要,那就像调用任何其他方法一样:

self.__init__(operatingMode)