Про культуру коммитов
Работаю над куском кода, который опирается на json файл. Файл этот длиной 7к+ строк (только не спрашивайте, как так вышло). И кусок этот одновременно
- сложный
- используется большим количеством пользователей.
И тут из мастера в этот json прилетают изменения. В которых во всем файле пробелы заменены на табы. То есть, дифф слишком большой и отсмотреть глазами не получается. Поправлю, конечно, но досадно -- и небезопасно.
В связи с чем напоминаю, как коммитить (вот, например, хорошая статья от другого автора)
Для меня главное в этом тексте:
Один коммит = одна атомарная задача
И от себя бы еще добавила:
Дифф удобно смотреть ривьюеру
Чтобы этого добиться, перед каждым коммитом нужно отсматривать изменения в каждом файле, которые хотите залить в репо: с помощью
И еще я никогда не делаю
чтобы даже не привыкать добавлять всю работу, которая у меня есть локально: в ней бывают мусор и наброски кода. Если себя приучить к этому, то гораздо сложнее случайно залить то, что лить не хотели.
Поделитесь, какие хорошие практики работы с git вы усвоили за время работы?
Работаю над куском кода, который опирается на json файл. Файл этот длиной 7к+ строк (только не спрашивайте, как так вышло). И кусок этот одновременно
- сложный
- используется большим количеством пользователей.
И тут из мастера в этот json прилетают изменения. В которых во всем файле пробелы заменены на табы. То есть, дифф слишком большой и отсмотреть глазами не получается. Поправлю, конечно, но досадно -- и небезопасно.
В связи с чем напоминаю, как коммитить (вот, например, хорошая статья от другого автора)
Для меня главное в этом тексте:
Один коммит = одна атомарная задача
И от себя бы еще добавила:
Дифф удобно смотреть ривьюеру
Чтобы этого добиться, перед каждым коммитом нужно отсматривать изменения в каждом файле, которые хотите залить в репо: с помощью
git diff
или в IDE.И еще я никогда не делаю
git add .
чтобы даже не привыкать добавлять всю работу, которая у меня есть локально: в ней бывают мусор и наброски кода. Если себя приучить к этому, то гораздо сложнее случайно залить то, что лить не хотели.
Поделитесь, какие хорошие практики работы с git вы усвоили за время работы?
Уже больше часа лежит весь Фейсбук. Вместе с продуктами компании (WhatsApp, Instagram, мессенджером), отвалились даже DNS и страница статусов https://status.fb.com. И может не работать всё то, что опиралось на fb.
Интересно, что случилось.
Интересно, что случилось.
Вопрос по Python. Как думаете, является ли тип complex составным?
Anonymous Quiz
19%
Это простой тип
36%
Это составной тип
45%
Что такое complex?
Пара фактов о численных типах
(которые вы, возможно, не знали)
Факт 1
В Python есть три встроенных численных типа. Кроме
Комплексные числа много используют в математике (например, с их помощью можно брать некоторые забористые интегралы, которые обычным способом не берутся), и в физике (особенно в расчетах, связанных с электричеством и магнетизмом).
Сконструировать комплексное число в Python можно так:
или вот так:
Получится одно и то же.
Факт 2
Все численные типы в Python унаследованы от класса
Кстати, сюрприз:
Факт 3
Под капотом логический тип — те же числа, только
Это же, впрочем, дает ни разу не интуитивное поведение в некоторых случаях:
Факт 4
Complex не является составным типом. Это просто объект, который принимает до двух параметров при инициализации.
Такие дела!🤗
(которые вы, возможно, не знали)
Факт 1
В Python есть три встроенных численных типа. Кроме
int
и float
, которыми мы обычно пользуемся, есть еще complex
— комплексные числа. Комплексные числа много используют в математике (например, с их помощью можно брать некоторые забористые интегралы, которые обычным способом не берутся), и в физике (особенно в расчетах, связанных с электричеством и магнетизмом).
Сконструировать комплексное число в Python можно так:
a = complex(2, 1)
или вот так:
a = 2 + 1j
Получится одно и то же.
Факт 2
Все численные типы в Python унаследованы от класса
Number
. Проверить это можно так: from numbers import Number
isinstance(1984, Number) #True
isinstance(3.1415926, Number) #True
isinstance(1j, Number) #True
Кстати, сюрприз:
bool
тоже унаследован от Number
:isinstance(False, Number) #True
Факт 3
Под капотом логический тип — те же числа, только
bool
имеет всего два значения: 0
и 1
. Это обеспечивает нам легкое приведение True
к единице, а False
к нулю. Это же, впрочем, дает ни разу не интуитивное поведение в некоторых случаях:
1/False # ZeroDivisionError: division by zero
True * 1 # 1
False * 1 # 0
complex(True, 3.9) # (1+3.9j)
my_list = [1, 2, 3, 4]
my_list[False] # 1
"False"[True] # a
А, и даФакт 4
Complex не является составным типом. Это просто объект, который принимает до двух параметров при инициализации.
Такие дела!🤗
/
и *
в определнии функцииВидели когда-нибудь вот такой синтаксис?
def foo(first, /, second, *, third):
print(first, second, third)
Выглядит странно?
На самом деле
/
и *
навязывают положение ключевых и позиционных аргументов.Попытаемся, например, вызвать
foo()
с неправильным набором параметров: >>> foo("bar", "qux")
...
TypeError: foo() missing 1 required keyword-only argument: 'third'
Нужен keyword аргумент, окей, укажем все параметры:
>>> foo("bar", "baz", "qux")
...
TypeError: foo() takes 2 positional arguments but 3 were given
А, один из них по ключу:
>>> foo("bar", "baz", third="qux")
bar baz qux
Получилось!
Оба символа опциональны. И, в моем опыте, если ставят, то чаще только звездочку.
Источник: https://docs.python.org/3/tutorial/controlflow.html#special-parameters
🐠
Делюсь с вами коллективным учебником по машинному обучению из Яндекса. На самом деле наполовину делюсь, наполовину сохраняю себе, чтобы прочитать позже. Теплый привет Вите Кантору
У нас опять есть группа, можно покомментить и покидаться какашками
UPD Правила игры те же: нетоксично про питон, программирование, инфраструктуру и за жизнь. Но нужно быть пуськой.
UPD2 https://t.me/+CgNMmJwzjYI1MmYy
UPD Правила игры те же: нетоксично про питон, программирование, инфраструктуру и за жизнь. Но нужно быть пуськой.
UPD2 https://t.me/+CgNMmJwzjYI1MmYy
Forwarded from Советы разработчикам (python и не только)
Работа import и структура проекта
Когда вы импортируете какой-то модуль в вашем коде, питон не учитывает, в каком файле этот импорт находится, влияет только то, как был запущен код.
Если модуль не был раньше загружен, питон пытается его найти по очереди в нескольких папках, которые можно посмотреть в переменной
* каталог, добавляемый при запуске
* каталоги указанные в переменной окружения
* каталог установки python
1. Если вы запускаете ваш скрипт командой
2. Если вы запускаете ваш код командой
3. Если вы запускаете код с помощью других инструментов вроде
Скорее всего, вам не стоит самостоятельно менять
Так как поиск пакетов для импорта происходит сначала в каталоге "проекта", стоит быть аккуратным именованием ваших файлов и каталогов. Если вы случайно назовете ваш модуль так же как встроенный или сторонний, при любом импорте такого модуля будет грузиться именно ваш, что сломает работу кода.
Иногда используемые нами фреймворки поддерживают только определенную, не всегда оптимальную, структуру проекта. В остальных случаях я могу предложить два подхода:
1. Вынести запускаемые скрипты на верхний уровень, а остальной код упаковать в пакет.
Упаковка кода в пакет с уникальным именем позволяет исключить конфликты имен. А вынесение всех запускаемых файлов на один уровень делает состав
Выглядеть это будет примерно так:
В этом случае вы упаковываете весь код в пакет, что помогает исключить конфликты имен.
Для запуска команд вы можете использовать синтаксис
Для удобства разработки с таким подходом удобно устанавливать пакет в editable-режиме с помощью команды типа
Структура будет примерно такой:
* https://packaging.python.org/en/latest/
* https://docs.python.org/3/reference/import.html
* https://docs.python.org/3/library/sys.html#sys.path
* https://ru.wikipedia.org/wiki/Рабочий_каталог
Когда вы импортируете какой-то модуль в вашем коде, питон не учитывает, в каком файле этот импорт находится, влияет только то, как был запущен код.
Если модуль не был раньше загружен, питон пытается его найти по очереди в нескольких папках, которые можно посмотреть в переменной
sys.path
По умолчанию она содержит примерно такие каталоги:* каталог, добавляемый при запуске
* каталоги указанные в переменной окружения
PYTHONPATH
* каталог текущего активированного виртуального окружения* каталог установки python
1. Если вы запускаете ваш скрипт командой
python scriptname.py
, то первым в списке будет тот каталог, где находится запускаемый скрипт. Текущий каталог не имеет значения.2. Если вы запускаете ваш код командой
python -m packagename
, то первым в списке будет текущий каталог. При запуске питон попытается найти и импортировать packagename по общим правилам.3. Если вы запускаете код с помощью других инструментов вроде
pytest
, они тоже могут сами добавлять что-то в sys.path
.Скорее всего, вам не стоит самостоятельно менять
sys.path
, так как алгоритм его заполнения стандартный и привычен для всех. Если по каким-то причинам вас он не устраивает, возможно у вас неверная структура проекта.Так как поиск пакетов для импорта происходит сначала в каталоге "проекта", стоит быть аккуратным именованием ваших файлов и каталогов. Если вы случайно назовете ваш модуль так же как встроенный или сторонний, при любом импорте такого модуля будет грузиться именно ваш, что сломает работу кода.
Иногда используемые нами фреймворки поддерживают только определенную, не всегда оптимальную, структуру проекта. В остальных случаях я могу предложить два подхода:
1. Вынести запускаемые скрипты на верхний уровень, а остальной код упаковать в пакет.
Упаковка кода в пакет с уникальным именем позволяет исключить конфликты имен. А вынесение всех запускаемых файлов на один уровень делает состав
sys.path
предсказуемым.Выглядеть это будет примерно так:
├── appname2. Создать распространяемый пакет (рекомендую).
│ ├── __init__.py
│ ├── other_module.py
│ └── some_module.py
├── cli_module.py
└── requirements.txt
В этом случае вы упаковываете весь код в пакет, что помогает исключить конфликты имен.
Для запуска команд вы можете использовать синтаксис
python -m appname.cli_module
или заполнить секцию entry_points
в файле с описанием проекта (setup.cfg
, pyproject.toml
), после чего иметь свои кастомные консольные команды. В обоих случаях вы сможете запускать код, находясь в любом каталоге, без необходимости указывать полные пути к файлам.Для удобства разработки с таким подходом удобно устанавливать пакет в editable-режиме с помощью команды типа
pip install -e .
Структура будет примерно такой:
├── pyproject.tomlДополнительные материалы:
└── src
└── appname
├── __init__.py
├── cli_module.py
├── other_module.py
└── some_module.py
* https://packaging.python.org/en/latest/
* https://docs.python.org/3/reference/import.html
* https://docs.python.org/3/library/sys.html#sys.path
* https://ru.wikipedia.org/wiki/Рабочий_каталог
Сегодня в 18:00 UTC+2 релизят Python 3.11 в прямом эфире, смотреть можно по ссылке ниже:
https://www.youtube.com/watch?v=PGZPSWZSkJI
UPD: еще можно подписаться на их youtube, чтобы не пропускать релизы в будущем, если вам интересно
https://www.youtube.com/watch?v=PGZPSWZSkJI
UPD: еще можно подписаться на их youtube, чтобы не пропускать релизы в будущем, если вам интересно
YouTube
Python 3.11 Release
Timestamps
00:00 - Introduction
24:30 - Brandt Bucher, Specializing Adaptive Interpreter
50:40 - Mark Shannon, Other Speedups
1:07:42 - Irit Katriel, Exception Improvements and Features
1:42:13 - Pablo Galindo, Better Tracebacks
1:58:46 - Pablo Galindo, tomllib…
00:00 - Introduction
24:30 - Brandt Bucher, Specializing Adaptive Interpreter
50:40 - Mark Shannon, Other Speedups
1:07:42 - Irit Katriel, Exception Improvements and Features
1:42:13 - Pablo Galindo, Better Tracebacks
1:58:46 - Pablo Galindo, tomllib…
Фича f-строк, о которой вы не знали
Смотрите, вот обычная f-строка
>>> text = 'JUPI'
>>> print(f'{text}')
JUPI
Если я добавлю в нее
* двоеточие,
* символ-заполнитель,
* и укажу требуемую длину строки с помощью символа <
>>> print(f'{text:-<15}')
То не занятый текстом остаток строки будет заполнен выбранным символом:
JUPI-----------
Если указать длину строки через >, то можно заполнить строку с левой стороны:
>>> print(f'{text:->15}')
-----------JUPI
Напишите в комментариях, как заполнить строку с обоих сторон, чтобы получилось
-----JUPI------
Подсказка: можно поискать вот здесь
🐠
Смотрите, вот обычная f-строка
>>> text = 'JUPI'
>>> print(f'{text}')
JUPI
Если я добавлю в нее
* двоеточие,
* символ-заполнитель,
* и укажу требуемую длину строки с помощью символа <
>>> print(f'{text:-<15}')
То не занятый текстом остаток строки будет заполнен выбранным символом:
JUPI-----------
Если указать длину строки через >, то можно заполнить строку с левой стороны:
>>> print(f'{text:->15}')
-----------JUPI
Напишите в комментариях, как заполнить строку с обоих сторон, чтобы получилось
-----JUPI------
Подсказка: можно поискать вот здесь
🐠
Python Enhancement Proposals (PEPs)
PEP 498 – Literal String Interpolation | peps.python.org
Python supports multiple ways to format text strings. These include %-formatting 1, str.format() 2, and string.Template 3. Each of these methods have their advantages, but in addition have disadvantages that make them cumbersome to use in practice. This...
Что такое JSON?
JSON (JavaScript Object Notation) – это удобный для чтения человеком и машиной структурированный формат, который используют для обмена данными.
JSON представляет собой наборы пар «ключ: значение» и основан на базе синтаксиса объектов JavaScript. При этом JSON считается независимым от JavaScript, так как может использоваться с любым языком программирования.
История
Создатель формата JSON – Дуглас Крокфорд, американский программист, принимающий активное участие в развитии JavaScript. В 2001 году, во время работы в своей компании State Software он разработал стандарт JSON как аналог формата XML, который уже существовал в тот момент.
Дугласу нужна была технология, позволяющая передавать данные в приложение до загрузки начальной страницы браузера, но еще не было формата, который работал бы со всеми браузерами.
Самое первое сообщение JSON было отправлено в 2001 году. Вот как оно выглядело:
Это был html-документ, только небольшая часть которого напоминает современный JSON. Сначала этот формат был предназначен для языка JavaScript, но позже его стали использовать с другими языками программирования.
Через год Крокфорд купил домен JSON.org и опубликовал там синтаксис JSON с примерами реализации, и этот сайт до сих пор работает.
Какую задачу решает
JSON – это случай, когда простой и лаконичный формат вытеснил более сложный и функциональный. Поскольку JSON предназначен для передачи данных по сети, то важные его функции – это сериализация и десериализация.
Сериализация – это кодирование данных, превращение в серию байтов, их перенос в файл JSON с целью хранения или передачи по сети. Десериализация – обратный процесс распаковки данных.
JSON полезнее всего
- для создания приложений, взаимодействующих с браузером,
- там, где необходимо обмениваться информацией между серверами,
- и когда нужно передать структурированную информацию по сети.
Как выглядит JSON
JSON файл структурирован и состоит из пар ключ-значение:
Вот список типов данных, которые поддерживает JSON:
- Строка – несколько литералов, заключенных в кавычки.
- Число – целое или действительное.
- Массив – множество значений, заключенное в квадратные скобки.
- Логическое значение – true/false.
- Пустое значение – null.
Как работать с JSON
Сегодня в любом языке программирования есть встроенная библиотека или модуль для работы с JSON, и в Python тоже! Для начала работы достаточно подключить модуль json с помощью команды import:
Сериализовать данные (например, словарь data) можно с помощью метода dump:
При кодировании из Python в JSON данные меняют свой тип следующим образом:
Записать данные в строку можно, используя метод dumps(). Методы похожи, единственное отличие состоит в том, что вторым параметром не указывается файловый объект, так как на самом деле не происходит записи на диск. В файле с кодом все будет выглядеть подобным образом:
Чтобы десериализовать файл, можно использовать методы
Так же, как и при сериализации, происходит трансформация данных в характерный для языка программирования тип. При этом переход из JSON в Python не полностью аналогичен переходу из Python в JSON.
Итого
- JSON – это в первую очередь человекочитаемый формат.
- Используется для передачи данных по сети и для клиент-серверного взаимодействия.
- Важные функции – это сериализация и десериализация, которые осуществляются методами
JSON (JavaScript Object Notation) – это удобный для чтения человеком и машиной структурированный формат, который используют для обмена данными.
JSON представляет собой наборы пар «ключ: значение» и основан на базе синтаксиса объектов JavaScript. При этом JSON считается независимым от JavaScript, так как может использоваться с любым языком программирования.
История
Создатель формата JSON – Дуглас Крокфорд, американский программист, принимающий активное участие в развитии JavaScript. В 2001 году, во время работы в своей компании State Software он разработал стандарт JSON как аналог формата XML, который уже существовал в тот момент.
Дугласу нужна была технология, позволяющая передавать данные в приложение до загрузки начальной страницы браузера, но еще не было формата, который работал бы со всеми браузерами.
Самое первое сообщение JSON было отправлено в 2001 году. Вот как оно выглядело:
<html><head><script>
document.domain = 'fudco';
parent.session.receive({
to: "session",
do: "test",
text: "Hello world"
})
</script></head></html>
Это был html-документ, только небольшая часть которого напоминает современный JSON. Сначала этот формат был предназначен для языка JavaScript, но позже его стали использовать с другими языками программирования.
Через год Крокфорд купил домен JSON.org и опубликовал там синтаксис JSON с примерами реализации, и этот сайт до сих пор работает.
Какую задачу решает
JSON – это случай, когда простой и лаконичный формат вытеснил более сложный и функциональный. Поскольку JSON предназначен для передачи данных по сети, то важные его функции – это сериализация и десериализация.
Сериализация – это кодирование данных, превращение в серию байтов, их перенос в файл JSON с целью хранения или передачи по сети. Десериализация – обратный процесс распаковки данных.
JSON полезнее всего
- для создания приложений, взаимодействующих с браузером,
- там, где необходимо обмениваться информацией между серверами,
- и когда нужно передать структурированную информацию по сети.
Как выглядит JSON
JSON файл структурирован и состоит из пар ключ-значение:
{
"name": "John Smith",
"age": 35
}
Вот список типов данных, которые поддерживает JSON:
- Строка – несколько литералов, заключенных в кавычки.
- Число – целое или действительное.
- Массив – множество значений, заключенное в квадратные скобки.
- Логическое значение – true/false.
- Пустое значение – null.
Как работать с JSON
Сегодня в любом языке программирования есть встроенная библиотека или модуль для работы с JSON, и в Python тоже! Для начала работы достаточно подключить модуль json с помощью команды import:
import json
Сериализовать данные (например, словарь data) можно с помощью метода dump:
with open(“my_file.txt”, “w”) as file:
json.dump(data, file)
При кодировании из Python в JSON данные меняют свой тип следующим образом:
dict -> Object
list-> Array
tuple -> Array
str -> String
int -> Number
float -> Number
True / False -> true / false
None -> null
Записать данные в строку можно, используя метод dumps(). Методы похожи, единственное отличие состоит в том, что вторым параметром не указывается файловый объект, так как на самом деле не происходит записи на диск. В файле с кодом все будет выглядеть подобным образом:
data_string = json.dumps(data)
Чтобы десериализовать файл, можно использовать методы
load()
и loads()
:with open(“my_file.txt”, “r”) as file:
data = json.load(file)
Так же, как и при сериализации, происходит трансформация данных в характерный для языка программирования тип. При этом переход из JSON в Python не полностью аналогичен переходу из Python в JSON.
loads()
идентичен dumps()
, он также предназначен для работы со строкой.Итого
- JSON – это в первую очередь человекочитаемый формат.
- Используется для передачи данных по сети и для клиент-серверного взаимодействия.
- Важные функции – это сериализация и десериализация, которые осуществляются методами
dump()
и load()
.Все знают, что целочисленное деление округляет результат вниз. Например, если результат деления во float
5. / 3. = 1.66666667 то в целых числах он округлится вниз, а не к ближайшему целому: 5 // 3 = 1 А если результат отрицательный? - 5 // 3 = ?
5. / 3. = 1.66666667 то в целых числах он округлится вниз, а не к ближайшему целому: 5 // 3 = 1 А если результат отрицательный? - 5 // 3 = ?
Anonymous Quiz
8%
1
38%
-1
45%
-2
8%
Ошибка
Стек и очередь
Идеи стека и очереди часто встречаются в различных языках программирования. Давайте разберемся, в каких ситуациях их применение уместно и удобно программисту.
С очередями мы сталкиваемся практически везде. В магазине очередь – это группа людей, которые стоят друг за другом, например, на кассу с продуктами. Тот, кто стоит ближе к кассе, оплатит свои продукты первым.
В программировании очередь работает аналогично. Очередь в программировании — это коллекция элементов, которые обрабатываются по принципу «первый зашел — первый вышел» (FIFO).
Очередь прекрасно подходит к задачам, которые нужно делать поэтапно. Например, если пользователь загружает несколько изображений на сайт, чтобы сервер мог их обработать и сохранить, загружаемые файлы помещаются в очередь на обработку. И первый файл, который пользователь загрузил, будет первым обработан на сервере.
Стек работает по-другому. Стек – это тоже коллекция элементов, но работает он по принципу «последним зашел — первым вышел» (LIFO)
В повседневной жизни мы можем встретить аналогию, когда мы кладем документы друг на друга, а затем перебираем их, начиная с самого верхнего. Это похоже на работу со стеком: можно работать только с последним элементом, который попал в коллекцию.
Частое применение стека в программировании – это организация записей об изменениях в файле или документе. Текстовые редакторы обычно записывают действия пользователя над файлом с помощью стека, чтобы потом можно было легко отменить эти действия в обратном порядке.
При работе со стеком и очередью нельзя обращаться к произвольному элементу в коллекции, потому что это нарушило бы принципы LIFO или FIFO. Чтобы добраться до нужного элемента, необходимо перебрать все элементы стоящие перед или после него в списке по установленному порядку.
В программировании стек и очередь часто используют для математических вычислений, для работы с данными и операциями. Принципы FIFO и LIFO позволяют нам структурировать и упорядочивать работу с данными. Такой подход позволяет быть уверенными, что данные и операции будут обрабатываться в нужном порядке.
Идеи стека и очереди часто встречаются в различных языках программирования. Давайте разберемся, в каких ситуациях их применение уместно и удобно программисту.
С очередями мы сталкиваемся практически везде. В магазине очередь – это группа людей, которые стоят друг за другом, например, на кассу с продуктами. Тот, кто стоит ближе к кассе, оплатит свои продукты первым.
В программировании очередь работает аналогично. Очередь в программировании — это коллекция элементов, которые обрабатываются по принципу «первый зашел — первый вышел» (FIFO).
Очередь прекрасно подходит к задачам, которые нужно делать поэтапно. Например, если пользователь загружает несколько изображений на сайт, чтобы сервер мог их обработать и сохранить, загружаемые файлы помещаются в очередь на обработку. И первый файл, который пользователь загрузил, будет первым обработан на сервере.
Стек работает по-другому. Стек – это тоже коллекция элементов, но работает он по принципу «последним зашел — первым вышел» (LIFO)
В повседневной жизни мы можем встретить аналогию, когда мы кладем документы друг на друга, а затем перебираем их, начиная с самого верхнего. Это похоже на работу со стеком: можно работать только с последним элементом, который попал в коллекцию.
Частое применение стека в программировании – это организация записей об изменениях в файле или документе. Текстовые редакторы обычно записывают действия пользователя над файлом с помощью стека, чтобы потом можно было легко отменить эти действия в обратном порядке.
При работе со стеком и очередью нельзя обращаться к произвольному элементу в коллекции, потому что это нарушило бы принципы LIFO или FIFO. Чтобы добраться до нужного элемента, необходимо перебрать все элементы стоящие перед или после него в списке по установленному порядку.
В программировании стек и очередь часто используют для математических вычислений, для работы с данными и операциями. Принципы FIFO и LIFO позволяют нам структурировать и упорядочивать работу с данными. Такой подход позволяет быть уверенными, что данные и операции будут обрабатываться в нужном порядке.
Рассказали о разнице функций input и raw_input в Python2 в карточках. Читайте!
Карточки подготовили вместе с @pro_python_code
Карточки подготовили вместе с @pro_python_code