Когда я набираю {5}
в консоли, я получаю набор:
>>> {3}
{3}
Принимая во внимание, что set(5)
приводит к ошибке:
>>> set(5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
{}
или функцию set()
можно использовать для создания наборов.. Так почему это происходит?
По той же причине [3]
и list(3)
не одно и то же.
Что вы подразумеваете под "почему"? Вы спрашиваете о преимущество{}
синтаксис, о разница базовых конструкций или о чем-то еще?
@MisterMiyagi Из ответа я понял, почему set (5) не работает, но почему тогда {5} работает?
Из документов: docs.python.org/3/tutorial/datastructures.html#setsNote: to create an empty set you have to use set(), not {}; the latter creates an empty dictionary
@DeveshKumarSingh Это для пустого набора. type({5})
это <class 'set'>
@Snow {5}
не является вызовом функции; это набор буквальный, точно так же, как 5
является литералом int
.
set
мог были написаны так, чтобы принимать произвольное количество аргументов, так что set(1,2,3) == {1,2,3}
и set(5) == {5}
, но тогда вы сталкиваетесь с проблемой наборов итераций. Хотя set("ab", "cd")
явно будет {"ab", "cd"}
, каким должно быть set("ab")
? Это один набор со строкой из двух букв или набор из двух элементов строк из одной буквы?
Поскольку определение set
это:
class set([iterable])
Он не принимает отдельные значения, он принимает итерируемые значения один. Например.:
set([5, 6, 7])
Вы бы использовали литерал, когда жестко кодируете фиксированное количество значений или переменных:
{'foo', bar, baz()}
И вы бы использовали конструктор set
, когда у вас есть итерация один, которую вы хотите «распаковать» в набор:
set(baz)
set(foo(bar) for bar in baz)
set(map(foo, baz))
«k» также является отдельным значением, но set('k')
приводит к набору. Вы имеете в виду числовые типы данных?
@Snow [5,6,7]
— это также одно индивидуальное значение, но это итерируемое значение. 'k'
— это также итерируемое значение, которое (как ни странно) содержит ровно одну строку, 'k'
. (Строки в Python выглядят странно из-за отсутствия отдельного типа char
.)
Строки повторяемый. set('foo')
результаты в сете {'f', 'o'}
.
@chepner deceze Понял. Спасибо!
Когда вы определяете экземпляр набора для нескольких значений, значения не обязательно являются конкретными. Например, они могут быть лениво вычислены генератором:
values = (a%1 for a in range(2500000))
my_set1 = set(values) # values can be *any* iterable type
Это эффективно с точки зрения использования памяти, так как set
всегда содержит не более двух значений. Дубликаты удаляются по мере их появления.
Если бы 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 ...}
вкраплений.
Это не очень актуально. Это бонус, который set
может отбрасывать дубликаты при чтении их из ленивой итерации, но вы также можете написать set([0,1,0,1,0,1])
и получить набор из двух элементов.
Обратите внимание, что вы могли бы и написали понимание множества… {a%1 for a in range(2500000)}
@chepner Дело в том, что, когда set
и {}
работают таким образом, вы могу используете оптимальный путь, но можете этого не делать. Если бы они работали по-другому, вы не могли бы использовать оптимальный путь.
@deceze будет ли временная сложность понимания такой же, как set((a%1 for a in range(2500000)))
? Или ниже?
@deceze Я выбрал выражение генератора только для демонстрации. Дело в том, что аргумент set
может быть любого типа, тогда как тип аргумента {..}
статически определяется синтаксисом.
@Snow Сложность одинакова в любом случае, и практически оба делают одно и то же за кулисами - подают генератор в конструктор набора. В принципе, set может быть переопределен в вашем случае. Обратите внимание, что pypy
может оптимизировать литерал {}
примерно вдвое быстрее, чем использование set
.