如何从Python中的单个函数委托许多函数?

时间:2013-01-04 07:55:12

标签: python

我需要在Python上的多个节点上的系统上运行许多命令。我希望每个节点独立运行,所以我想我会为每个节点创建一个类,并用特定于节点的方法填充它。但是当编写我的算法来使用这些方法时,我发现我必须将每个调用放在for循环中:

class MultipleThings(object):
    def __init__(self, index):
        self.index = index

    def some_function(self):
        print "I am index ", self.index

class CollectionOfThings(object):
    def __init__(self):
        self.list_of_objects = []
        for index in range(5):
            self.list_of_objects.append(MultipleThings(index))

        for thing in self.list_of_objects:     # Yuck!!
            thing.some_function()

main = CollectionOfThings()

此代码完美运行,并为我提供了5个打印语句。但这是丑陋乏味的。我怎样才能摆脱“for self.list_of_objects:”中的东西,只需调用self.some_function()一次,然后让装饰者对我的事物列表中存在的所有东西进行for循环?

编辑:我想你可以认为这是一种“批量”模式吗?例如,我想在10个系统上运行“ls -l”,然后让每个类实例处理这些命令。完成所有操作后,下一个命令可能是所有10个系统上的“rm tmp”,依此类推。还需要多处理,以便for循环不会在每个节点上阻塞。

2 个答案:

答案 0 :(得分:0)

不确定这是否符合您的要求:

class CollectionOfThings(object):
    def __init__(self):
        self.list_of_objects = []
        self.list_of_objects = [MultipleThings(index) for index in range(5)]
        self.callFunc(self.list_of_objects, "some_function")

    def callFunc(self, items, func, *args, **kwargs):
        for thing in items:
            getattr(thing, func)(*args, **kwargs)

正如@Thorsten指出的那样,只需将maplambda一起使用即可实现您的目标。我的尝试是做出一般解决方案。

您可以像这样传递函数,以避免传递字符串参数。

class CollectionOfThings(object):
    def __init__(self):
        self.list_of_objects = []
        self.list_of_objects = [MultipleThings(index) for index in range(5)]
        self.callFunc(self.list_of_objects, MultipleThings.some_function)

    def callFunc(self, items, func, *args, **kwargs):
        for thing in items:
            func(thing, *args, **kwargs)

这将返回列表和字典:

class MultipleThings(object):
    def __init__(self, index):
        self.index = index

    def some_function(self, key, value):
        return "I am index %s with key %s and value %s" % (self.index, key, value)

class CollectionOfThings(object):
    def __init__(self):
        self.list_of_objects = []
        self.list_of_objects = [MultipleThings(index) for index in range(5)]
        retList, retDict = self.callFunc(self.list_of_objects, MultipleThings.some_function, "foo", value="bar")
        print retList, retDict

    def callFunc(self, items, func, *args, **kwargs):
        retList = []
        retDict = {}
        for i, thing in enumerate(items):
            funcReturn = func(thing, *args, **kwargs)
            retList.append(funcReturn)
            retDict[i] = funcReturn

        return retList, retDict

答案 1 :(得分:0)

听起来你有一个list,比方说10项,然后你想在list的每个项目上运行50个函数,你不想写50个for循环。

一种可能性是编写一个包装它的函数:

def apply(method, seq):
    for item in seq:
        method(item)

然后,而不是这个:

for thing in self.list_of_objects:     # Yuck!!
    thing.some_function()
for thing in self.list_of_objects:     # Yuck!!
    thing.other_function()
for thing in self.list_of_objects:     # Yuck!!
    thing.third_function()
# ... 47 more times

你可以这样做:

apply(ThingClass.some_function, self.list_of_objects)
apply(ThingClass.other_function, self.list_of_objects)
apply(ThingClass.third_function, self.list_of_objects)
# ... 47 more times

函数和方法是Python中的第一类对象,就像其他任何东西一样,因此您可以轻松地将它们传递给其他函数。当然first_thing.some_functionsecond_thing.some_function实际上是不同的绑定方法;诀窍是你可以从类中获取未绑定的方法,然后将对象作为显式self参数传递给它。 (尝试打印出first_thing.some_functionThingClass.some_functiontype(first_thing.some_function)type(ThingClass.some_function),然后在交互式翻译中播放它们。)

当然,这假设所有这些项具有相同方法的原因是它们是同一类的实例。如果那不是真的,而你依赖于鸭子打字,那么你需要一些稍微不同的东西:

def apply(methodname, seq):
    for item in seq:
        getattr(item, methodname)()

apply('some_function', self.list_of_objects)
# ... 49 more times

这里的技巧是getattr函数,它允许您在运行时按名称获取任何方法或成员。

但是如果你考虑一下,你真正想要的是迭代这50个未绑定的方法或名称,而不必重复自己。例如:

for name in ('some_function', 'other_function', 'third_function',
             # ... 47 more names
            ):
    for obj in self.list_of_objects:
        getattr(obj, name)()

请注意,apply方法的第一个版本可以很容易地被内置的map替换(仅在Python 2.x中,而不是3.x),甚至是列表理解。但是,有一些原因没有这样做:

  • 当你只是为了副作用而调用函数时,通常认为使用map或理解是有问题的。
  • 如果您有批次项目,而不是仅仅50 * 10,则会浪费内存和时间来构建您不需要的list个结果。
  • 在Python 3中,map返回一个迭代器而不是list,因此它根本不会实际执行这些函数,除非你以某种方式迭代它。 (你今天显然使用的是2.x,因为你的print语句,但大概有一天你会切换到3.x.)

您可以使用itertools.imap(或生成器表达式)代替map(或列表推导)来解决这些问题,并通过例如将其提供给0大小来处理迭代deque。但在那时,你的代码正在做什么并不完全清楚。

同时,明确地编写函数意味着当您发现需要时,很容易将其修改为例如第二个版本。