TGINSIGHT CHAT
Python Заметки
@pythonotes
EducationИнтересные заметки и обучающие материалы по Python Контакт: @paulwinex ⚠️ Рекламу на канале не делаю!⚠️ Хештеги для поиска: #tricks #libs #pep #basic #regex #qt #django #2to3 #source #offtop
Неодамнешни објави
Ознака: #tricks · 178 објави
Објавено 16 јан.
Почему нет функции, аналога для f-string? На самом деле, f-string можно представить в виде простой функции format() в которую вместе со стркой-шаблоном передаются словари locals() и globals(). Но так делать точно не стОит! И прежде всего, в целях безопасности. О чём это я? Допустим, у нас есть шаблон: template = 'Hello {username}!' И потом мы просто форматируем строку по этому шаблону псевдо-функцией fstring() (представим, что она существует) username = user.get_name() greeting = fstring(template) Выглядит всё логично, но тут есть скрытая угроза безопасности! Допустим, у нас некий веб сервис и мы предоставляем юзеру возможность сформировать себе любой шаблон какой ему нравится, после чего форматируем строку по аналогии с f-string. В чем же тут может быть опасность? А в том, что в отличие от простого метода строки format() этот способ имеет возможность вшить в шаблон любой экспрешн! То есть, мы позволяем юзеру написать любой код и самолично его выполняем на сервере! Если юзер действует по правилам, то напишет что-то вроде: "Hey what's up, {username}?)))" А если попадётся слишком догадливый, знающий о некомпетентности программиста и, допустим, что логика на Django+Python, то он может написать что-то такое: "{__import__('django.conf', fromlist=['conf']).settings.SECRET_KEY}" И алгоритм выдаст ему секретный ключ сайта! Можно и более кардинально: "{__import__('django.contrib.auth', fromlist=['auth']).get_user_model().objects.filter(email='[email protected]').update(is_superuser=True)}" Этот однострочный экспрешн делает юзера суперадмином! Теперь можно заходить на сайт как админ и делать там что хочешь))) Поэтому, только программист может формировать подобные строки. То есть нельзя в f-sting передать неизвестно что из переменной. Никогда не давайте юзерам слишком много свободы. Если требуется подобный функционал, лучше использовать простой метод format() или класс 'string.Template'. А ещё лучше, выдавать список готовых вариантов 🤓 PS. Для тех кто хочет поэкспериментировать с f-string функцией, можете попробовать этот вариант def fstring(fstring_text): return eval(f'f"{fstring_text}"', locals(), globals()) Пример: >>> template = 'Hello, {username}!' >>> username = 'Max' >>> fstring(template) Hello, Max! Теперь попробуйте придумать хитрые способы взлома) #tricks
Hashtags
Објавено 15 јан.
Как с помощью Python быстро расшарить файлы в локальную сеть? Если без заморочек, то очень просто! Заходим в нужную директорию и выполняем команду: Shell: $ python -m http.server Получаем ответ Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ... Значит, что сервер запущен! 0.0.0.0 - означает, что могут зайти все кто сможет "достучаться" до вашего компьютера. Теперь остаётся заменить этот IP на свой локальный адрес. Чтобы его узнать, можете поискать во всяких окошках, но это не наш метод 😎 Выполняем команду: Windows: > ipconfig Linux: $ ifconfig В полученной распечатке ищем нужное сетевое устройство и его адрес. Ищем что-то вроде 192.168.1.100 Для тех, кто чтит Python-way Linux/Windows > python -c "import socket;print(socket.gethostbyname(socket.gethostname()))" Теперь формируем и отправляем полученный адрес кому требуется: http://192.168.1.100:8000/ Небольшой FAQ Q: Не работает что-то :( A: Проверь настройки сети, может фаервол? Q: А как прервать передачу файлов? A: Ctrl+C Q: Смогут ли люди из интернета зайти на мой минисервер? A: Если ты не на публичном сервере с белым IP то нет. Q: А можно поставить свой "секретный" порт вместо порта по умолчанию? A: Можно: python -m http.server 12345 А еще можно посмотреть справку чтобы узнать другие параметры запуска $ python -m http.server -h Есть способ раздать и "наружу" в интернет, но об этом в другой раз. #tricks
Hashtags
Објавено 14 јан.
Допустим, есть некий массив a = [1, 2, 3] Как мы переберём все элементы? Это очевидно. for x in a: print(x) Как это сделать с помощью генератора списка (list comprehension)? r = [x for x in a] Добавим еще один массив b = 'abc' Теперь нам нужно перебрать все возможные сочетания из этих двух списков. Тоже не проблема: for x in a: for y in b: print(x, y) Можно ли такой алгоритм повторить с помощью генератора списка? На самом деле можно >>> [[x, y] for x in a for y in b] [[1, 'a'], [1, 'b'], [1, 'c'], [2, 'a'], ...] А если добавить еще один список? >>> c = ('yes', 'no', 'maybe') >>> r = [[x, y, z] for x in a for y in b for z in c] [[1, 'a', 'yes'], [1, 'a', 'no'], [1, 'a', 'maybe'], ...] Получаем список списков, в которых есть все варианты комбинаций по 3 элемента. В следующем коде круглыми скобками показаны части разных итераций, чтобы было понятно чо во что входит. Код не для исполнения! [( ( ([x, y, z] for x in a) for y in b) for z in c)] Генераторы списков это круто, но когда используются без фанатизма! Всегда помните: Простое лучше чем сложное (Simple is better than complex). И на последок пример: vectors = [[[1, 2, 3], [4, 5, 6], [7, 8, 9]], [[10, 11, 12], [13, 14, 15]]] print([digit for part in vectors for elem in part for digit in elem]) Попробуйте понять что там происходит и что распечатает этот код 😭😵🤪 Это хороший пример того, как не стоит усложнять читаемость кода. PS. Да, "генераторы" в Python это другая сущность, но как-то иначе удачно перевести comprehension не получается. Ну не называть же их "пониматоры" списков) PPS. Для получения множества комбинаций лучше используйте itertools.combinations #tricks
Hashtags
Објавено 13 јан.
Нужно записать большое число? Обычно пишем так for i in range(300000000): execute_test(i) И сколько там нулей? Попробуй посчитать... Такие числа можно записать более кратко или понятно. 1. Экспонента for i in range(3e9): execute_test(i) Запись XeY означает X*(10**Y) # оператор ** это степень Если нужно не ровное число с нулями, то можно добавить любое уточняющее действие >>> 4e10+1234 40000001234.0 >>> 15e5-42 1499958.0 Экспонента может быть отрицательной, тогда нулики добавляем справа от точки >>> 7e-5+1 1.00007 Такая запись не создаёт другой тип данных, это просто формат записи 2. Использовать разделитель Есть специальный синтаксис для записи длинных чисел, используем для разделителя символ подчёркивания. for i in range(300_000_000): execute_test(i) Сразу видны элементы числа! Таким способом можно записать любое число. >>>143_435_543.123_000_2 143435543.1230002 Кстати, наверняка замечали что иногда Python сам выводит числа в виде экспоненты, если это возможно >>> 0.000_000_1 1e-07 #tricks
Hashtags
Објавено 12 јан.
Признавайтесь, кто так пишет? if not value in array: ... По задумке, мы пытаемся проверить, отсутствует ли значение value в списке array. Звучит логично, но! В данном случае оператор not и оператор in не связаны. То есть всё, что делает оператор not, это инвертирование значения, идущего после него, причём результатом будет bool. А после мы проверяем, есть ли этот bool в списке array??? Неожиданный поворот! Выглядеть это может так: if (not value) in array: ... Но стоп, почему же тогда никаких проблем с этой записью нет и ОНО РАБОТАЕТ в таком виде!? Дело в том, что я намеренно перечислил операторы в порядке их написания (чтобы страшней было). Хотя, на самом деле, оператор inимеет бОльший приоритет. В этом примере мы сначала проверяем наличие элемента в списке (вместо проверки "отсутствия") а потом инвертируем результат. if not (value in array): ... Да, получаем ровно тот же результат (считай повезло), но в два действия. Специально для таких случаев есть оператор not in, то есть более "питонично" писать так: if value not in array: ... Это будет выполняться в одно действие, так как оператор один (несмотря не мизерные затраты времени оператора not). Понимайте то, что кодите! #tricks
Hashtags
Објавено 11 јан.
Не удержался и решил показать пример как можно записать код из поста про if-else еще короче, буквально в одну строку! value = any((get_new_value(i) for i in array)) or get_default() Тут стоит знать две особенности функций any/all и операторов or/and. 1.Эти операторы не возвращают тип bool Они только делают преобразование данных в этот тип на момент сравнения. 2. Как именно устроена логика? В какой момент сработает return? any() - возвращает первый элемент, который преобразовался в True или последний элемент all() - возвращает первый элемент, который преобразовался в False или последний элемент or и and делают аналогично, только для двух операндов Но все они возвращают именно исходное значение, а не bool! Ну а наличие генератора позволяет не выполнять лишних операций, только до момента нахождения нужного значения. Если значение так и не найдено, оператор or запустит функцию get_default(). PS. Несмотря на возможность записать таким образом, я призываю всегда отдавать предпочтение понятности кода, а не краткости. Если про понятность не понятно, делаемimport thisи читаем, читаем, читаем... #tricks
Hashtags
Објавено 10 јан.
Аналогично конструкции for-else есть конструкция while-else Как работает? Блок while перед каждой итерацией проверяет условие. Если оно верно, то выполняется блок цикла. Если нет, то выполняется блок else, после которого выход из итерации. Так это обычно выглядит в виде простой и понятной записи: while True: if [condition]: # здесь тело цикла else: # здесь выполняем код перед выходом break # выходим из цикла Почему мы не поместили кондишен непосредственно после while? Чтобы перехватить его изменение и выполнить что-то еще, используя дополнительные условия. А вот так в сокращенном виде while [condition]: # здесь тело цикла else: # здесь выполняем код перед выходом Стоит уточнить, что если в теле цикла вызвать break, то блок else не сработает. #tricks
Hashtags
Објавено 10 јан.
На заметку начинающим. Оператор for..in имеет необязательный блок else. Как это работает? Этот блок выполняется только если итерация завершилась успешно, пройдя все элементы. Чтобы блок else не выполнился, итерация должна прерваться с помощью break. Зачем это нужно? Предполагается, что такая конструкция нужна для определения значения по умолчанию, в случае если мы ищем нужное значение в цикле. Как только необходимые данные найдены, выходим из цикла с помощью break. Если ничего не нашли, то выполняется блок else, в котором выполняем альтернативные действия. Чем полезно? В целом, пишется более лаконично (читай питонично) Позволяет сократить время вычислений, если получение дефолта достаточно затратная процедура. Например, есть такой код: value = get_default() for i in array: res = get_new_value(i) if res: value = res break В этом примере мы сначала создаем значение по умолчанию, потом в итерации получаем нужный параметр. Если get_new_value() не вернула новое значение, блок if никогда не сработает и остается значение по умолчанию. Но что, если функция get_default() занимает слишком много времени или не должна вызываться просто так? Тогда написать можно иначе: value = None for i in array: res = get_new_value(i) if res: value = res break if value is None: value = get_default() А с блоком else можно записать короче for i in array: res = get_new_value(i) if res: value = res break else: value = get_default() А тех кто на Python3.8+, еще короче: for i in array: if (res:=get_new_value(i)): value = res break else: value = get_default() Вероятно, можно такую логику записать еще короче, но это уже другая история #tricks
Hashtags
Објавено 9 јан.
Есть такая запись форматирования >>> "{0:>10}".format(42) " 42" Что означает: сделать строку шириной 10 символов и переданный аргумент выровнять по правую сторону. Есть аналог для перемещения влево и по центру, но не об этом сейчас. Что будет, если подать в такую строку кастомный класс? >>> class MyClass: >>> pass >>> c = MyClass() >>> "{0:>10}".format(c) TypeError: unsupported format string passed to MyClass.__format__ Упс, ошибка форматирования! То есть Python говорит что у нашего класса нет метода ˍˍformatˍˍ. Даже ˍˍstrˍˍ и ˍˍreprˍˍ не помогут. >>> class MyClass: >>> def __str__(self): >>> return 'My Class' >>> def __repr__(self): >>> return '<My Class>' >>> c = MyClass() >>> "{0:>20}".format(c) TypeError: unsupported format string passed to MyClass.__format__ Что ж, это легко решается добавлением метода ˍˍformatˍˍ. Но а что если это не ваши классы и изменить исходники никак нельзя? Можно сделать небольшое преобразование, например такими способами: >>> "{0:>20}".format(str(c)) " My Class" >>> "{0:>20}".format(repr(c)) " <My Class>" Но лучше использовать явный конвертор, то есть механизмы самого форматирования. В таком случае необходимое действия будет жёстко указано в самом паттерне. Чтобы "конвертнуть" свой класс с помощью ˍˍstrˍˍ пишем так: "{0!s:>20}".format(c) А чтобы преобразование сделалось с помощью ˍˍreprˍˍ, пишем так: "{0!r:>20}".format(c) Теперь не важно какой класс прилетит, всё сработает! Только учтите, если не определить методы ˍˍstrˍˍ и ˍˍreprˍˍ то получится что-то типа: "<MyClass object at 0x7f27a62ed278>" Но хотя бы не будет ошибки. PS. А если определить метод ˍˍformatˍˍ, то можно и другие буковки использовать для специфичных преобразований! #tricks
Hashtags
Објавено 6 јан.
Ранее, в посте о главных новшествах Python3, я упоминал про полный переход на unicode. А в конце было сказано: "Конечно же это НЕ относится к именам переменных и файлов! Только строки и комменты." На самом деле это было лишь предостережение. Можно создавать имена переменных и модулей на unicode! То есть мы вполне можем сделать так: Bash: $ echo "print('Приветы')" > моймодуль.py // создали файл с именем на кириллице >>> import моймодуль Приветы Или так >>> Василий = "Василий" >>> Петрович = "Петрович" >>> фулнейм = ' '.join([Василий, Петрович]) >>> print(фулнейм) Василий Петрович Или так def сделать_красиво(было): более_красиво = 100500 стало = было * более_красиво return стало Или так >>> Ψ = 100 >>> Σ = -100 >>> смысл = Ψ + Σ >>> print(смысл) 0 Надеюсь не нужно объяснять что смысла в этом ноль и так делать не стоит))) Кстати, это была секретная информация, так что никому! Чтобы и в мыслях не было! 🤐 PS. я поддерживаю русский язык только в комментариях по коду. Конечно, если только вы уверены, что команда разработчиков будет русскоязычной и никак иначе! Это очень помогает разбираться в чужом коде (или в своём через время) Но это крайний случай. #tricks
Hashtags