Python调用返回布尔值的方法,比如'issuperset',内存友好?
我编写了一些代码,假设对超集的检查会对内存友好,并且会导致较少的碎片,因为它返回一个布尔值(一个_列表总是不大于2个非常小的字符串元素,顺序与Python调用返回布尔值的方法,比如'issuperset',内存友好?,python,c,set,cpython,superset,Python,C,Set,Cpython,Superset,我编写了一些代码,假设对超集的检查会对内存友好,并且会导致较少的碎片,因为它返回一个布尔值(一个_列表总是不大于2个非常小的字符串元素,顺序与foo和bar相同)。e、 g 我认为上面的比下面的更可取,如果只是为了可读性,也是因为,我希望它不会创建任何其他对象,因为它只返回布尔值 def are_args_ok(a_list): if [i for i in a_list if i not in ['foo', 'bar']]: # expected to run a lot
foo
和bar
相同)。e、 g
我认为上面的比下面的更可取,如果只是为了可读性,也是因为,我希望它不会创建任何其他对象,因为它只返回布尔值
def are_args_ok(a_list):
if [i for i in a_list if i not in ['foo', 'bar']]: # expected to run a lot
raise ValueError('bad value in a_list') # virtually never
然而,我并不清楚Python的所有内部工作原理。因此,我一直在阅读,似乎检查超集会创建另一个的集合对象(如果它还不是集合):
static PyObject *
set_issuperset(PySetObject *so, PyObject *other)
{
PyObject *tmp, *result;
if (!PyAnySet_Check(other)) {
tmp = make_new_set(&PySet_Type, other);
if (tmp == NULL)
return NULL;
result = set_issuperset(so, tmp);
Py_DECREF(tmp);
return result;
}
return set_issubset((PySetObject *)other, (PyObject *)so);
}
所以,当我把一个列表作为另一个列表时,我似乎创建了一个新的集合,所以我的假设是错误的,即使它更具可读性。我认为第二段代码实际上可能更快,至少在我使用Python2.6进行测试时是这样。所以我的问题是,在内存性能和碎片方面,第一个代码比第二个代码更可取吗
是否有一种严格的主导方法我还没有考虑过
编辑: 这回答了有关性能的相关问题:
must= '''MUST=set(['a','b'])
def validate(vals):
if not MUST.issuperset(vals):
raise Exception'''
mustdiff= '''MUST=set(['a','b'])
def validate(vals):
if set(vals) - MUST:
raise Exception'''
must2= '''def validate(vals):
if not set(['a','b']).issuperset(vals):
raise Exception'''
old_list = '''def validate(vals):
if [i for i in vals if i not in ['a','b']]:
raise Exception
'''
old_tup = '''def validate(vals):
if [i for i in vals if i not in ('a','b')]:
raise Exception
'''
test = "validate(['a']); validate(['a', 'b'])"
def main():
print timeit.repeat(test, setup=must)
print timeit.repeat(test, setup=mustdiff)
print timeit.repeat(test, setup=must2)
print timeit.repeat(test, setup=old_list)
print timeit.repeat(test, setup=old_tup)
产出:
[0.90473995592992651, 0.90407950738062937, 0.90170756738780256]
[1.0068785656071668, 1.0049370642036592, 1.0076947689335611]
[1.4705243140447237, 1.4697376920521492, 1.4727534788248704]
[0.74187539617878429, 0.74010685502116758, 0.74236680853618964]
[0.74886594826284636, 0.74639892541290465, 0.74475293549448907]
我认为第二段代码实际上可能更快,至少当我
使用Python2.6进行测试
如果真是这样的话,我会感到震惊——有问题的名单有多大?我的猜测是,将列表转换为集合可能会有一些固定的开销,这会抵消使用集合操作带来的任何性能好处
设置此类操作的操作。我希望issuperset
方法能为您提供最佳性能,然后可能是:
if not set(a_list) - OK_SET:...
使用O(len(a_列表))
performance。请注意,使用全局变量OK\u SET
也会严重影响性能
这就是说:除非您测试包含数千个元素的集合,否则差异可能可以忽略不计。过早优化是万恶之源。如果您的生产代码实际上只测试了两个元素,我怀疑您是否会发现很大的差异
我认为第二段代码实际上可能更快,至少当我
使用Python2.6进行测试
如果真是这样的话,我会感到震惊——有问题的名单有多大?我的猜测是,将列表转换为集合可能会有一些固定的开销,这会抵消使用集合操作带来的任何性能好处
设置此类操作的操作。我希望issuperset
方法能为您提供最佳性能,然后可能是:
if not set(a_list) - OK_SET:...
使用O(len(a_列表))
performance。请注意,使用全局变量OK\u SET
也会严重影响性能
这就是说:除非您测试包含数千个元素的集合,否则差异可能可以忽略不计。过早优化是万恶之源。如果您的生产代码实际上只测试了两个元素,我怀疑您是否会发现有太大的差异。对于如此少的项目,这似乎比我尝试的其他几项稍快一些:
.
.
.
kiss = '''MUST=['a','b']
def validate(vals):
for i in vals:
if i not in MUST:
raise Exception
'''
test = "validate(['a']); validate(['a', 'b'])"
def main():
print ' must:', min(timeit.repeat(test, setup=must))
print 'mustdiff:', min(timeit.repeat(test, setup=mustdiff))
print ' must2:', min(timeit.repeat(test, setup=must2))
print 'old_list:', min(timeit.repeat(test, setup=old_list))
print ' old_tup:', min(timeit.repeat(test, setup=old_tup))
print ' kiss:', min(timeit.repeat(test, setup=kiss))
if __name__ == '__main__':
main()
对于数量如此之少的项目,这似乎比我尝试的其他几个项目要快一些:
.
.
.
kiss = '''MUST=['a','b']
def validate(vals):
for i in vals:
if i not in MUST:
raise Exception
'''
test = "validate(['a']); validate(['a', 'b'])"
def main():
print ' must:', min(timeit.repeat(test, setup=must))
print 'mustdiff:', min(timeit.repeat(test, setup=mustdiff))
print ' must2:', min(timeit.repeat(test, setup=must2))
print 'old_list:', min(timeit.repeat(test, setup=old_list))
print ' old_tup:', min(timeit.repeat(test, setup=old_tup))
print ' kiss:', min(timeit.repeat(test, setup=kiss))
if __name__ == '__main__':
main()
临时设置将在
issupertt
返回之前消失。如果a_list
总是很小,它很可能总是为集合分配相同的单个插槽。这并不意味着它会更快,但它确实更方便。通常最多两个元素。哦,顺便说一句,如果你能为OK\u SET
编写['foo','bar']
,你应该尝试('foo','bar')
,这样可以避免在测试中的每个上创建一个列表(它在编译时创建一次元组,并在需要时加载)。你能链接到源代码吗?我还没有深入研究函数编译。实现可能分布在许多单独的文件中(此优化涉及字节码编译、pyc
格式和解释器循环),但您可以使用dis
观察它(注意加载常量
操作码)。临时设置将在issupertt
返回之前消失。如果a_list
总是很小,它很可能总是为集合分配相同的单个插槽。这并不意味着它会更快,但它确实更方便。通常最多两个元素。哦,顺便说一句,如果你能为OK\u SET
编写['foo','bar']
,你应该尝试('foo','bar')
,这样可以避免在测试中的每个上创建一个列表(它在编译时创建一次元组,并在需要时加载)。你能链接到源代码吗?我还没有深入研究函数编译。实现可能分布在许多单独的文件中(此优化涉及字节码编译、pyc
格式和解释器循环),但您可以使用dis
(注意加载常量
操作码)。列表非常小,请参见编辑介绍段落附近的内容。我使用的是Python2.6,与每次调用都创建全局变量相比,全局变量在性能上占绝对优势+1尽管如此,我还是想提供帮助。如果转换为集是罪魁祸首,如果不设置(a_列表)-OK_set
不会改善这一点(它实际上会创建两个集,一个像内置的issuperset
,另一个是-
的结果)。@AaronHall你测试过吗?我不相信使用局部变量会导致每次调用都重新创建它。如果做不到这一点,请将其用作函数参数的默认值——这肯定只计算一次。但是有一个@delnan是的,我并不是说这个版本可以避免开销——我说它会的