我有一个班级(node
)我要为其分配一组特定的功能(在这种情况下为a1
,a2
,b1
,以及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')
会产生错误。
答案 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
然后装饰方法a1
,a2
等,以便
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
警告:有一件事会困扰我发布的所有解决方案,在切换名称后,可用的方法会发生变化。这意味着当您使用这些类进行编程时,您必须先跟踪实例的状态,然后才能知道可用的方法。这很尴尬。
如果ANode
和BNode
完全相同的界面 - 所有相同的属性和方法名称 - 只有定义,您的程序会简单得多那些改变的方法(当模式 - 或类 - 改变时)。
关于这个评论:
我有大约一百个功能和七种操作模式。那些 在所有操作模式之间共享大约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)