Pull to refresh
2
0

Пользователь

Send message

Eventlet -- 2008 год
Gevent -- 2009 год

В Twisted использовались реактор и коллбэки, да и сейчас используются. На asyncio он никогда не переходил.

При абсолютном импорте Python почему-то ищет импортируемые зависимости рядом

Почему-то... На самом деле никакой магии нет.

При запуске скрипта python path/to/script.py, путь к директории в которой находится запускаемый скрипт добавляется в sys.path. Дальше никакой магии нет и поиск импортируемых модулей и пакетов происходит при помощи sys.path. (На самом деле все сильно сложнее, но для понимания работы этого пока будет достаточно).

Когда вы импортируете http_get.modules.http_get, сперва будет проверен кеш модулей sys.modules, и если модуль с указанным именем в нем не обнаружен, будет произведена попытка поиска модуля в sys.path.

Пакет http_get будет найден непосредственно в sys.path и все подмодули будут импортированы относительно него. Во время импорта интерпретатор выполняет код модуля, создает объект модуля и создает ссылку на него в словаре sys.modules.

Ключем в этом словаре выступает полное имя модуля, значением соответственно ссылка на объект модуля. Таким образом в sys.modules появилась новая запись с ключем http_get.modules.http_get.

И тут PyCharm подкладывает вам и всем новичкам утку. При запуске при помощи PyCharm, по-умолчанию PyCharm устанавливает переменную окружения PYTHONPATH, значением которой передает путь к корню проекта (или точней к "Sources Root").

Забавно, что вне PyCharm ваш код просто так работать не будет. Попробуйте запустить свой main.py из консоли, не трогая PYTHONPATH.

Таким образом директория проекта тоже попадает в sys.path.Теперь у вас в sys.path два пересекающихся пути.

Таким образом, когда вы импортируете ModulesAndPackages.module_examples.http_get.modules.http_get, будет снова проверен sys.modules, и так как этот модуль имеет совершенно другое имя, он не будет найден в sys.modules, а соответственно будет произведена попытка его поиска в sys.path. Что и успешно происходит, так как PyCharm заботливо подложил путь к корню проекта в sys.path.

Это типичная ошибка новичков, использующих IDE и не имеющих понимания ни работы системы импортов, ни инструментов, с которыми они работают. К сожалению преисполнившись самоуверенности они начинают строчить статьи на Хабр.

А почему же это проблема? Да, потому что два экземпляра модуля это два совершенно разных независимых объекта в памяти, которые имеют независимое состояние. И хоть мы все знаем, что глобальные переменные (глобальные состояние) - зло, рано или поздно это вызовет проблему, которую к слову не легко обнаружить и которая проявляется неочевидным образом.

P.S. Поэтому если уж используете PyCharm, я настоятельно рекомендую отключать эти две опции в настройках запуска:

И учиться работать, не трогая PYTHONPATH или sys.path без необходимости. Для этого всего лишь нужно грамотно огранизовать структуру проекта.

Материал для дальнейшего изучения:

  1. https://docs.python.org/3/library/sys.html#sys.modules

  2. https://docs.python.org/3/library/sys.html#sys.path

  3. https://docs.python.org/3/reference/import.html

  4. https://packaging.python.org/en/latest/tutorials/packaging-projects/

На самом деле, как бы странно не звучало, но невозможно загрузить один и тот же модуль дважды.

Вы крайне серьезно заблуждаетесь.

id(модуля с длинным импортом(по моим словам абсолютным)) = id(модуля с коротким импортом(по моим словам неявно относительным))

Чушь.

У модуля один и тот же путь(C:\file.py, .\file.py и file.py по факту могут указывать на один файл) и хеш.

С чего вы это взяли? При чем тут путь, какой еще хеш? Система импортов в питоне работает совершенно не так.

На самом деле это легко проверить, возьмем ваш же любезно предоставленный репозиторий и любимый PyCharm. Добавим в функцию main всего пару принтов:

