Python调用返回布尔值的方法,比如'issuperset',内存友好?

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

我编写了一些代码,假设对超集的检查会对内存友好,并且会导致较少的碎片,因为它返回一个布尔值(一个_列表总是不大于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
        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是的,我并不是说这个版本可以避免开销——我说它会的