我知道大多数时候都不推荐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( ... ):
...
谢谢,
答案 0 :(得分:3)
首先,您应该使用内置getattr
和setattr
代替__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的编写高效程序。