动态创建具有可变数量语句的功能

时间:2017-03-11 23:13:33

标签: python python-2.7 function

我知道大多数时候都不推荐exec和eval,但我想动态创建一个具有可变数量语句的函数。我想这样做的原因是因为产生3个函数和1个语句,每个函数的性能都比具有3个语句的1个函数差。

基本上我有一些对象,会从输入中读取字段和对象。

对于可变数量的obj,我想为它们组装一个函数,而不是创建许多手动函数。有没有办法避免 (或者有没有办法生成make_funcN?)

def make_func1( obj1, field1, obj2, field2 ):
  def copy():
    obj1.__setattr__( field1, obj2.__getattribute__( field2 ) )
  return copy

def make_func2( obj1, field1, obj2, field2, \
                obj3, field3, obj4, field4 ):
  def copy():
    obj1.__setattr__( field1, obj2.__getattribute__( field2 ) )
    obj3.__setattr__( field3, obj4.__getattribute__( field4 ) )
  return copy

def make_func3( ... ):
  ...

谢谢,

4 个答案:

答案 0 :(得分:3)

首先,您应该使用内置getattrsetattr代替__setattr____getattribute__;为了便于阅读,不要交易微型性能提升。

然后,您可以将args分组为四肢,然后相应地设置并获取每对对象的属性:

def make_func(*args):
   if len(args) % 4 != 0:
       raise ValueError("number of arguments must be a multiple of 4")
   def copy():
       for i in range(0, len(args), 4):
           setattr(args[i], args[i+1], getattr(args[i+2], args[i+3]))
   return copy

答案 1 :(得分:0)

def make_func1(*args):
    assert len(args) % 4 == 0

    def copy():
        for i in range(0, len(args), 4):
            obj1, field1, obj2, field2 = args[i:i + 4]
            obj1.__setattr__(field1, obj2.__getattribute__(field2))

    return copy

答案 2 :(得分:0)

您可以将对象和字段作为列表或元组传递:

def make_func(to_objs, to_fields, from_objs, from_fields):
    def copy():
        for to_obj, to_field, from_obj, from_field in zip(to_obj, to_fields, 
                                                          from_objs, from_fields):
            to_obj.__setattr__(to_field, from_obj.__getattribute__(from_field))
    return copy

答案 3 :(得分:0)

这不是OP所要求的。我没有创建一系列主要在arity中不同的类似属性复制函数,而是提出了一些简单函数和这个问题的“零假设”:创建多个类似函数是出于善意,但不是有效优化这种情况。

最简单的方法是单一复制功能,迭代次数与设置字段的次数相同:

def copyattr(obj1, field1, obj2, field2):
     setattr(obj1, field1, getattr(obj2, field2))

根据需要多次调用以设置您喜欢的所有属性。 它简单,Pythonic,并且基于我运行的基准测试,表现非常好。

如果你想要一个复制指令的平面列表(由扩展参数列表建议),那么:

def copyattrs(*args):
    for i in range(0, len(args), 4):
        setattr(args[i], args[i+1], getattr(args[i+2], args[i+3]))

会做的伎俩。它以与以前相同的顺序采用任意长的平坦参数序列。除了需要设置步骤(copy())并通过闭包传输参数之外,回复make_func()函数与嵌入循环的几个答案基本上是这样的。

如果您只使用Python 3,则可以使用扩展参数解压缩语法缩短几纳秒:

def copyattrs(*args):
    for i in range(0, len(args), 4):
        setattr(*args[i:i+2], getattr(*args[i+2:i+4]))

但我尝试过的任何变种 - 包括生成特定arity的复制功能 - 在这项任务中都有很大的不同。基于Python数据处理语义,这些方法都不会复制太多数据。它们都复制指针,而不是大型数据集,并且几乎以任何方式执行它都非常有效。与原始数据副本相比,参数构造和访问以及函数调用设置/拆除消耗了大部分工作。我必须运行大量的拷贝 - 1,000,000到20,000,000 - 甚至开始花费有趣的时间。即使是200,000,000个属性副本的完整基准测试也只花了几秒钟。这包括每次将测试状态重置为接地零所需的完整数据集复制。

长话短说,这种“创建动态属性复制功能”的方法似乎没什么优势。这是一个有用的实验,但我运行的基准测试都没有显示出明显更快的结果 - 特别是如果你考虑(你应该)创建自定义属性复制功能所需的时间,然后将参数编组到其中。根据使用方式的不同,在内存中生成和保留大量闭包也存在潜在的重大开销。在我运行的场景中,闭包驱动的无参数copy()实际上是最差的基准。 (对于较大的武装,可怕的较慢。)

杰出的计算机科学家CAR Hoare和Donald Knuth着名地说“过早优化是所有邪恶的根源”。因此,在您采用花哨的优化之前,请确保您知道真正的性能问题在哪里,并且您提出的解决方案实际上是一种改进。 Ned Batchelder nails this

  

......“你不知道问题出在哪里。”我记不清有多少次我提出了一个关于加快速度的尝试,尝试过它并且没有用。

他所指的文章,Russ Olsen的Five Truths about Optimization,是一本关于加快速度的可靠读物和良好指导。如需精辟的书本长度操作方法,请尝试使用Jon Bentley的编写高效程序