• Совмещение асинхронного и синхронного программирования - это плохо?

    @deliro
    Сейчас много что в питоне создаёт иллюзию асинхронности пулом тредов. Ничего в этом удивительного нет. Если асинхронный код выполнять как синхронный (что и происходит в статье) — он будет медленней синхронного. Очевидно же, что синхронный выполняется последовательно, а асинхронным нужно управлять — постоянные свичи "контекста", сколько-то времени тратит event loop на себя, треды порождаются долго (если "асинхронность" на тредах).

    Производительность не в том, что запросы должны лететь быстрее, а в том, что другой код может выполняться, пока запрос к БД висит. Потому что он висит в отдельном треде и никому не мешает (за исключением всяких edge-кейсов, которые блокируют все треды). Да, часто пула тредов может не хватать (по умолчанию это 5 * количество_ядер). Например, если ты пытаешься через aiohttp скачать 100к страниц. Но в этом случае синхронный был бы раз в 40 медленней (на 8-ядерном процессоре). И этот пул при желании можно увеличить. Но я сильно сомневаюсь, что затык будет в тредах, а не в сети.
    Ответ написан
    Комментировать
  • Python. Как хранить данные?

    @deliro
    Зависит оттого, сколько пользователей и сообщений в день. Если сообщений меньше миллиона — хоть в чём храните. Если пользователей меньше сотни тысяч — аналогично. Можно хоть в shelve
    Ответ написан
    Комментировать
  • Видимость свойств python?

    @deliro
    self — это объект. Все методы этого объекта имеют доступ ко всем атрибутам этого объекта.
    Ответ написан
    Комментировать
  • Медленная работа Skype, язык или плохая оптимизация?

    @deliro
    Дело в программистах и размере болта, который они положили на пользователей
    Ответ написан
    3 комментария
  • Как работать с socks5?

    @deliro
    Первая причина — это ты то, что ты весь код запихал в try-except Exception и теперь совершенно непонятно, что в твоём коде возбуждает исключение. В 99% случаев в try блоке должно быть одна-две строчки. И в 99% случаев в except не должно быть такого широкого класса, как Exception.
    Ответ написан
    2 комментария
  • Нужно ли умножать сложности?

    @deliro
    Нет. Вот если бы у тебя было два вложенных цикла по string, то да. А тут сложность "складывается", потому что сначала O(n) — реверс, потом O(n) — создание цикла, потом O(n) — join. Но т.к. константы в сложности никому не нужны — это только O(n)

    Но ты, конечно, не поверишь, поэтому вот тебе доказательство. Время линейно растёт вместе с N
    5c6f07ce35b63943246212.png
    Ответ написан
    Комментировать
  • Можно ли запустить в интернет Django-сайт на VDS?

    @deliro
    Можно
    Ответ написан
    Комментировать
  • Почему время выполнения на разных машинах отличается?

    @deliro
    Ещё раз, чёрным по белому. Сложность алгоритма НЕ ОТРАЖАЕТ реального времени выполнения. Время может различаться хоть в миллион раз. Сложность ОТРАЖАЕТ характер роста времени/памяти при росте этого твоего N.
    Ответ написан
    1 комментарий
  • Как нарисовать пиксельное изображение в Python?

    @deliro
    Pillow
    Ответ написан
    Комментировать
  • Почему возникает ошибка при обновлении pip?

    @deliro
    Это значит, что у тебя установлен пакет sanetime такой версии, который требует такую версию python-dateutil, которая не входит в промежуток требуемых версий pip.

    Сначала выполни

    pip install -U sanetime

    А потом обновляй pip
    Ответ написан
    Комментировать
  • Можно ли так описать хэш-таблицу?

    @deliro
    1. ХТ пуста от 2/3 своего размера до 1/3. 1/3 - это уже она вот-вот реаллоцируется.
    2. Про циклы - фигня. В питоне нет ни одного способа добавить элементы кучей так, чтобы на самом деле они добавились не в цикле на уровне Си. Даже dict comprehensions добавляют элементы последовательно.
    3. Хэш от ключа - это встроенная функция hash(). Для конкретной ХТ берется остаток от деления хэша на размер ХТ. На самом деле, берётся хэш по битовой маске (размер_ХТ - 1) [Например hash(obj) & 2**16 - 1]. Но для степеней двойки эти операции равноценны.
    4. Ты совсем забыл момент с разрешением коллизий (это когда хэши двух разных ключей совпадают). В питоновых словарях это самый интересный момент. И именно из-за него удаленные данные из словаря не удаляются физически до следующей реаллокации.
    5. "Очень быстро" - это как?

    UPD.

    На текущий момент реализация словаря в питоне поменялась. В 3.6 версии сделали все словари по умолчанию ordered и заодно уменьшили размер словаря в байтах на 20-25%. Вот реализация актуальных словарей на питоне (в оригинале, она, конечно на Си):

    Раскрыть
    import array
    import collections
    import itertools
    
    # Placeholder constants
    FREE = -1
    DUMMY = -2
    
    
    class Dict(collections.MutableMapping):
        "Space efficient dictionary with fast iteration and cheap resizes."
    
        @staticmethod
        def _gen_probes(hashvalue, mask):
            "Same sequence of probes used in the current dictionary design"
            PERTURB_SHIFT = 5
            if hashvalue < 0:
                hashvalue = -hashvalue
            i = hashvalue & mask
            yield i
            perturb = hashvalue
            while True:
                i = (5 * i + perturb + 1) & 0xFFFFFFFFFFFFFFFF
                yield i & mask
                perturb >>= PERTURB_SHIFT
    
        def _lookup(self, key, hashvalue):
            "Same lookup logic as currently used in real dicts"
            assert self.filled < len(self.indices)  # At least one open slot
            freeslot = None
            for i in self._gen_probes(hashvalue, len(self.indices) - 1):
                index = self.indices[i]
                if index == FREE:
                    return (FREE, i) if freeslot is None else (DUMMY, freeslot)
                elif index == DUMMY:
                    if freeslot is None:
                        freeslot = i
                elif (
                    self.keylist[index] is key
                    or self.hashlist[index] == hashvalue
                    and self.keylist[index] == key
                ):
                    return (index, i)
    
        @staticmethod
        def _make_index(n):
            "New sequence of indices using the smallest possible datatype"
            if n <= 2 ** 7:
                return array.array("b", [FREE]) * n  # signed char
            if n <= 2 ** 15:
                return array.array("h", [FREE]) * n  # signed short
            if n <= 2 ** 31:
                return array.array("l", [FREE]) * n  # signed long
            return [FREE] * n  # python integers
    
        def _resize(self, n):
            """Reindex the existing hash/key/value entries.
               Entries do not get moved, they only get new indices.
               No calls are made to hash() or __eq__().
    
            """
            n = 2 ** n.bit_length()  # round-up to power-of-two
            self.indices = self._make_index(n)
            for index, hashvalue in enumerate(self.hashlist):
                for i in Dict._gen_probes(hashvalue, n - 1):
                    if self.indices[i] == FREE:
                        break
                self.indices[i] = index
            self.filled = self.used
    
        def clear(self):
            self.indices = self._make_index(8)
            self.hashlist = []
            self.keylist = []
            self.valuelist = []
            self.used = 0
            self.filled = 0  # used + dummies
    
        def __getitem__(self, key):
            hashvalue = hash(key)
            index, i = self._lookup(key, hashvalue)
            if index < 0:
                raise KeyError(key)
            return self.valuelist[index]
    
        def __setitem__(self, key, value):
            hashvalue = hash(key)
            index, i = self._lookup(key, hashvalue)
            if index < 0:
                self.indices[i] = self.used
                self.hashlist.append(hashvalue)
                self.keylist.append(key)
                self.valuelist.append(value)
                self.used += 1
                if index == FREE:
                    self.filled += 1
                    if self.filled * 3 > len(self.indices) * 2:
                        self._resize(4 * len(self))
            else:
                self.valuelist[index] = value
    
        def __delitem__(self, key):
            hashvalue = hash(key)
            index, i = self._lookup(key, hashvalue)
            if index < 0:
                raise KeyError(key)
            self.indices[i] = DUMMY
            self.used -= 1
            # If needed, swap with the lastmost entry to avoid leaving a "hole"
            if index != self.used:
                lasthash = self.hashlist[-1]
                lastkey = self.keylist[-1]
                lastvalue = self.valuelist[-1]
                lastindex, j = self._lookup(lastkey, lasthash)
                assert lastindex >= 0 and i != j
                self.indices[j] = index
                self.hashlist[index] = lasthash
                self.keylist[index] = lastkey
                self.valuelist[index] = lastvalue
            # Remove the lastmost entry
            self.hashlist.pop()
            self.keylist.pop()
            self.valuelist.pop()
    
        def __init__(self, *args, **kwds):
            if not hasattr(self, "keylist"):
                self.clear()
            self.update(*args, **kwds)
    
        def __len__(self):
            return self.used
    
        def __iter__(self):
            return iter(self.keylist)
    
        def iterkeys(self):
            return iter(self.keylist)
    
        def keys(self):
            return list(self.keylist)
    
        def itervalues(self):
            return iter(self.valuelist)
    
        def values(self):
            return list(self.valuelist)
    
        def iteritems(self):
            return itertools.izip(self.keylist, self.valuelist)
    
        def items(self):
            return zip(self.keylist, self.valuelist)
    
        def __contains__(self, key):
            index, i = self._lookup(key, hash(key))
            return index >= 0
    
        def get(self, key, default=None):
            index, i = self._lookup(key, hash(key))
            return self.valuelist[index] if index >= 0 else default
    
        def popitem(self):
            if not self.keylist:
                raise KeyError("popitem(): dictionary is empty")
            key = self.keylist[-1]
            value = self.valuelist[-1]
            del self[key]
            return key, value
    
        def __repr__(self):
            return "Dict(%r)" % self.items()
    
        def show_structure(self):
            "Diagnostic method.  Not part of the API."
            print("=" * 50)
            print(self)
            print("Indices:", self.indices)
            for i, row in enumerate(zip(self.hashlist, self.keylist, self.valuelist)):
                print(i, row)
            print("-" * 50)
    
    
    if __name__ == "__main__":
        d = Dict([("timmy", "red"), ("barry", "green"), ("guido", "blue")])
        d.show_structure()

    Описание (возможно, понадобится VPN из-за выходок РКН)
    Ответ написан
    2 комментария
  • Как происходит удаление из множества?

    @deliro
    Множество — это почти тот же словарь. Там та же самая хэш-таблица. Только значений нет, есть только ключи. Соответственно, алгоритм поиска тот же — вычисляем хэш элемента, по нему определяем индекс в хэш-таблице за константное время и помечаем элемент как удалённый.
    Ответ написан
    Комментировать
  • Как правильно распрасить json?

    @deliro
    Это не JSON. В JSON кавычки двойные

    In [6]: x = '[{"tzid": "000000000", "phone": "000000", "service": "test", "statu
       ...: s": "TZ_NUM_PREPARE"}]'
    
    In [7]: x
    Out[7]: '[{"tzid": "000000000", "phone": "000000", "service": "test", "status": "TZ_NUM_PREPARE"}]'
    
    In [8]: json.loads(x)
    Out[8]: 
    [{'tzid': '000000000',
      'phone': '000000',
      'service': 'test',
      'status': 'TZ_NUM_PREPARE'}]
    Ответ написан
    1 комментарий
  • Как в Python3 использовать вложенный список в multiprocessing?

    @deliro
    Не надо так низкоуровнево писать (обычно).

    from concurrent.futures import ProcessPoolExecutor
    
    
    D = {'x': [1, 2, 3], 'y': [4, 5, 6], 'z': [7, 8, 9]}
    
    
    def process(arg):
        key, values = arg
        return key, [v * 10 for v in values]
    
    
    if __name__ == "__main__":
        with ProcessPoolExecutor() as executor:
            result = executor.map(process, D.items())
    
        print(dict(result))


    Чтобы убедиться, что оно действительно работает и не блокирует друг друга:
    from concurrent.futures import ProcessPoolExecutor
    from time import sleep
    
    
    D = {'x': [1, 2, 3], 'y': [4, 5, 6], 'z': [7, 8, 9]}
    
    
    def process(arg):
        key, values = arg
        print("executing", key, values)
        sleep(1)
        return key, [v * 10 for v in values]
    
    
    if __name__ == "__main__":
        with ProcessPoolExecutor(2) as executor:
            result = executor.map(process, D.items())
    
        print(dict(result))
    Ответ написан
    7 комментариев
  • Как происходит создание словаря?

    @deliro
    Нет.

    1. Создаётся в Си структура словаря
    2. Создаётся хэш-таблица на 8 элементов (это до 6 ключ-значений словаря)
    3. Поочерёдно добавляются (на уровне Си) элементы

    Когда ты вставляешь 7-ой элемент в словарь, хэш-таблица увеличивается в 2 раза — до 16 ячеек (это до 11 ключ-значений), все существующие в старой хэш-таблице элементы (кроме удалённых [да, удаление значения из словаря не удаляет его на самом деле до следующей реаллокации]) переезжают по одному в новую, при этом заново вычисляются хэши всех этих элементов (потому что старые хэши работали только со старым размером хэш-таблицы).

    И так до бесконечности. Как только количество элементов в словаре превышает load factor хэш-таблицы (который равен 2/3) — происходит создание новой хэш-таблицы. Но не раньше.

    Вот этот код, возможно, поможет тебе понять, как это работает:
    import sys
    
    def fs(s):
        if s >= 2 ** 20:
            return f"{s/(2**20):.2f}МБ"
        if s >= 2 ** 10:
            return f"{s/(2**10):.2f}КБ"
        return f"{s}Б"
    
    def g():
        n = int((2**16) * (2/3))
        d = dict.fromkeys(range(n))
        print("Размер словаря:", fs(sys.getsizeof(d)), "Элементов:", len(d))
        for i in range(n):
            del d[i]
        print("Размер словаря всё ещё", fs(sys.getsizeof(d)), "хотя он пуст. Элементов:", len(d))
        
        # В уже пустой словарь весом 1.25мб вставляем всего лишь один элемент
        d["hello"] = "world"
        newsize = sys.getsizeof(d)
        print("Хэш-таблица превысила load factor (2/3), реаллокация. Новый размер:", fs(newsize), "Элементов:", len(d))
    
    g()
    Ответ написан
    Комментировать
  • Что значит в глазах работодателя "Умею проектировать архитектуру БД" для программиста?

    @deliro
    Это значит, что ты умеешь что-то кроме архитектуры уровня "Пост - Юзер". Что когда тебе дадут задачу по архитектуре, например, сделать и хранить систему скидок, чтобы там декларативно можно было описать условия, указать, какие скидки суммируются с какими (а какие нет), какие проценты суммируются, а какие умножаются — ты бы не впал в депрессию и не уволился или, чего хуже, не написал такое решение, которое невозможно бы было поддерживать и работать с ним.
    Ответ написан
    Комментировать
  • Как создать новый проект в pycharm?

    @deliro
    Это правильно, что он не видит компилятор. Ведь у питона интерпретатор.
    Ответ написан
    Комментировать
  • Как лучше хранить большой массив чисел на диске с очень быстрым поиском?

    @deliro
    Если допустимы ложноположительные ошибки (например, с шансом в 0,1%), то можно юзать фильтр Блума. Так ты 800млн значений впихнешь в 1,34гб. Чем меньше ошибка - тем больше нужно места
    Ответ написан
  • Как сохранить скриншот в базу данных Django?

    @deliro
    JSом отправляешь снимки на ручку API, которую ты написал на джанге. Она принимает и сохраняет файлы.
    Ответ написан
    Комментировать