Я написал некоторый код, предполагая, что проверка надмножества будет дружественной к памяти и приведет к меньшей фрагментации, поскольку она возвращает логическое значение (a_list is всегда не больше, чем 2 элемента очень маленьких строк в том же порядке как foo
и bar
). например
OK_SET = set('foo', 'bar')
def are_args_ok(a_list):
if not OK_SET.issuperset(a_list): # expected to run a lot
raise ValueError('bad value in a_list') # virtually never
И я посчитал вышеизложенное предпочтительнее нижеследующего хотя бы для удобства чтения, но и потому, что Я предположил, что лучше не создавать множество ненужных списков, и я надеюсь, что он не создаст никаких других объектов, поскольку возвращает только логическое значение.
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. Поэтому я читал исходный код CPython (отрывок ниже) и кажется, что проверка надмножества создает объект набора другого, если он еще не является набором:
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);
}
Итак, похоже, что я создаю новый набор, когда мне дается список в качестве другого, поэтому мое предположение было неверным, даже если оно более читабельно. Я думаю, что второй код на самом деле может быть быстрее, по крайней мере, когда я тестирую Python 2.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]
issuperset
вернется. Еслиa_list
всегда мало, вполне может быть, что он всегда выделяет один и тот же слот для набора. Не значит, что так будет быстрее, но точно удобнее. - person   schedule 04.06.2014['foo', 'bar']
дляOK_SET
, вам следует попробовать('foo', 'bar')
, что позволит избежать создания списка для каждого отдельного тестаin
(кортеж создается один раз во время компиляции и загружается при необходимости). - person   schedule 04.06.2014pyc
и цикла интерпретатора), но вы можете наблюдать за ней, используяdis
(обратите внимание на код операцииLOAD_CONST
). - person   schedule 04.06.2014