import sys

from ModulesAndPackages.module_examples.http_get.modules.http_get import get_dict as absolute
from http_get.modules.http_get import get_dict as relative


def main():
    print(absolute is relative)
    print(absolute.__module__ is relative.__module__)
    print(id(sys.modules["http_get.modules.http_get"]))
    print(id(sys.modules["ModulesAndPackages.module_examples.http_get.modules.http_get"]))

    # Workable
    print(absolute())
    print(relative())

И что же мы видим?

False
False
139668730660880
139668732362352
{'satus': 200, 'data': 'success'}
{'satus': 200, 'data': 'success'}

Почему так происходит? Да потому модуль загружен в двух экземплярах, в sys.modules существует две записи, указывающие на каждый из загруженных модулей соответственно.

P.S. Может быть стоит сперва изучить букварь по языку, прежде чем статьи на Хабр писать?

Далее в корне создадим main.py файл, в который импортируем наш модуль двумя разными способами(об импортах описано в статье):

from ModulesAndPackages.module_examples.http_get.modules.http_get import get_dict as absolute
from http_get.modules.http_get import get_dict as relative

У вас тут не два разных способа импорта (относительный и абсолютный), а один - абсолютный импорт. А тот факт, что вы можете импортировать один и тот же модуль при помощи двух разных абсолютных путей говорит о наличии мусорки в sys.path. При этом один и тот же модуль будет загружен в двух экзмплярах, а это ошибка, которая ведет к тяжелым последствиям.

Относительные импорты в современном питоне начинаются с точки: from .module import name.

А то что вы называете "относительными импортами" существовало в двойке, было объявлено устаревшим и в тройке этот механизм убрали.

Если у Вас есть опыт языка Си и знание английского, то будет достаточно прочитать официальный туториал [1] и дальше уже пользоваться официальной документацией [2].

В противном же случае могу порекомендовать книги Изучаем Python, Марк Лутц (довольно объемное обстоятельное чтиво) или "Укус Питона" (A Byte of Python).

  1. https://docs.python.org/3/tutorial/

  2. https://docs.python.org/3/

Это не попытки притащить парадигмы из одного языка в другой. src-layout – это довольно распространенный паттерн Python проектов, который призван решать некоторые проблемы при разработке приложений. Подробней об этом можно узнать здесь:

  1. https://docs.pytest.org/en/7.1.x/explanation/goodpractices.html#choosing-a-test-layout-import-rules

  2. https://blog.ionelmc.ro/2014/05/25/python-packaging/#the-structure

Это не очень плохая идея, это стандартное поведение итераторов ("Протокол итераторов") в питоне. Итератор должен возвращать self в методе __iter__, это нужно для того, чтобы итераторы можно было использовать в циклах или функциях, ожидающих итерируемый объект.

iterator.__iter__()

Return the iterator object itself. This is required to allow both containers and iterators to be used with the for and in statements.

https://docs.python.org/3/library/stdtypes.html#typeiter

P.S. А если клиентский код захочет делать tee, для этого есть модуль itertools и функция itertools.tee() как раз для этой задачи.

Понимаете, тут вопрос в применимости. Для аутентификации по паролю онлайн (в удалённой системе) необходимо и достаточно хранить необратимые и стойкие к коллизиям солёные хеши.

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

А вообще в первую очередь следует пользоваться общепринятыми рекомендациями, например OWASP Password Storage Cheat Sheet, вместо того чтобы выдумывать свои схемы.

На мой взгляд лучше уж какой-нибудь учебник по основам сетевого программирования. Из документации библиотеки socket в принципе тоже можно было бы разобраться, но челвоеку, не знакомому с предметной областью, это будет сделать слегка затруднительно. Так как библиотека `socket` это почти прямой враппер над соответствующими системными вызовами, описания этих системных вызовов в документации не полные.

new_socket.listen(2) # Указываем нашему сокету, что он будет слушать 2 других

