Когда вы определяете экземпляр набора для нескольких значений, значения не обязательно являются конкретными. Например, они могут быть лениво вычислены генератором:
values = (a%1 for a in range(2500000))
my_set1 = set(values) # values can be *any* iterable type
Это эффективное использование памяти, поскольку set
содержит только 2 значения. Дубликаты удаляются по мере их возникновения.
Если set
будет принимать отдельные значения, вам придется *
- распаковать ленивую итерацию во временный кортеж. Промежуточный кортеж будет содержать все 2500000 значений.
def inefficient_set(*items): # items is an intermediate tuple
return set(items)
values = (a%1 for a in range(2500000))
my_set2 = inefficient_set(*values)
Поскольку set
принимает итерацию, вы используете промежуточный контейнер только тогда, когда вам это нужно.
Когда вы определяете буквальный набор из нескольких значений, тогда все значения уже являются конкретными. Точно так же, когда вы определяете буквальный набор ленивых значений, они также являются конкретными.
my_set3 = {0, 1, 0, 1, 0, 1} # values are known to be concrete
my_set4 = {a%1 for a in range(2500000)} # values are known to be lazy
В этом случае для итерации итерация потребуется бесполезный промежуточный контейнер. Если {}
принимает индивидуальные значения, промежуточный контейнер используется только тогда, когда он вам нужен.
Важно учитывать, что {...}
- это синтаксис , тогда как set(...)
- это обычный экземпляр типа . В Python синтаксис статический, а типы динамические. Это позволяет статически отличать {a, b, c, ...}
литералы конкретных значений от {... for ... in ...}
пониманий.