在Python中配置类的最优雅方式?

时间:2011-07-13 13:52:17

标签: python

我正在模拟一个分布式系统,其中所有节点都遵循某些协议。这包括评估协议中的一些小变化。变化意味着单个方法的替代实现。 所有节点始终遵循相同的变体,这由实验配置决定(在任何给定时间只有一个配置处于活动状态)。在不牺牲性能的情况下,最明智的方法是什么?

由于实验可能相当广泛,我显然不希望任何条件。在我刚使用继承之前,比如:

class Node(object):

    def dumb_method(self, argument):
        # ...    

    def slow_method(self, argument):
        # ...

    # A lot more methods

class SmarterNode(Node):

    def dumb_method(self, argument):
        # A somewhat smarter variant ...

class FasterNode(SmarterNode):

    def slow_method(self, argument):
        # A faster variant ...

但现在我需要测试所有可能的变种并且不希望指数数量的类混乱源。我还希望其他人偷看代码,以最小的努力来理解它。你有什么建议吗?

编辑:有一点我没有强调:对于所有预想的用例,似乎在配置时修补类是好的。我的意思是:它可以通过简单的Node.dumb_method = smart_method来工作。但不知怎的,它没有感觉正确。这种解决方案会给随机的智能读卡器造成严重的麻烦吗?

5 个答案:

答案 0 :(得分:2)

您可以使用__slots__机制和工厂类。您需要为每个实验实例化一个NodeFactory,但它会从那里为您创建Node个实例。例如:

class Node(object):
    __slots__ = ["slow","dumb"]

class NodeFactory(object):
     def __init__(self, slow_method, dumb_method):
         self.slow = slow_method
         self.dumb = dumb_method
     def makenode(self):
         n = Node()
         n.dumb = self.dumb
         n.slow = self.slow
         return n

示例运行:

>>> def foo():
...     print "foo"
...
>>> def bar():
...     print "bar"
...
>>> nf = NodeFactory(foo, bar)
>>> n = nf.makenode()
>>> n.dumb()
bar
>>> n.slow()
foo

答案 1 :(得分:2)

您可以使用type功能创建新的子类型。您只需将子类命名空间作为dict。

# these are supposed to overwrite methods
def foo(self):
    return "foo"

def bar(self):
    return "bar"


def variants(base, methods):
    """
        given a base class and list of dicts like [{ foo = <function foo> }] 
         returns types T(base) where foo was overwritten
    """
    for d in methods:
        yield type('NodeVariant', (base,), d)


from itertools import combinations
def subdicts(**fulldict):
    """ returns all dicts that are subsets of `fulldict` """
    items = fulldict.items()
    for i in range(len(items)+1):
        for subset in combinations(items, i):
            yield dict(subset)

# a list of method variants
combos = subdicts(slow_method=foo, dumb_method=bar)

# base class
class Node(object):
        def dumb_method(self):
            return "dumb"
        def slow_method(self):
            return "slow"

# use the base and our variants to make a number of types
types = variants(Node, combos)

# instantiate each type and call boths methods on it for demonstration
print [(var.dumb_method(), var.slow_method()) for var
          in (cls() for cls in types)]

# [('dumb', 'slow'), ('dumb', 'foo'), ('bar', 'slow'), ('bar', 'foo')]

答案 2 :(得分:0)

我不确定你是否正在尝试做类似于此的事情(允许换出运行时“继承”):

class Node(object):
    __methnames = ('method','method1')    

    def __init__(self, type):
        for i in self.__methnames:
            setattr(self, i, getattr(self, i+"_"+type))

    def dumb_method(self, argument):
        # ...    

    def slow_method(self, argument):
        # ...

n = Node('dumb')
n.method() # calls dumb_method

n = Node('slow')
n.method() # calls slow_method

或者如果您正在寻找类似的东西(允许运行(并因此测试)该类的所有方法):

class Node(object):
    #do something

class NodeTest(Node):

    def run_tests(self, ending = ''):
        for i in dir(self):
            if(i.endswith(ending)):
                meth = getattr(self, i)
                if(callable(meth)):
                    meth() #needs some default args.
                    # or yield meth if you can

答案 3 :(得分:0)

您可以使用metaclass

如果允许您根据每个变体动态创建一个类。

答案 4 :(得分:0)

在实例化类或之后,是否应该调用要调用的方法?假设它是在实例化类时,以下内容如何:

class Node():
    def Fast(self):
        print "Fast"

    def Slow(self):
        print "Slow"

class NodeFactory():
    def __init__(self, method):
        self.method = method

    def SetMethod(self, method):
        self.method = method

    def New(self):
        n = Node()
        n.Run = getattr(n, self.method)
        return n

nf = NodeFactory("Fast")

nf.New().Run()
# Prints "Fast"

nf.SetMethod("Slow")

nf.New().Run()
# Prints "Slow"