Нет, это не "он будет слушать два других", это размер беклога. Это означает что до двух других входящих подключений могут ожидать в очереди на прием.

        #Получим 1024 байта от первого клиента
        a = conn1.recv(1024)

После этого статью можно в принципе дальше не читать. Хрестоматийный пример того как делать не надо.

      #Получаем строку от сервера
        b = client_socket.recv(1024)
       #Печатаем, предварительно раскодировав
        print(b.decode("utf-8"))

А здесь еще лучше.

Во-первых socket.recv возвращает не ровно 1024 байта, а до 1024 байта.

Во-вторых нет никакой гарантии что эта функция вернет все сообщение целиком (не забываем что у нас здесь потоковые сокеты). Да и вообще нет никакой гарантии того, что байтовая последовательность, которую вернет sock.recv будет корректной utf-8 последовательностью по этой же причине.

С socket.send ровно та же проблема. То что этот код создает видимость работоспособного всего лишь удачное стечение обстоятельств, не более.

Вишенкой на торте две функции acceptor1 и acceptor2, использующие глобальные паременные и отличающиеся только одной переменной.

А еще лучше использовать специально предназначенный для измерения интервалов, неубывающий, имеющий максимальную доступную точность time.perf_counter() [1]

[1]: docs.python.org/3/library/time.html#time.perf_counter
Человека, незнакомого с языком, вполне закономерно может удивить тот факт, что реализации операторов + и += для некоторых типов могут иметь разную семантику.
Так как во многих языках a += b как-правило является эквивалентом a = a + b, поведение этих операторов должно бы быть консистентным.
В Python же для списка реализация оператора a += b эквивалентна вызову метода a.extend(b) (за исключением присваивания), что описано в документации [1], который работает не только со списками, а с любым iterable.

[1] docs.python.org/3/library/stdtypes.html#mutable-sequence-types
Скорее не +=, а его реализация для изменяемых последовательностей (mutable sequences), в том числе и списков.
Пакет venv входит в стандартную библиотеку Python начиная с версии 3.3 и устанавливать сторонний пакет `virtualenv` нет необходимости.

$ python3 -m venv <path/to/venv>
Кем считается? В чем именно проявляется «неоптимальность»?

pipenv и poetry облегчают ведение проекта, но решают несколько разные задачи.
Кроме того что авторы `pipenv` имели ощутимые проблемы во взаимодействия с комьютнити, проблемы с совместимостью [1], а последние несколько месяцев не ведется никакой активной разработки. Проект фактически умер. Поэтому я бы его не стал использовать или рекомендовать для разработки новых проектов.
`poetry` же, на мой взгляд, весьма перспективный проект, но все еще имеет проблемы с производительностью (ощутимые на некоторых сценариях) [2] и регрессии [3].
В любом случае оба этих проекта так или иначе используют виртуальные окружения.

Конкретно пакет virtualenv действтельно считается устаревшим, так как поддержка виртуальных окружений является частью стандартной библиотеки Python начиная 3.3 [4].

[1] chriswarrick.com/blog/2018/07/17/pipenv-promises-a-lot-delivers-very-little
[2] github.com/python-poetry/poetry/issues/2094
[3] github.com/python-poetry/poetry/issues/2170
[4] docs.python.org/3/library/venv.html
непонятно только, зачем было так переводить вполне логичное слово «экспансия»


полагаю, потому что слово expanse обозначает "​a wide and open area of something, especially land or water" [1] и наиболее близким переводом этого слова является пространство [2], в то время как expansion переводится как экспансия [3].

[1] www.oxfordlearnersdictionaries.com/definition/english/expanse
[2] translate.google.com/#view=home&op=translate&sl=en&tl=ru&text=expanse
[3] translate.google.com/#view=home&op=translate&sl=en&tl=ru&text=expansion
Что переводится как «надмножество» или «надстройка».
1

Information

Rating
Does not participate
Registered
Activity