- 1
- 2
- 3
- 4
- 5
- 6
- 7
import json
a = {}
b = {}
for i in xrange(128): a[str(i)] = i
for i in a: b[i] = a[i]
print a == b
print json.dumps(a) == json.dumps(b)
Нашли или выдавили из себя код, который нельзя назвать нормальным, на который без улыбки не взглянешь? Не торопитесь его удалять или рефакторить, — запостите его на говнокод.ру, посмеёмся вместе!
0
import json
a = {}
b = {}
for i in xrange(128): a[str(i)] = i
for i in a: b[i] = a[i]
print a == b
print json.dumps(a) == json.dumps(b)
Результат:
True
False
Почему не True True ?
а) питух дерьмо
б) for i in xrange и i in a не гарантируют одинаковый порядок ключей
в) на сравнение словарей это влияет, а вот на сравнение строк еще как
г) возвращаемся к тому, что питух дерьмо, ибо мапа не должна зависеть от порядка объявления ключей
д) сам-то принт чего не сделаешь да не посмотришь
Лучше бы отсортировали по алфавиту. Кому нахуй нужен этот порядок добавления?
Я вот в другом порядке вторую мапу набью. И она будет равна первой т.к. при сравнении порядок не играет роли. А json представление у них будет разное т.к. порядок обхода разный. Т.е. тройка не решает эту проблему.
З.Ы. А решить эту проблему можно только сортировкой перед сериализацией в жсон. Почти как DER в ASN.1
Какую проблему-то? В «Питоне» «JSON» — это просто строка, о её внутренностях «Питон» ничего не знает и знать не должен. Никакой проблемы в этом нет.
Вот если бы он словари сравнивал порядкозависимо, тогда да, был бы багор.
Т.е. в терминах ASN.1 это аналог BER но не DER.
- зачем? зачем?
Хотя, в json, конечно, из-за пробелов и переносов строк все полимеры будут просраны. Всё-таки в двоичных форматах единства представления легче добиться.
Чисто для сравнения двух джейсонов?
Это-то как раз не проблема, достаточно просто не ставить никакие пробелы и переносы строк. А вот что меня беспокоит — так это представление плавающих питухов. Можно ли гарантировать, что один и тот же питух будет сериализован в одну и ту же строку на любом компьютере?
Надо тестить старые сборки питона или сборки под другие платформы. Вот там могут быть отличия.
Я думал, надо проверять разные языки и сериализаторы
На своём проверил, осталось проверить все остальные:
Для всех остальных чисел. А то вдруг ты просто удачное число выбрал.
-
Какой багор)))
Если сделать из дабла строку при помощи интерполяции, то там всё будет окей.
А как ее всякие говны выводят -- пофиг же
Достаточно сказать "в кроссплатформенном формате JSON индеец имеет такое вот направление"
Во-вторых, JSON это человекочитаемый формат.
Потому сериализовывать нужно в простые дроби.
>человекочитаемый
а, ну тогда вам шашечки, или ехать?
Или у нас человекочитаемый формат, или ieee-754.
Имхо, для передачи данных между программами человекочитаемый формат не нужен.
Осилили же как-то printf'ы и прочие логи с выводом плавучки без потери точности.
Десериализация в следующем номере или по короткому номеру
* Сериализовать питуха так, чтобы его мог понять человек
* Сериализовать питуха так, чтобы его мог понять компутер.
Человек -- существо слабое, и нет большой беды в том, что часть точности питуха проебется.
Тут можно сделать printf из любого практически языка программирования.
Компьютеру же хочется получить питуха точь-в-точь таким, каким его закодировали на той стороне (разумеется, если софт и хард поддерживают питухов такого размера).
Обидно будет узнать, что я передал сообщение по цепочке от компа A к компу B, а оттуда к компу C, и с каждым шагом в нем менялся питух.
Это как с аналоговой пленочной аудиокассетой: чем больше раз переписал песню -- тем она хуёвее.
Так быть не должно.
К сожалению, нужный компьютеру питух человеку не читаем.
Потому можно ввести два вида питуха:
unskillable-petuh: для человека
tsar-petuh aka strict-petuh: для компа. В Hex.
Типа пишешь 0.1 а получаешь 0.99999999 или 0.100000001. Потому что между ними более подходящих чисел во флоате нет.
Если у нас мантисса на 4 бита, то туда поместится 16 значений, а это всего два порядка в десятичной.
Но у меня с переводами между системами немного всё проржавело, могу и хуйню написать ненароком.
З.Ы. Хотя, наверное, можно округлить в нужную сторону чтобы потом при обратном преобразовании звёзды сошлись... Но это уже не "без потерь", а просто "обратимо несмотря на погрешность".
1111 -- 15
Потому, что это будет так же нечитаемо, как 0xF?
Реально без потерь они означают 0.625, 0.750 и 0.875.
Но у нас битов даже на один десятичный разряд не набирается, поэтому мы смело можем их округлить до 0.6, 0.8 и 0.9. Ну и когда мы будем их переводить обратно в двоичное, то получим то что и было на входе.
Т.е. вроде и обратимо, но 0.101 (2) это нихуя не 0.6. Хотя и рядом.
Видимо вот такой приём и подразумевается как "вывод без потерь точности".
Ну или двоичные дроби. Впрочем ни то ни другое неюзабельно для человека.
Вот будет там какое-нибудь 1609/512 и ты даже не подумаешь, что это кусок пи.
И Пи я узнаю из 3.14блабла, да. А вот там какую-нибудь постоянную Планка уже фиг, а потому какая мне разница, какая там дробь?
Хочешь кексом, хочешь базой64, хочешь чем хочешь.
Про читаемость https://govnokod.ru/26782#comment556851
Именно поэтому я за кресты, где всё просто и понятно: есть отсортированная мапа и есть неотсортированная мапа, в обоих случаях всем похуй на порядок добавления.
З.Ы. В питоне отсортированной мапы, кстати, нету.
В «PEP 468» это сделали для того, чтобы сохранить порядок аргументов в **kwargs: https://www.python.org/dev/peps/pep-0468/#id6.
А вообще, «dict» — это хэш-массив, его ключи совершенно не обязаны быть сравнимыми/сортируемыми.
В пхп вроде тоже есть.
https://govnokod.ru/26415#comment524714
Так смысл не в том, чтобы их отсортировать, смысл в том, чтобы показать их в том порядке, в каком они были переданы в функцию.
> Но почему тогда явно не заказать ordereddict?
Потому что тогда тебе придётся вместо простого «f(a=1, b=2, c=3)» писать питушарское «f(OrderedDict((a, 1), (b, 2), (c, 3)))» (не знаю, какой там точно у него коньструктор, мог наврать).
Мне, в общем-то, наиболее полезным показался последний пример, с «Specifying argument priority by order».
«kwargs» — это просто название аргумента, оно может быть любым. То, что в этот аргумент складываются именованные параметры, определяют две звёздочки перед нем. В пепе, в разделе альтернатив, есть предложение расширить синтаксис тремя звёздочками, но это, очевидно, хуёвая идея. «New syntax is only added to Python under the most dire circumstances».
Для этого разработчикам «Питона» придётся расширить синтаксис, а это — плохая идея. К расширению синтаксиса следует прибегать только в самых печальных обстоятельствах, когда без этого будет совсем уж говно.
https://www.python.org/dev/peps/pep-0468/#id6
> есть три кейса
Два кейса. Сортировать по ключу нельзя, потому что ключи не обязаны быть сравнимыми. Собственно, они не обязаны даже быть одного типа. А между «сохранять порядок вставки» и «не сохранять порядок вставки» выбор, при прочих равных, очевиден: чем меньше в языке неопределённости, тем лучше.
https://www.php.net/manual/ru/function.ksort.php
Есть даже krsort, которая сортирует по убыванию ключей. И даже есть uksort, которой ты можешь подсунуть свой компаратор:
https://www.php.net/manual/ru/function.uksort.php
Именно поэтому я за «PHP».
А дали такую гарантию после того, как в «CPython» изменили реализацию словаря на такую, которая гарантировала сохранение порядка вставки. И она оказалась быстрее, чем старая.
> Ты может и для сета хочешь сохранять порядок?
А такой сет вполне можно эмулировать имеющимся словарём, причём с сохранением всех асимптот.
А зачем? Ты получишь поиск за питушарские O(logN), при этом тебе ещё и хэш нужно вычислять.
> А как можно получить O(1) для случайного ключа не засрав всю память?
https://en.wikipedia.org/wiki/Hash_table
Вкратце: вспоминаем Царя, берём МАССИВ buckets на N элементов-списков, берём нормированный в [0; N) (банальным %, например) хэш H ключа, добавляем в buckets[H] пару (ключ, значение) — как ты и предлагал.
Теперь, если у нас достаточно хороший хэш и достаточно малая заполненность таблицы (отношение суммарного количества элементов к N), то в каждой корзине у нас будет по одному элементу и мы получим то самое амортизированное O(1). Это — самая примитивная реализация, в реальности, конечно, всё сложнее (плюс существует второй вореант с «открытой адресацией» — это когда в МАССИВЕ хранятся не списки, а сразу пары (ключ, значение), и если при очередной вставке buckets[H] занята, то мы поочерёдно пытаемся вставить в H+1, H+2, H+3 и так далее, причём вместо простого инкремента могут применяться весьма изощрённые техники).
В «Питоне» всё осложняется тем, что нам нужно уметь итерироваться по ключам, и делать это быстро. Как ты понимаешь, перебор всего массива buckets со всеми внутренними списками — это пиздец как долго. Исправить это можно хранением списка всех существующих ключей, и при вставке нового просто добавлять его в конец. Собственно, так мы и можем получить сохранение порядка вставки ключей без порчи асимптот. Такое решение, разумеется, весьма наивно, как там питонухи сделали — хуй знает.
> Кажется, что не всегда)
Честно говоря, «309 ns +- 10 ns» как-то не выглядит надёжно медленнее «320 ns +- 8 ns». А учитывая, что в замере скорости этот питушок брал медиану вместо минимума…
Если «дерево» будет с достаточно большим N, то это ничем не будет отличаться от стандартной хэш-таблицы, кроме того, что тебе для поиска придётся разыменовывать хуеву тучу лишних уко-ко-козателей, что приведёт пирфоманс прямиком в жопу.
Хэш для таких штук берётся не питушарский, а царский, с максимально равномерным распределением. Поэтому в среднем в каждой корзине будет лежать по M/N (суммарное количество элементов/количество элементов в МАССИВЕ) элементов — эту метрику по-научному называют «load factor».
Если у тебя дерево будет N-ричным, то тебе тоже придётся делать N листьев, лол. По памяти так ты только проиграешь (не говоря уже про пирфоманс).
Остался один шаг: выкинуть ошмётки дерева и получить нормальный, человеческий хэшмассив.
> Зато я буду подгружать с диска только нужные мне куски, а тебе придется выгружать в память весь массив и делать туда случайный доступ.
Стоп-стоп-стоп, это как? У тебя первый уровень N-ричного дерева представляет из себя как раз массив из N элементов, как ты его собрался на куски дробить и почему это нельзя сделать с обычным массивом из N элементов?
> Если же дерево будет достаточно глубоким то да, придется
Джвух уровней будет достаточно, чтобы слить «классической» хэштаблице.
> Впрочем, если твой hash целиком влезает в память то да, разумеется он лучше.
Я очень сильно сомневаюсь, что при разработке словаря в «Питоне» учитывалась необходимость хранения данных, не влезающих в оперативную память.
А как ты узнаешь, какие тебе нужны страницы? Ты же дерево строишь по хэшу, то есть очередной ключ окажется в абсолютно произвольном куске дерева. Если бы дерево было построено по самим ключам, тогда бы можно было делать предположения — например, что похожие ключи будут лежать где-то рядом.
> Значит ли это, что дерево сосет у hashtable для всех случаев, кроме тех, где данные должны упорядочиваться по значению?
В случаях, когда данных мало, а памяти много (гигабайт данных, десять гигабайт памяти, например) — да. Но когда данных становится очень много, и тратить гигабайты оперативы на пустые корзины становится затратно — тогда уже дерево вырывается вперёд. В этом случае, кстати, основным источников тормозов будут обращения к диску, так что промахи кэша практически перестанут влиять на скорость.
Ну вот, а в случае с массивом достаточно вычислить хэш и загрузить в память в точности ту корзину, которую надо.
Как в «Питоне» реализовано — не знаю, не смотрел.
А можно, например, хранить список указателей на вставленные кортежи:
В этом случае, кстати, можно будет вспомогательный список преобразовать в дерево (ключ — порядковый номер вставки), тогда удоление элемента будет O(logN).
А вообще, усложнение внутренней реализации для ускорения/улучшения стандартной библиотеки — это абсолютно нормально. Люди, которые делают делают реализацию языка, нужны именно для того, чтобы писать сложные вещи, которыми смогут пользоваться их менее продвинутые коллеги.
Ну, собственно, сначала «CPython» перешёл на новую реализацию, которая сохраняет порядок ключей (то ли в 3.5, то ли в 3.6), и только потом это поведение зафиксировали официально.
Статья про всю эту питушню: https://pbedn.github.io/post/ordered-dict-officially-ordered/.
А по поводу отдельных структур на каждый чих хорошо сказано в вышеупомянутой статье:
(Кстати, я тоже без гугла не скажу, чем отличается std::scoped_lock от std::lock_guard и зачем они оба нужны, если есть std::unique_lock).
UPD: И да, «хэш(словарь, карта, ассоц. массив) в первую очередь используется для получения значения по ключу» не выглядит совсем уж очевидным утверждением.
UPD: Из пепа:
Какая разница подходов )))
Потому что лично я часто вижу в питонокоде конструкцию «for key in some_dict: ...» и не могу бездоказательно согласиться с тем, что операция получения доступа по ключу настолько превалирует над итерацией, что последнюю можно игнорировать.
Нет, поиск конкретного ключа во всех этих вореантах никак не изменяется. Вспомогательный список нужен для того, чтобы быстро итерироваться по всем ключам. А «указатель_на_ключ_в_списке» — чтобы можно было удалять элемент за O(1): ищешь его в таблице за O(1), удаляешь его из вспомогательного списка (тоже за O(1) благодаря тому, что у тебя сразу есть адрес нужного элемента вспомогательного списка), удаляешь из корзины ещё за один O(1).
Может, у этих контейнеров поддерживаются какие-то хитрые свойства?
ахаха. такие анскилябры, что хуевый вариант оказался менее хуевым первого.
Какие разработчики игр «Facebook» и «Google» )))
Не совсем.
Там линкед лист бакетов, ключи в одном экземпляре, просто к ним прикручены next/prev.
Такая же питушня.
Няшно.
> b = {2:3, 1:2}
> print(a == b)
Сранно. Но смотря на этот код я уже ожидаю подвоха. 99% что False.
Кмк, сравнивать сложные типы через == в скриптухе западло.
Должен быть какой-то хитрый map.equals_kv, map.equals_with_order или что-то в этом духе.
Змею можно заставить сравнивать словари с учётом порядка ключей через «OrderedDict()», кстати.
Как работает == никто точно не скажет, особенно когда типы разные (пусть даже это разные реализации мап).
А когда ЯВНО указан метод для сравнения всё ясно и понятно.