Pull to refresh

Comments 24

В качестве предложения. Было бы замечательно сопровождать рассказ об операциях с коллекциями информацией о сложности операций в О-нотации. Это полезная информация при выборе коллекций для использования.
В конце первой статьи приведены ссылки на информацию по алгоритмической сложности операций с коллекциями.
 a.append(b)
 print(a, b)        # [1, 2, 3, [4, 5]]  [4, 5]

Это — не конкатенация, уберите.

Чтоб два раза не вставать:
>>> a, b = [1, 2, 3], [4, 5]
>>> a + b  # вот это - конкатенация
[1, 2, 3, 4, 5]
>>> [*a, *b]  # работает на версии питона 3.5 и выше
[1, 2, 3, 4, 5]
>>> 

и, наконец,
>>> a += b  # эквивалентно a.extend(b)
>>> a
[1, 2, 3, 4, 5]

Большое спасибо за Ваши дельные замечания и дополнения!

Убрал термин конкатенация везде, поставил вместо него объединение, так как четкого однозначного определения не нашел, а суть указанных мной способов — объединение коллекций или добавление в нее нового элемента.
[*a, *b] - добавил этот способ с указанием Вашего авторства

Добавил комментарии
a += b  # эквивалентно a.extend(b)
a += [b]  # эквивалентно a.append(b)
a = set(['a', 'b'])

— этот синтаксис устарел лет десять назад, с появлением литерала множества:
a = {'a', 'b'}
Я бы ещё добавил, что все приведённые варианты копирования списков «наивные»:

>>> a = [1, [2, 3], 4]  
# В списке ссылка на объект 1, на объект (список), на объект 4
>>> b = a.copy()
# Новый объект список, в котором ровно те же ссылки, в частности ссылка на тот же список
>>> a[1].append(3.5)
# Так как ссылка на список общая, то и список общий
>>> print(b)
[1, [2, 3, 3.5], 4]


Визуализатор 1;

Если нужно копировать рекурсивно по полной:
>>> from copy import deepcopy
>>> c = deepcopy(a)
>>> a[1].append(3.75)
>>> print(c)
[1, [2, 3, 3.5], 4]
>>> print(a)
[1, [2, 3, 3.5, 3.75], 4]


Причём копирует всё достаточно аккуратно:
from copy import deepcopy

a = [1, 2]
b = [3, [[5, [a, 4], a], a, 6], a]
c = deepcopy(b)
a[1] = 9
print(b)
print(c)
c[2][1] = 3
print(c)
print(id(c[2]), id(c[1][1]), id(c[1][0][2]), id(c[1][0][1][0]))

[3, [[5, [[1, 9], 4], [1, 9]], [1, 9], 6], [1, 9]]
[3, [[5, [[1, 2], 4], [1, 2]], [1, 2], 6], [1, 2]]
[3, [[5, [[1, 3], 4], [1, 3]], [1, 3], 6], [1, 3]]
15864976 15864976 15864976 15864976

(аккуратно, но абсолютно всё. Функции и всё, что в них замкнуто, скажем, просто дублируются)

Визуализатор 2.
Большое спасибо за дельный детальный комментарий!
Добавил ссылку на него в конце статьи.
Для операций над множествами intersection и symmetric_difference имеется короткая форма записи:
a = {'a', 'b'}
b = {     'b', 'c'}
c = a & b  # c = a.intersection(b)
print(c)   # {'b'}
c = a ^ b  # c = a.symmetric_difference(b)
print(c)   # {'c', 'a'}
Большое спасибо за дополнение!
Добавил в соответствующие места в статье, с указанием Вашего авторства.

А так же, и для остальных двух:


>>> {'a', 'b'} - {'b', 'c'}  # difference
{'a'}
>>> {'a', 'b'} | {'b', 'c'}  # union
{'b', 'c', 'a'}
В Питоне 3.5 появился новый более изящный способ

На самом деле ещё во втором питоне можно было получать третий словарь путём слияния двух других:
dict3 = dict(dict1, **dict2)
Большое спасибо за дополнение!
Добавил в соответствующее место в статье, с указанием Вашего авторства.
> В Питоне 3.5 появился новый более изящный способ

чем это они изящный? Вот если бы + перегрузили это было бы изящно
Перегрузить + конечно было бы еще изящней, но это явно четче и понятней, чем комбинация .copy() и .update()
Собственно способ указанный renskiy выше тоже очень хорош, добавлю его чуть позже в статью.
Перегружать немного опасно, так как операция слияния словарей разрушительна.
Что должна делать
{1: 'one'} + {1: 'zero'}
?
Всевозможные варианты вида dict(dict1, **dict2) и dict(**dict1, **dict2) вернут {1: 'zero'}.
Операцию + над словарями можно заменить такой штукой.
>>> x = {"a": 1}
>>> y = {"b": 2}
>>> dict(x.items(), **y)
{'a': 1, 'b': 2}

Ну и + должен делать также, только при этом быть значительно читаемее

Согласен с ShashkovS, операция с потерей данных не должна проходить незаметно. Кроме того, для сложения свойственна коммутативность, которой при сложении словарей и не пахнет. Конкатенация списков, конечно, тоже не коммутативна, но там, хотя бы, интуитивно ясен порядок в результате.

согласен, значит не сложение, а какой-нибудь метод я-ля union или merge, но без магических символов в стиле си, не так
dict3 = dict(dict1, **dict2)

а так
dict3 = merge(dict1, dict2)

В целом, такая функция в 5 строк пишется. Но, как-то, это unpythonic, нет единственного способа сделать это правильно.

Вот по поводу магических символов согласился бы в том случае, если бы это было введено специально для объединения словарей, но это стандартный синтаксис передачи переменного числа параметров в функцию. Это гибкий и удобный способ, вполне в духе Питона.
Почему это не должно работать в функции создания словаря dict() — это ведь по сути такая же функция как и остальные?
Более того, при создании словаря не функцией dict(), а задав пары ключ: значение в фигурных скобках {} мы по сути имеем дело с коротким синтаксисом вызова той самой функции, так что вполне логично применять синтаксис по передаче переменного числа аргументов и тут.
Ну да, вариант который в 3.5 появился в эту логику вписывается, а вот вышеупомянутый вариант который был в ранних версиях чуток кривоват.
Один косяк — насколько понимаю и вариант добавленный в 3.5 не рекурсивный (сам не тестил, может ошибаюсь)
Кстати, важный момент это мерджинг вложенных словарей, то что в перле выглядит как merge($hash1, %hash2) в питоне какими-то замороченными способами делается
Sign up to leave a comment.

Articles