Python: вопросы на собеседовании. Часть III. Senior
Мы уже рассмотрели списки вопросов с собеседований для программистов уровня Junior и Middle. Переходим к самым сложным вопросам, для сеньоров. Все вопросы — из коллекции, собранной на основе реальных интервью.
Все части нашей «трилогии»:
- Python: вопросы собеседования. Часть I. Junior
- Python: вопросы собеседования. Часть II. Middle
- Python: вопросы собеседования. Часть III. Senior
Нам всем хорошо известно, что Senior Developer это не только техническая роль, связанная с большим опытом работы и глубокими знаниями. У него также должны быть навыки наставника, некоторые навыки управления, чтобы, например, оценивать время исполнения задач, а также определенные навыки в разработке архитектуры. Но данные вопросы посвящены только технической части его работы.
Вопросы
- Возможно ли использовать конструкцию True = False ?
- Когда в конструкции try. except. else может быть исполнен блок кода else ?
- Что такое обезьяний патч (Monkey patch)? Как его можно использовать в Python? Приведите примеры. А вообще это хорошая идея?
- Как dict и set реализованы внутри Python?
- Что такое порядок разрешения методов (Method Resolution Order — MRO) в Python? Как он работает?
- В Python есть оператор присваивания?
- Что такое дескрипторы? Есть ли разница между дескриптором и декоратором?
- Как в Python передаются аргументы, по значению или по ссылке?
- Какие инструменты помогают вам находить червоточину в коде или производить статический анализ кода? Что еще вы умеете делать, чтобы код был читаемым и поддерживаемым?
- Почему, когда Python завершает работу, освобождается не вся память?
- Возможно ли, чтобы поток производителя, читающий из сети, и поток потребителя, пишущий в файл, работали одновременно? И как насчет GIL (Global Interpreter Lock)?
- Что такое GIL? И почему GIL до сих пор существует?
- Что из себя представляют процессы компиляции и линковки в Python?
- Как создавать пакеты в Python? Какие пакетные менеджеры вы знаете?
- Как работать с переходными зависимостями?
- Что такое wheel и eggs в Python? Какая между ними разница?
- Как распространять код на Python?
- Как упаковывать бинарные зависимости?
- Что такое пакетный менеджер? Каким вы пользуетесь и почему?
- Что такое Cython?
- Как ускорить уже написанный код Python?
- Как изолировать код Python? Что такое virtualenvs? Что такое docker?
- Python поддерживает парадигму функционального программирования? Обозначьте требования к коду, написанному в такой парадигме.
- Определите подводные камни и ограничения функционального кода.
- Объясните, как получить доступ к модулю, написанному на Python, из C. А наоборот?
- Что такое файлы с расширением .pth ?
- Каковы преимущества массивов NumPy перед списками Python (в частности, вложенными списками)?
- Что такое CUDA?
- Что представляет из себя платформа Anaconda? Зачем она нужна?
- Что делает флаг PYTHONOPTIMIZE ?
Вопросы с кодом
- Приведите примеры работы функций filter и reduce с итерируемым объектом.
- У вас есть функция, которая принимает другую функцию в качестве аргумента. Как проверить значение аргумента?
- Расскажите, как поменять порядок работы генератора.
- Вам нужно реализовать функцию, которая будет использовать статическую переменную (например, счетчик вызовов). Вы не можете писать никакого кода вне этой функции, к тому же у вас нет никакой информации о внешних переменных (вне вашей функции). Как это сделать?
- У вас есть функция, которая принимает аргумент, находящийся в заданных рамках [a, b] . Установите параметры реализации теста так, чтобы увеличить скорость тестирования. Варианты данного задания:
- аргумент находится в списке [a, b] ;
- a
- a in range(a, b + 1) ;
- Какие методы и в каком порядке вызываются при выполнении данного выражения: print (A() + B()) ?
- Каким будет результат выполнения данной функции?
def Foo(): yield 42; return 666
- Что будет в результате выполнения данного кода?
a = [[]]*3 a[1].append(1) print(a) # [[1], [1], [1]
- Расположите данные функции в порядке их быстродействия. Обоснуйте ответ.
def f1(lIn): l1 = sorted(lIn) l2 = [i for i in l1 if idef f2(lIn): l1 = [i for i in lIn if idef f3(lIn): l1 = [i*i for i in lIn] l2 = sorted(l1) return [i for i in l1 if i
- Напишите однострочный код для вычисления всех заглавных букв в файле. Ваш код должен работать, даже если файл слишком велик, чтобы целиком поместиться в памяти.
- Каким будет результат? Почему? И есть ли здесь наследование?
class C: pass type (C ()) type (C)
- Какой результат мы получим, запустив следующий код?
big_num_1 = 1000 big_num_2 = 1000 small_num_1 = 1 small_num_2 = 1 big_num_1 is big_num_2 small_num_1 is small_num_2
- Как такое возможно?
_MangledGlobal__mangled = 23 class MangledGlobal: def test(self): return __mangled >>> MangledGlobal().test() 23
- Какой будет результат?
- Вы видите только следующий кусок кода. Что с ним не так? И для чего такой код нужен?
if __debug__: assert False, ("error")Что делает оптимизация Python (-O или PYTHONOPTIMIZE)?
В документах говорится, что интерпретатор Python выполняет "базовые оптимизации", не вдаваясь в подробности. Очевидно, что это зависит от реализации, но есть ли способ понять, какие вещи могут быть оптимизированы, и сколько экономии времени выполнения он мог бы генерировать? Есть ли недостаток в использовании -O? Единственное, что я знаю, это то, что -O отключает assert , но, предположительно, не следует использовать assert для вещей, которые все еще могут ошибиться в производстве.
max 24 янв. 2011, в 00:12
Поделиться
Возможный дубликат Какая польза от основного режима оптимизации Python? ( python -O )
tzot 17 фев. 2011, в 08:52
Поделиться:
optimization1 ответ
Лучший ответ
В Python 2.7, -O имеет следующий эффект:
- расширение байтового кода изменяется на .pyo
- sys.flags.optimize получает значение 1
- __debug__ - False
- утверждения не выполняются
Кроме того, -OO имеет следующий эффект:
- sys.flags.optimize получает значение 2
- Строки doc недоступны
Чтобы проверить эффект для другого выпуска CPython, grep исходный код для Py_OptimizeFlag .
Martin v. Löwis 23 янв. 2011, в 23:16
Поделиться
Есть ли какой-либо недостаток флага -O, кроме отсутствия во встроенной информации отладки?
max 24 янв. 2011, в 02:17
Я видел много модулей Python, которые предполагают, что строки документации доступны, и сломались бы, если бы этот уровень оптимизации использовался, например, в компании, где я работаю, raw sql помещается в строки документации и выполняется с помощью декораторов функций (даже не шутка) ). Несколько реже assert используется для выполнения логических функций, а не просто для объявления инвариантных ожиданий точки в коде, и поэтому любой подобный код также будет поврежден.
SingleNegationElimination 24 янв. 2011, в 03:12
@max: если вы пройдете полный список семантических изменений выше: считаете ли вы какие-либо из них "недостатком"? Если нет, то минусов нет. Лично я считаю недостатком то, что меняется имя файлов байт-кода - это способствует беспорядку на диске. Обратите внимание, что «отсутствует встроенная информация отладки» нет в списке; pdb продолжает работать нормально (это было не так в более ранних выпусках Python, где -O отказался от поддержки пошагового перехода в pdb).
Martin v. Löwis 24 янв. 2011, в 07:43
Какую оптимизацию он действительно может предложить? Очевидно, что это зависит от кода, интерпретатора, среды, но, возможно, применимы некоторые общие комментарии?
max 27 янв. 2011, в 00:47
@max: в общем, я бы не ожидал каких-либо существенных изменений в скорости. Оптимизированный байт-код был изначально разработан для исключения неэффективной инструкции байт-кода SETLINENO, которая была необходима для пошагового выполнения. Тем не менее, пошаговое выполнение с тех пор получило более эффективный подход, поэтому -O утратил свою точку зрения.
Martin v. Löwis 27 янв. 2011, в 06:01
По крайней мере, в текущих версиях CPython __debug__ не просто меняется на False , любой код, if __debug__ полностью if __debug__ .
kindall 18 нояб. 2013, в 16:57
такое же поведение для Python 3?
Serge 13 фев. 2014, в 10:25
Насколько я знаю, то же самое для python 3, хотя я полагаю, что новая объектная модель позволяет еще больше встраивать __debug__ управления на основе __debug__ . Не __debug__ меня;) В любом случае, это должно быть довольно похоже. Некоторые вещи, которые cpy2 не может (или не может) оптимизировать: логические операции короткого замыкания, if not __debug__ , встроенные операторы if .
DylanYoung 14 дек. 2017, в 21:39
Показать ещё 6 комментариев
Ещё вопросы
- 0 Как показать / скрыть теги div на основе выбора радиокнопки в форме
- 0 В выпадающем меню есть повторяющаяся проблема при выпадении
- 1 Как сохранить цветовую шкалу d3 с разрывом и повторным соединением ссылок
- 0 Отправка электронной почты через PHP с телом в MYSQL db и анализ \ n
- 1 Помещение меток в массив с помощью цикла for в Visual Studio C #
- 1 Логический вывод типа в Java8
- 1 Регулярное выражение для захвата до определенного процента / десятичных знаков
- 1 RadioButton выравнивает текст по левой стороне и кнопку вправо
- 0 Почему мой угловой пейджинг не работает?
- 0 Необработанное исключение, не похоже, что что-то не так
- 1 Расположение флажка в checkboxtableviewer по горизонтали в eclipse e4
- 0 Разбить UL на несколько столбцов
- 0 MySQL выбрать с использованием даты и времени, сгруппировать только по дате
- 1 По какой причине конструкторы классов ES6 нельзя назвать обычными функциями?
- 0 Выполнение запроса MySQL с использованием Python MySQL?
- 1 Получить значение из web.config в файл XSLT
- 0 Создание приложения для создания заметок с использованием JQuery для мобильных телефонов и телефонов
- 0 Сбор продукта и фильтр Magento
- 1 Группировать по numpy.mean
- 1 Получение nsi: введите xml
- 1 Модуль веб-модуля RefrenceError не определен
- 0 SQL подготовленный оператор не показывает вывод
- 0 Значение поля ввода Javascript не определено
- 1 метрика f1_score в лайтбм
- 0 отладка __do_global_dtors_aux, чтобы найти расположение кода
- 1 Как опубликовать данные base64string в веб-просмотре с URL?
- 1 notify () против notifyAll ()?
- 0 Использование jQuery.load с RequireJS
- 0 отправка почты с использованием бесплатной функции codignator, доставляемой как спам
- 1 Цвет фона JFrame не изменится
- 1 разделение значения и отображение в отформатированном виде
- 1 Рекурсивное чтение древовидной структуры XML в списке
- 0 Как сделать этот PHP, если оператор более простым и коротким?
- 1 Как напечатать отчет Джаспер без предварительного просмотра в Java
- 0 как сделать пользовательскую директиву в угловых для расчета расстояния с помощью карт Google
- 0 Получить текст из строки столбца
- 0 Инициировать событие jQuery (bPopup) для выбранного значения раскрывающегося списка.
- 0 Активная запись реляционной базы данных Yii Framework 2.0
- 1 нахождение контуров корня растения
- 0 C ++ возможные комбинации монет с использованием цикла while
- 1 Python / Flask-WTF - Как я могу рандомизировать выбор из динамического RadioField?
- 1 Путаница между PIL.Image и PIL.Image.Image и правильный способ их использования?
- 0 Список динамических флажков в Angular и JSON API
- 1 Не удалось получить информацию о домене (1355). Не умеет читать значения в коллекции LINQ
- 0 При создании новой комнаты сделайте $ location.path ('/' + roomNumber); Как я могу создать новый маршрут, когда пользователь создает новую комнату?
- 0 PHP - извлекать числа и заключать их в
- 1 Javascript: Как получить значение переменной из конкретного файла из 2 разных файлов?
- 1 ScrollView внутри меню NavigationDrawer
- 0 Оставил соединение, мне нужно объяснение о коде
- 1 Java не может отправить письмо с HTML с изображениями
101 вопрос (с ответами) на которые должен ответить Python-разработчик
Высокоуровневый язык программирования общего назначения с динамической типизацией и автоматическим управлением памятью. Является полностью объектно-ориентированным, т.к. все элементы в нем объекты.
2. В каком году появился первый релиз Python?
В феврале 1991 г. Гвидо ван Россум опубликовал Python v.0.9.0
3. Какие типы данных есть в Python? На какие категории делятся?
Mutable (изменяемые)
Immutable (неизменяемые)
4. Что такое лямбда-функция? Какое у неё назначение?
Lambda function - анонимная однострочная функция. Без них вполне можно обойтись, но благодаря им код становится проще и лаконичнее.
5. Что такое PEP8?
PEP (Python Enhancement Proposal) - это документ составленный Гвидо ван Россумом, Барри Варшавой и Ником Когланом в 2001 году содержащий рекомендации по написанию кода на Python с целью улучшения читабельности и логичности кода.
6. Как получить документацию по атрибутам объекта?
print(dir(object)) for attr in dir(object): print(attr) print(attr.__doc__, '\n')
7. Что такое docstring?
def my_function(a, b): '''Explanation of function called docstring''' return a ** b print(my_function.__doc__) # get function description
8. В чём разница между типами list и tuple?
List - изменяемый (mutable) тип данных т.е. мы можем добавлять\удалять\модифицировать элементы внутри него.
Tuple - неизменяемый (immutable), операции удаления\добавления\модификации в нем недоступны. Исключение, если в Tuple содержится, например, List или Set, в таком случае мы можем модифицировать элементы внутри данных типов.
9. Может ли быть индекс списка отрицательным?
Сами значения индексов могут быть только положительными т.к. они автоинкрементируемые (увеличиваются на единицу от предыдущего).
Однако, мы можем обращаться к элементам списков по отрицательному индексу, это действие называет back indexing.
arr = [1, 2, 3] print(list(enumerate(arr))) # [(0, 1), (1, 2), (2, 3)] print([ for index, value in enumerate(arr)]) # [, , ] print(arr[0]) # 1 print(arr[-1]) # 3 print(arr[-2]) # 2
10. Что значит конструкция pass?
Проигнорировать участок кода, а именно scope в котором он указан.
11. Чем отличаются многопоточное и многопроцессорное приложение?
Многопоточные приложения (модуль threading).
Мультипроцессорные приложения (модуль multiprocessing).
- Потоки существуют внутри процесса
- В одном процессе может быть несколько потоков
- Потоки в одном процессе разделяют состояние и память родительского процесса
Критерий | Процессы (Process) | Потоки (Thread) |
---|---|---|
Распределение памяти | Память не распределяется между процессами | Память распределяется между потоками внутри процесса |
Используемый объем памяти | Много | Мало |
Привязка к процессору и устройствам ввода\вывода | Оптимизирован для задач, связанных с ЦП | Оптимизирован для задач, связанных с вводом-выводом |
Время запуска | Медленнее потока | Быстрее процесса |
Прерываемость | Дочерние процессы прерываемы | Потоки не прерываются |
12. Как просмотреть методы объекта?
print([method_name for method_name in dir(YOUR_OBJECT) if callable(getattr(YOUR_OBJECT, method_name))])
13. Что такое *args и **kwargs в определении функции?
*args - неограниченное кол-во необязательных аргументов рассматриваемое в теле функции в виде Tuple.
**kwargs - неограниченное кол-во необязательных именованных аргументов рассматриваемое в теле функции в виде Dictionary.
14. Python полностью поддерживает ООП?
Наследование, инкапсуляция и полиморфизм являются основными столпами ООП, Python реализует их в полной мере, соответственно, полностью поддерживает ООП.
15. Что такое globals() и locals()?
globals() - всегда возвращает словарь пространства имен модуля
locals() - всегда возвращает в словарь в текущем пространстве имен
vars() - возвращается либо в словаре текущего пространства имен (если вызывается без аргументов), или в словаре аргумента.
def my_function(a, b): result = a / b print(globals()) print(locals()) # print(vars()) # return result class MyClass: a = 1 b = 2 print(globals()) print(locals()) print(vars()) print(vars(MyClass)) my_function(5, 10)
16. Что хранится в атрибуте __dict__?
__dict__ содержит в себе все атрибуты объекта.
class MyClass: a = 1 b = 2 print(MyClass.__dict__) # , '__weakref__': , '__doc__': None>
17. Как проверить файл .py на синтаксические ошибки, не запуская его?
touch app.py
# print('hello') print('hello
python -m py_compile app.py
18. Зачем в Python используется ключевое слово self?
Параметр self является ссылкой на текущий экземпляр класса и используется для доступа к переменным, принадлежащим классу.
19. Что такое декоратор? Как написать собственный?
Декоратор - это функция которая неявным образом модифицирует поведение другой (декорируемой функции)
import time def execution_time(function): def wrapper(): start = time.time() function() print(f' execution time: seconds') return wrapper @execution_time def my_func1(): time.sleep(2.5) @execution_time def my_func2(): time.sleep(1.5) my_func1() my_func2()
20. Что может быть ключом в словаре?
Ключами в словарях могут быть только объекты, поддерживающие хэширование (строки, числа). Использовать float не рекомендуется т.к. они хранятся в памяти ввиде приближений.
Использовать в качестве ключей списки, словари и другие изменяемые (мутабельные) типы не получится.
21. В чём разница между пакетами и модулями?
Основное отличие в структуре.
Модуль - это просто файл с кодом, который можно импортировать.
Пакет - это папка с несколькими модулями и файлом __init__.py
22. Как перевести строку, содержащую двоичный код (1 и 0), в число?
print(''.format(50)) # целое число в двоичную строку print(f'') # целое число в двоичную строку a = '00110010' print(int(a, 2)) # двоичная строка в число
23. Для чего используется функция __init__?
Это т.н. конструктор. Отвечает за инициализацию экземпляров класса после их создания.
24. Что такое слайс (slice)?
Slice (срез) вернет объект, представляющий часть итерируемого объекта, которая будет соответствовать шаблону, указанному в аргументах среза.
25. Как проверить, что один кортеж содержит все элементы другого кортежа?
t2 = (4, 5, 6, 7, 8, 9, 1, 2, 3, 8, 15) print(set(t1).issubset(set(t2))) # True
26. Почему пустой список нельзя использовать как аргумент по-умолчанию?
def cart(product, items_in_cart=[]): items_in_cart.append(product) return items_in_cart product1 = cart('Product 1') print(product1) # ['Product 1'] product2 = cart('Product 2') print(product2) # ['Product 1', 'Product 2'] # верул два товара, хотя мы не добавляли 2-й, в это и заключается проблема
Причина такого поведения в том, что когда интерпретатор определяет функцию, он также создает аргумент по умолчанию. Затем он связывает этот аргумент и созданный объект (ставит ссылку на него в памяти). В примере, Python выделил пустой список и привязал его к аргументу items_in_cart.
Пустой список создается один раз и аргумент items_in_cart указывает на него в течение всего времени существования функции. Всякий раз, когда вызывается функция снова, без указания items_in_cart, то она будет использовать значение по умолчанию, которое было создано при определении функции.
. Чтобы избежать подобного поведения, аргументы по умолчанию должны быть неизменяемыми:
def cart(product, items_in_cart=None): if items_in_cart is None: items_in_cart = [] items_in_cart.append(product) return items_in_cart product1 = cart('Product 1') print(product1) # ['Product 1'] product2 = cart('Product 2') print(product2) # ['Product 2']
27. Что такое @classmethod, @staticmethod, @property?
@staticmethod - декоратор который объявляет метод внутри класса статическим. Грубо-говоря просто функция внутри класса, которая ничего не знает о классе или экземпляре класса.
@classmethod - в целом похож на @staticmethod с той разницей, что первым аргументом мы передаем cls со всеми его методами и атрибутами класса, но не его экземпляра.
@property - не вдаваясь в детали, декоратор property позволяет задать getter, setter, deleter, а с помощью аргумента doc можно задать docstring для метода.
28. Что такое синхронный код?
Выполняет процесс до тех пор пока он полностью не завершится, при этом блокирует выполнение нижестоящего кода.
29. Что такое асинхронный код? Приведите пример.
Асинхронный код не блокирует выполнение нижестоящего кода а создает т.н. сoroutines (сопрограммы) которые выполняются с прерыванием, при этом сохраняют состояние на котором произошло прерывание.
import asyncio import random async def task1(): print('Run task 1') await asyncio.sleep(random.randint(1, 3)) print('Complete task 1') async def task2(): print('Run task 2') await asyncio.sleep(random.randint(1, 3)) print('Complete task 2') async def task3(): print('Run task 3') await asyncio.sleep(random.randint(1, 3)) print('Complete task 3') async def main(): t1 = asyncio.create_task(task1()) t2 = asyncio.create_task(task2()) t3 = asyncio.create_task(task3()) await asyncio.gather(t1, t2, t3) asyncio.run(main())
30. Каким будет результат следующего выражения?
print(-30 % 10) # 0
31. Для чего нужна функция id()?
Возвращает уникальный идентификатор объекта который присваивается ему интерпретатором при создании.
32. Что такое итератор?
Итератор - это объект реализующий методы __iter__() и __next__() который можно "перебрать" (проитерировать) только один раз.
33. Что такое генератор? Чем отличается от итератора?
Генератор - это объект, который сразу при создании не вычисляет значения всех своих элементов а хранит в памяти только последний элемент.
Каждый генератор является итератором, но не наоборот.
Фактически генератор это более элегантный способ написания итераторов.
34. Для чего используется ключевое слово yield?
Для создания функций-генераторов.
35. Чем отличаются __iter__ и __next__?
Метод __iter__ возвращает объект итератора.
Метод __next__ возвращает следующий элемент из последовательности.
36. Что такое контекстный менеджер?
Оператор with использует магические методы __enter__ и __exit__, позволяет сохранять\восстанавливать глобальные состояния, блокировать\разблокировать различные ресурсы, открывать\закрывать файлы, подключаться\закрывать соединения с сервером, базой данных и т.п. в автоматическом режиме.
Используя with, мы задаем некий контекст действий выйдя за рамки которого действие завершается.
37. Как сделать Python-скрипт исполняемым в различных операционных системах?
38. Как сделать копию объекта? Как сделать глубокую копию объекта?
import copy arr = [1, 2, 3] new_arr = arr.copy() # просто копия print(arr is new_arr) # False new_arr_deep = copy.deepcopy(arr) # если объект поддерживает глубокое копирование print(arr is new_arr_deep) # False
39. Опишите принцип работы сборщика мусора в Python
Python управляет памятью автоматически. Для этих целей в языке присутствует garbage collector (сборщик мусора).
Сборщик мусора запускается периодически и подсчитывает ссылки на на объекты (reference counting), увеличивает или уменьшает их количество.
В случае если объекты ссылаются друг на друга и больше не используются сборщик мусора использует т.н. "обнаружение циклов" (cycle detector) и освобождает память, если они не используются
Кроме того, существует механизм generational garbage collection, которые разбивает объекты на "поколения". Также существует модуль gc, благодаря которому можно осуществить принудительную сборку мусора gc.collect() .
40. Как использовать глобальные переменные? Это хорошая идея?
var1 = 100 def my_function(): global var1 var1 = 500 my_function() print(var1) # 500
Если все делать внимательно, аккуратно и уместно, то ничего криминального в использовании глобальных переменных нет. Однако их использование считается плохой практикой и использовать их без крайней необходимости не рекомендуется т.к. это может привести к неожиданным последствиям, вызвать ошибки и доставить массу неприятностей. Поэтому лучше стараться избегать использования глобальных переменных если это возможно.
41. Для чего в классе используется атрибут __slots__?
__slots__ используется для оптимизации памяти и ускорения работы с объектами созданными на основе класса.
Python создает для каждого экземпляра класса словарь __dict__ который содержит все его атрибуты, в случае если в классе много атрибутов и вы создаете много объектов на основе класса это может привести к большому расходу памяти. __slots__ позволяет определить какие именно атрибуты класса должны быть созданы в объекте и записывает их не в словарь а в список, что позволяет ускорить работу и уменьшить расход памяти.
class User1: def __init__(self, name, email): self.name = name self.email = email class User2: __slots__ = ['name', 'email'] def __init__(self, name, email): self.name = name self.email = email u1 = User1('user1', 'user1@gmail.com') print(u1.__dict__) # u2 = User2('user2', 'user2@gmail.com') # print(u2.__dict__) # AttributeError: 'User2' object has no attribute '__dict__'. print(u2.__slots__) # ['name', 'email']
42. Какие пространства имен существуют в Python?
Пространство имен - это система которая гарантирует что все имена функций, переменных и объектов будут уникальны и могут использоваться без каких-либо конфликтов.
Некоторые пространства имен (на самом деле их больше): main, name, builtins, globals, locals.
43. Как реализуется управление памятью в Python?
Осуществляется автоматически с помощью сборщика мусора (garbage collector).
44. Что такое метаклассы и в каких случаях их следует использовать?
Метаклассы довольно сложная концепция для понимания на первый взгляд относящаяся к метапрограммированию.
Википедия дает следующее определение метаклассам:
Метакласс - в объектно-ориентированном программировании это класс, экземпляры которого в свою очередь являются классами.
Более простыми словами, метаклассы - это классы которые определяют свойства и поведение других классов, они используются для изменения способа которым Python создает и обрабатывает классы.
Ниже простой пример как это работает:
class MyMetaClass(type): def __new__(cls, name, bases, dct): dct['some_attribute'] = 'this attribure added from Metaclass' return super(MyMetaClass, cls).__new__(cls, name, bases, dct) class A(metaclass=MyMetaClass): pass class B(A): pass class C(B): pass print(A.some_attribute) # this attribute added from Metaclass print(B.some_attribute) # this attribute added from Metaclass print(C.some_attribute) # this attribute added from Metaclass
Стоит ли использовать метаклассы?
- Если вы знаете что делаете и нет более простых путей решения задачи, то да, стоит. В подавляющем большинстве случаев можно обойтись без них.
Метаклассы могут быть полезны если вы захотите разработать собственный фреймворк или создать класс на основе заранее неизвестных данных, когда он будет формироваться в хоте выполнения различных условий.
45. Зачем нужен pdb?
pdb - это библиотека для отладки кода.
46. Каким будет результат следующего выражения?
print([0, 1][10:]) # []
Это срез, который выбирает все элементы, начиная с 10-го индекса, а в списке всего два элемента, соответственно, вернется пустой список.
47. Как создать класс без слова class?
NewClass = type('NewClass', (), ) obj1 = NewClass() print(obj1.attr) # 100 print(obj1.method()) # 200
48. Как перезагрузить импортированный модуль?
from importlib import reload import my_module my_module.do_something() reload(my_module)
49. Напишите декоратор, который будет перехватывать ошибки и повторять функцию максимум N раз.
import functools def try_run_function(tries): def func_wrp(function): @functools.wraps(function) def wrp(*args, **kwargs): for i in range(1, tries+1): try: run_function = function(*args, **kwargs) return run_function except Exception as e: print(f'Try #', 'Error:', e) raise Exception(f' failed after runs') return wrp return func_wrp @try_run_function(5) def my_function(x, y): return x ** y print(my_function(2, 3)) # ok print(my_function('2', 3)) # rise an exception
50. Каким будет результат следующего выражения?
print(list(map(str, [[0], [1]]))) # ['[0]', '[1]'] список с двумя строками res = ' '.join(list(map(str, [[0], [1]]))) # print(res, type(res)) # строка '[0] [1]' print(len(res)) # 7 - количество символов включая пробел в строке print(len(' '.join(list(map(str, [[0], [1]]))))) # 7
51. Python - легкий язык. Согласны?
В плане синтаксиса - да, Python воспринимается на порядок легче других языков. В остальном нет, это полноценный и развитый язык который требует серьёзного изучения.
52. Какие проблемы есть в Python?
- Garbage collector не всегда работает эффективно что может привести к утечками памяти
- Документация к некоторым библиотекам и модулям оставляет желать лучшего
- Динамическая типизация - однозначное зло
- ООП слишком упрощен, мне больше нравится реализация в PHP и Java
- Python уступает по скорости работы языкам C-группы, в задачах машинному обучению и научных вычислениях
Большинство проблем "высосаны из пальца", в любых языках присутствуют проблемы, нет ничего идеального.
53. Когда будет выполнена ветка else в конструкции try…except…else?
Если код в блоке try успешно выполнится.
try: # try to execute something except: # in case of error else: # in case of success try block finally: # executes regardless success or fail
54. Поддерживает ли Python множественное наследование?
class A: def method1(self): return 'method1' class B: def method2(self): return 'method2' class C(A, B): pass obj = C() print(obj.method1(), obj.method2()) # method1 method2
55. Как dict и set реализованы внутри? Какова сложность получения элемента? Сколько памяти потребляет каждая структура?
Словари и множества реализованы в виде хэш-таблицы.
Хеш-таблица - это структура данных, в которой все элементы хранятся в виде пары ключ-значение, где:
ключ - уникальное число, которое используется для индексации значений, генерируется с помощью функции хеширования;
значение - данные, которые связаны с этим ключом.
O(1) - константная временная сложность (операции выполняются максимально быстро)
O(n) - линейная временная сложность (средняя скорость выполнения)
Сложность получения элемента в Set:
Проверить наличие элемента в множестве: O(1)
Отличие множества A от B: O (длина A)
Пересечение множеств A и B: O (минимальная длина A или B)
Объединение множеств A и B: O(N) , где N это длина (A) + длина (B).
Сложность получения элемента в Dictionary:
Получение элемента: O(1)
Установка элемента: O(1)
Удаление элемента: O(1)
Проход по словарю: O(n)
Потребляемый объем памяти:
import sys d1 = dict() s1 = set() print(sys.getsizeof(d1)) # 64 bytes print(sys.getsizeof(s1)) # 216 bytes
56. Что такое MRO? Как это работает?
MRO (Method Resolution Order) - определяет порядок наследования атрибутов и методов классов. MRO используется при множественном наследовании.
class A: def method1(self): print('A method1') class B: def method1(self): print('B method1') class C(B): def method1(self): print('C method1') class D(C): pass class E(A, D): pass d = D() d.method1() # C method1 e = E() e.method1() # A method1
57. Как аргументы передаются в функции: по значению или по ссылке?
По ссылке на объект.
def rename(name): name = 'NewName' return name name = 'Vasya' print(rename(name)) # NewName print(name) # Vasya
В случае с мутабельными типами данных, изменяемый объект будет модифицирован
def add_to_list(arr): arr.append('Vasya') my_list = [1, 2, 3] add_to_list(my_list) print(my_list) # [1, 2, 3, 'Vasya']
58. С помощью каких инструментов можно выполнить статический анализ кода?
Статический анализ кода - это анализ качества кода без его запуска.
Динамический анализ кода - это анализ качества кода с помощью его запуска\компиляции в реальном или виртуальном процессах.
Инструментов для статического анализа очень много, перечеслю основные:
- Pylint проверяет код на наличие ошибок, соответствие PEP8, вносит предложения по рефакторингу.
- Pyflakes конкурент Pylint
- Mypy крутой инструмент для проверки корректности статической типизации
- еще несколько полезных модулей: Bandit, Black, Flake8.
59. Что будет напечатано в результате выполнения следующего кода?
import sys arr_1 = [] arr_2 = arr_1 print(sys.getrefcount(arr_1)) # 3
sys.getrefcount возвращает счетчик количества ссылок на объект. Счетчик обычно больше на единицу чем фактических ссылок т.к. он считает за ссылку переданный в нее аргумент.
60. Что такое GIL? Почему GIL всё ещё существует?
Global Interpreter Lock (GIL) - это механизм блокировки потоков который гарантирует что в любой момент времени в состоянии выполнения может находиться только один поток.
Если вы разрабатываете однопоточные программы, возможно вам этот принцип не знаком.
GIL существует т.к. является важной частью интерпретатора Python.
61. Опишите процесс компиляции в Python.
Python интерпретируемый язык, он не требует компиляции, интерпретатор выполняет код напрямую.
При первом запуске интерпретатор компилирует содержимое .py в байт-код находящийся в __pycache__ с расширением .pyc, далее байт-код выполнятся через PVM (Python Virtual Machine).
62. Как тиражировать Python код?
Если я правильно понял вопрос, то речь идет о репликации (копировании) кода. Это можно сделать как угодно, от создания архива отправки по почте )), до использования Git, Docker, pyinstaller и т.д.
63. Что такое дескрипторы? Есть ли разница между дескриптором и декоратором?
Дескрипторы - это объекты Python, которые реализуют метод протокола дескрипторов, что дает вам возможность определять поведение объекта когда мы обращаемся к нему через его методы или атрибуты.
class AttributeDescriptor(): def __get__(self, obj, type=None) -> object: print('получили значение атрибута') return 'something. ' def __set__(self, obj, value) -> None: print("пропробовали задать значение атрибута") raise AttributeError("Не вариант") class MyClass(): attr1 = AttributeDescriptor() my_object = MyClass() a = my_object.attr1 print(a) # my_object.attr1 = 56 # AttributeError: Не вариант
Декораторы - это функция которая модифицирует другую функцию неявным образом.
Разница между дескрипторами и декораторами в том, что дескрипторы определяют поведение атрибутов объекта, а декораторы изменяют поведение функций. Хотя декораторы могут быть использованы для реализации дескрипторов:
class MyClass(): @property def attr1(self) -> object: print('получили значение атрибута') return 'something. ' @attr1.setter def attr1(self, value) -> None: print("попробовали задать значение атрибута") raise AttributeError("Не вариант") my_object = MyClass() a = my_object.attr1 print(a) # my_object.attr1 = 56 # AttributeError: Не вариант
64. Почему всякий раз, когда Python завершает работу, не освобождается вся память?
Из-за garbage collector т.к. на момент завершения работы кода не все объекты попадают в сборщик мусора.
65. Что будет напечатано в результате выполнения следующего кода?
class Variable: def __init__(self, name, value): self._name = name self._value = value @property def value(self): print(self._name, 'GET', self._value) return self._value @value.setter def value(self, value): print(self._name, 'SET', self._value) self._value = value var_1 = Variable('var_1', 'val_1') # вызывается getter value var_2 = Variable('var_2', 'val_2') var_1.value, var_2.value = var_2.value, var_1.value # вызывается setter value ''' Outputs: var_2 GET val_2 var_1 GET val_1 var_1 SET val_1 var_2 SET val_2 '''
66. Что такое интернирование строк? Почему это есть в Python?
String Interning - это оптимизация строковых данных, когда Python с целью экономии ресурсов записывает одинаковые строки в одну область памяти.
a = 'Vasya' b = 'Vasya' print(a is b) # True т.к. сохранены в одну область памяти c = 'Vasya learning Python' d = 'Vasya learning Python' print(c is d) # True в ранних релизах Python 3.x было False т.к. интернирование срабатывало на строках до 5-ти символов, сейчас значение увеличено e = 'Vasya learning Python and JavaScript' f = 'Vasya learning Python' f += ' and JavaScript' print(e is f) # False т.к. хоть строка в области 'f' равна по значению 'e', но она составлена из разных областей и интернирование не работает
67. Как упаковать бинарные зависимости?
Слово "бинарные" сбивает с толка. Проще-говоря как сохранить в пакете и запустить все библиотеки необходимые для работы проекта.
С помощью pip.
pip install flask flask-bcrypt flask-wtf email-validator touch requirements.txt pip freeze > requirements.txt
pip install -r requirements.txt
Как вариант можно рассмотреть Wheel.
68. Почему в Python нет оптимизации хвостовой рекурсии? Как это реализовать?
Из-за того что хвостовая рекурсия может привести к переполнению стека вызовов она не оптимизируется автоматически.
Относительно Tail recursion можно почитать в статье Guido van Rossum Tail Recursion Elimination и в Final Words on Tail Calls.
# Tail recursion def countdown_tr(n): if n < 0: return print('Counting down', n) countdown_tr(n - 1) countdown_tr(4) # Replace tail recursion with while loop def countdown_loop(n): while True: if n < 0: return n print('Counting down', n) n -= 1 countdown_loop(3)
69. Что такое wheels и eggs? В чём разница?
Wheels и Eggs - это форматы дистрибуции пакетов для pip.
Egg появился в 2004 г., Wheel в 2012 г.. Egg может содержать .pyc файлы, что является проблемой при инсталляции пакета на разных платформах. Wheel является стандартом на текущий момент и лишен всех недостатков Egg.
70. Как получить доступ к модулю, написанному на Python из C и наоборот?
Честно-говоря странная идея. Если погуглить, то оказывается что с Python возможно всё Embedding Python in Your C Programs или библиотеку C в Python:
from ctypes import cdll libm_so = cdll.LoadLibrary('libm.so') print(libm_so.sqrt(7.0)) # квадратный корень от 7.0
71. Как ускорить существующий код Python?
Универсальных рецептов нет, надо смотреть на сам код который требует улучшения. Первое что приходит на ум - произвести рефакторинг, использовать более быстрые алгоритмы, попробовать найти альтернативу вашим функциям среди встроенным в язык, попробовать обрабатывать меньше данных за единицу времени.
Попробовать Cython, PyPy, Threads, Multiprocessing, AsyncIO, NumPy. вариантов много.
Вернусь к тому с чего начал: xтобы ускорить код, надо смотреть не сколько на сам старый код, а на задачу.
72. Что такое __pycache__? Что такое файлы .pyc?
При запуски кода Python интерпретатор компилирует ваш код в байт-код и сохраняет в папке __pycache__, внутри этой папке появляются скомпилированные с помощью байт-кода файлы с расширением .pyc в которых содержится оптимизированная версия вашего кода.
Смысл всего этого процесса в том, что ваша программа будет запускаться немного быстрее. Однако вы должны понять, что не следует передавать папку __pycache__ с её содержимым другим пользователям, она должна быть включена в .gitignore .
73. Что такое виртуальное окружение?
Суперполезная концепция в Python позволяет создавать инкапсулированные (изолированные) среды для разработки различных проектов.
Предположим, вы работаете над несколькими проектами и каждый из них требует различного набора модулей\библиотек\фреймворков, устанавливать все их глобально через pip плохая идея т.к. это захламит ваш проект. Отличное решение Virtualenv или pyenv.
Создали изолированную виртуальную среду, активировали её, установили все пакеты и радуемся жизни.
virtualenv .venv source .venv/bin/activate # или .venv\Scripts\activate (.venv): pip install -r requirements.txt (.venv): python manage.py runserver
74. Python - это императивный или декларативный язык?
Python объектно-ориентированный язык, а ООП - это ничто иное как императивная парадигма. Соответственно Python является преимущественно императивным языком, хотя поддерживает и декларативную парадигму.
Справедливо назвать его мультипарадигменным языком.
76. Что такое менеджер пакетов? Какие менеджеры пакетов вы знаете?
Package Manager - это инструмент который отвечает за установку\удаление\обновление модулей\библиотек\фреймворков и их зависимостей.
pip - основной менеджер пакетов в Python, другие: easy_install, conda и т.д.
76. В чём преимущества массивов NumPy по сравнению с (вложенными) списками Python?
NumPy более эффективно использует память для работы с большими объемами данных, а также использует более быстрые алгоритмы. Проще-говоря NumPy работает быстрее.
77. Вам нужно реализовать функцию, которая должна использовать статическую переменную. Вы не можете писать код вне функции и у вас нет информации о внешних переменных (вне вашей функции). Как это сделать?
Речь идет о замыкании (closure):
def multiplier(x): def mult(y): return x * y return mult times5 = multiplier(5) times10 = multiplier(10) print(times5(10)) print(times5(5)) print(times10(10)) print(times10(5))
78. Что будет напечатано в результате выполнения следующего кода?
def f_g(): yield 43 return 66 print(f_g()) #
В результате получим объект генератор т.к. функция является генератором из-за присутствия слова yield. Если хотите получить результат функции, то:
print(next(f_g())) # 43 # или for item in f_g(): print(item) # 43 # return генераторы игнорируют
79. Как имплементировать словарь с нуля?
Словари Python реализованы в виде хеш-таблиц.
class CustomDictionary: def __init__(self): self.table_size = 10 self.keys = [None] * self.table_size self.values = [None] * self.table_size def __setitem__(self, key, value): get_index = hash(key) % self.table_size self.keys[get_index] = key self.values[get_index] = value def __getitem__(self, key): get_index = hash(key) % self.table_size return self.values[get_index] a = CustomDictionary() a['name'] = 'Vasya' a['age'] = 34 print(a['name']) # Vasya print(a['age']) # 34
80. Напишите однострочник, который будет подсчитывать количество заглавных букв в файле.
touch text.txt Lorem ipsum dolor sit amet consectetur adipisicing elit. Ipsam debitis incidunt deleniti ab accusantium ullam! Dignissimos cum nesciunt quae incidunt earum magni odit optio sunt velit est quasi, neque eligendi.
print(sum(1 for row in open('text.txt') for c in row if c.isupper()))
81. Что такое файлы .pth?
.pth - это файл в котором указываются пути к модулям которые вы хотите импортировать в ваше пространство имен.
Используются в случае если необходимые модули лежат где-то вне файлов проекта, например, на другом разделе жесткого диска.
Как воспользоваться данной фичей?
Для начала посмотрите где Python ищет модули:
import sys print(sys.path) # ['c:\\Users\\Denis\\Desktop\\sb', 'C:\\Python311\\python311.zip', 'C:\\Python311\\DLLs', 'C:\\Python311\\Lib', 'C:\\Python311', 'C:\\Python311\\Lib\\site-packages']
Далее создайте файл с любым именем, но с раcширением .pth в папке C:\Python311\Lib\site-packages или site-packages вашей virtualenv.
У меня получилось так:
touch new_path.pth # file content: D:\new_folder
Теперь еще раз запустим sys.path и увидим что D:\new_folder в списке:
print(sys.path) # ['c:\\Users\\Denis\\Desktop\\sb', 'C:\\Python311\\python311.zip', 'C:\\Python311\\DLLs', 'C:\\Python311\\Lib', 'C:\\Python311', 'C:\\Python311\\Lib\\site-packages', 'D:\\new_folder']
Мой вам совет: не морочьте себе и другим людям голову с .pth файлами, могут быть проблемы.
82. Какие функции из Сollections и Itertools вы используете?
На этот вопрос так и хочется ответить: те которые нужны, но видимо хотят проверить знакомы ли вы с этими модулями впринципе.
Модуль Collections предоставляет различные типы контейнеров
Хорошая статья про Collections и несколько примеров:
from collections import Counter, ChainMap # Counter может посчитать кол-во элементов в итерируемом объекте arr = [1, 1, 2, 3, 1, 2, 3, 4, 5, 4, 6] print(Counter(arr)) # Counter() arr2 = ['F', 'L', 'L', 'X', 'W', 'X', 'A', 'L'] print(Counter(arr2)) # Counter() # ChainMap позволяет интересно и удобно работать со словарями d1 = d2 = d3 = chain = ChainMap(d1, d2, d3) print(chain.keys()) # KeysView(ChainMap(, , )) print(chain.values()) # ValuesView(ChainMap(, , )) # вот в этом их преимущество: у нас три разных словаря, объединив их с помощью ChainMap, работаем с ними как с одним print(chain['a'], chain['c'], chain['f']) # 1 3 6
Модуль Itertools предоставляет методы для создания сложных итераторов и эффективной работы с ними.
Хорошая статья про Itertools и несколько примеров:
from itertools import count, repeat, chain, accumulate # count iter_even =(count(start=0, step=2)) print("Список четных:", list(next(iter_even) for _ in range(6))) # [0, 2, 4, 6, 8, 10] список из 6-ти четных чисел iter_odd = (count(start=1, step=2)) print("Список нечетных:", list(next(iter_odd) for _ in range(6))) # [1, 3, 5, 7, 9, 11] список из 6-ти нечетных чисел # repeat print(list(repeat(100, 5))) # [100, 100, 100, 100, 100] создаст итератор из 5-ти повторяющихся чисел # chain arr1 = [1, 2, 3] arr2 = [4, 5, 6] print(list(chain(arr1, arr2))) # [1, 2, 3, 4, 5, 6] объединит списки в один # accumulate arr3 = [1, 2, 3, 4, 5, 6] res = accumulate(arr3, lambda n1, n2: n1 * n2) print([n for n in res]) # [1, 2, 6, 24, 120, 720] умножит предыдущее число на следующее
83. Что делает флаг PYTHONOPTIMIZE?
PYTHONOPTIMIZE удаляет из кода операторы assert, игнорирует любой код находящийся внутри блока __debug__, удаляет docstrings.
''' Try to run file app.py with: touch app.py python app.py python -O app.py python -OO app.py ''' def my_function(): '''my_function description''' return 'my_function' print('prod') print(my_function.__doc__) assert 1 == 1 # ok # this will be ignored in -O and -OO if __debug__: print('debug') print(my_function.__doc__) assert 1 == 2 assert 1 == 2
84. Что будет напечатано в результате выполнения следующего кода?
arr = [[]] * 5 arr_1, arr_2 = arr, arr for k, arr in enumerate((arr_1, arr_2)): arr[0].append(k) arr = (arr_1, 5, arr_2) print(arr) # ([[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]], 5, [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]])
Теперь рассмотрим как это работает подробнее:
arr = [[]] * 5 print(arr) # [[], [], [], [], []] получаем пять пустых вложенных списков arr_1, arr_2 = arr, arr print(arr_1, arr_2) # [[], [], [], [], []] [[], [], [], [], []] делаем их алиасы в двух переменных print(list(enumerate((arr_1, arr_2)))) # [(0, [[], [], [], [], []]), (1, [[], [], [], [], []])] конвертируем их в кортежи со счетчиком for k, arr in enumerate((arr_1, arr_2)): print(arr) # [[0], [0], [0], [0], [0]] arr[0].append(k) # добавляем во вложенные списки порядковый номер 1 из arr_2 в enumerate print(arr) # [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]] добавляем к каждому из вложенных списков ключ arr = (arr_1, 5, arr_2) # создаем кортеж в котором первый элемент это arr_1, второй 5, а третий alias arr - arr_2 print(arr) # итог ([[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]], 5, [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]])
85. Какие переменные среды, влияющие на поведение интерпретатора Python, вы знаете?
Полный список environment variables в Python. Эти переменные среды влияют на поведение Python.
Пример:
Вы хотите собрать проект в Docker-контейнер и не хотите что бы внутри него создавались __pycache__ и .pyc, это легко можно сделать добавив в Dockerfile настройки:
FROM python:3.11-slim ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONUNBUFFERED 1
Опять-таки, применений им масса, поэтому адресую к началу ответа со ссылкой на список переменных среды.
86. Что такое Cython? Что такое IronPython? Что такое PyPy? Почему они до сих пор существуют и зачем?
Cython является надмножеством языка Python, который дополнительно поддерживает вызов функций C и объявление типов C для переменных и атрибутов класса. Cython позволяет генерировать код C из кода Python.
IronPython аналогично Cython, только для .NET
PyPy - это замена CPython. Он построен с использованием языка RPython. Основная причина использовать его вместо CPython - скорость.
Для чего они существуют:
- Скорость, чаще всего они работают быстрее
- Удобство работы как для Python-разработчиков, так и для .NET, C и в обратном порядке
87. Как перевернуть генератор?
gen = [n * 2 for n in list(range(6))] print([item for item in reversed(list(gen))]) # [10, 8, 6, 4, 2, 0]
88. Приведите пример использования filter и reduce над итерируемым объектом.
Довольно простой пример выбора нечетных чисел с помощью filter:
print(list(filter(lambda n: n % 2 != 0, range(1, 11)))) # [1, 3, 5, 7, 9]
Просуммируем все числа в списке:
from functools import reduce print(list(range(6))) # [0, 1, 2, 3, 4, 5] print(reduce(lambda a, b: a + b, range(6))) # 15 print(sum(range(6))) # 15 (аналог для проверки)
89. Что будет напечатано в результате выполнения кода?
print(_) # NameError: name '_' is not defined
_ - является переменной, она просто не определена.
90. Чем фреймворк отличается от библиотеки?
Попробую объяснить совсем просто.
Library - это просто набор функций\методов. Вызывайте любой необходимый где вам надо и все будет работать.
Framework - это тоже набор функций и классов НО в нем есть некоторые требования к архитектуре (расположения и именования файлов и папок). Фреймворки создаются для решения каких-то задач. Например: разработка backend, frontend, сбор данных и т.д.
91. Расположите функции в порядке эффективности, объясните выбор.
from time import time list1 = list(range(1000000)) start = time() def f1(arr): l1 = sorted(arr) # сортирует весь список, затратная операция l2 = [i for i in l1 if i < .5] return [i * i for i in l2] f1(list1) print(f'Execution time: sec') # ~ 0.06 sec start = time() def f2(arr): # самая быстрая из предложенных l1 = [i for i in arr if i < .5] l2 = sorted(l1) # не сортирет весь список return [i * i for i in l2] f2(list1) print(f'Execution time: sec') # ~ 0.05 sec start = time() def f3(arr): l1 = [i * i for i in arr] l2 = sorted(l1) # непонятно зачем это здесь return [i for i in l1 if i < (.5 * .5)] f3(list1) print(f'Execution time: sec') # # ~ 0.17 sec
92. Произошла утечка памяти в рабочем приложении. Как бы вы начали отладку?
Установил бы Memory Profiler и objgraph и протестировал код с их помощью.
93. В каких ситуациях возникает исключение NotImplementedError?
NotImplementedError возникает когда в классе-наследнике не был реализован метод обозначенный в родительском классе.
from abc import ABC, abstractmethod class ParentAbstractClass(ABC): @abstractmethod def this_methon_sould_be_implemented(self): raise NotImplementedError('this_methon_sould_be_implemented() is not implemented') class ChildOne(ParentAbstractClass): def this_methon_sould_be_implemented(self): return 'OK' class ChildTwo(ParentAbstractClass): def this_methon_sould_be_implemented(self): super().this_methon_sould_be_implemented() obj1 = ChildOne() print(obj1.this_methon_sould_be_implemented()) obj2 = ChildTwo() print(obj2.this_methon_sould_be_implemented()) # NotImplementedError: this_methon_sould_be_implemented() is not implemented
94. Что не так с этим кодом? Зачем это нужно?
if __debug__: assert False, ("error") # AssertionError: error
По-умолчанию __debug__ = True поэтому assert останавливает выполнение программы с ошибкой и комментарием "error". Это может быть нужно что бы вынудить пользователя запускать скрипт в режиме Optimized mode (с флагом -O), но об этом стоило бы сообщить в AssertionError.
if __debug__: assert False, ("please run this code in Optimized mode:\npython -O app.py")
95. Что такое магические методы (dunder)?
Dunder (double underscore) или магические методы, это специальные методы с двойными подчеркиваниями в начале и в конце названия метода. Они определяют поведение объектов на основе класса в различных контекстах, например:
class MyClass: def __init__(self, name): # так будет весть себя при инициализации self.name = name # будет требовать указать атрибут 'name' def __str__(self): return 'Так объект класса будет вести себя в качестве строки' def __add__(self, other): return f'Такое поведение наблюдается при использовании + ' def __len__(self): # Такое поведение будет если применим к объекту функцию len() return 228 def __getitem__(self, *args): # Так объект будет вести себя если обратимся к нему как к элементу списка return ['Vasya', 228, *args] obj = MyClass('Vasya') print(obj) # Так объект класса будет вести себя в качестве строки print(obj + 1) # Такое поведение наблюдается при использовании + 1 print(len(obj)) # 228 print(obj[0], obj[1]) # ['Vasya', 228, 0] ['Vasya', 228, 1]
96. Объясните, почему такое возможно?
_MangledGlobal__mangled = "^_^" class MangledGlobal: def test(self): return __mangled assert MangledGlobal().test() == "^_^" print(MangledGlobal().test()) # '^_^'
Это явление называется Name mangling (коверканье имен). Обратите внимание на имя класса и переменной, думаю станет понятно в чем дело. Вот хорошая статья на тему Name mangling in Python.
97. Что такое monkey patching? Приведите пример использования.
Monkey patching - это динамическая замена атрибутов во время выполнения.
Предположим у нас есть некий класс с неким методом который получает данные со стороннего сервера\API в случае если нам потребуется написать тест данного метода то желательно избежать ситуации попадания в зависимость от стороннего источника данных, поэтому нам будет удобно заменить этот метод "заглушкой", которая будет возвращать какие-то заранее подготовленные данные, эти действия и называются "monkey patching".
class MyClass: def method(self): print('MyClass.method() is called') def monkey_function(self): print('monkey_function() is called') obj = MyClass() obj.method() # MyClass.method() is called MyClass.method = monkey_function # replace method() with monkey_function() obj = MyClass() obj.method() # monkey_function() is called
98. Как работать с транзитивными зависимостями?
Здесь подвох в словосочетании "транзитивные зависимости", а на самом деле все просто - это модули и библиотеки которые ставятся вместе с необходимым вам пакетом\фреймворком.
Например: вы хотите установить Flask и пишете в терминале pip install flask и в результате получаете такую картину:
blinker click colorama Flask itsdangerous Jinja2 MarkupSafe pip setuptools Werkzeug wheel
Это и есть транзитивные зависимости т.е. это те пакеты и модули которые подтягиваются автоматически для работы какого-нибудь фреймворка, например.
Отсюда и ответ на вопрос как с ними работать, да очень просто с помощью pip .
99. Что будет напечатано в окне браузера?
print(__name__) print(__file__)
__main__ JsException(PythonError: Traceback (most recent call last): File "/lib/python3.10/site-packages/_pyodide/_base.py", line 429, in eval_code .run(globals, locals) File "/lib/python3.10/site-packages/_pyodide/_base.py", line 300, in run coroutine = eval(self.code, globals, locals) File "", line 1, in NameError: name '__file__' is not defined )
Можно попробовать что-то типа:
print(1 + 1) # 2 print('Hello, world!') # Hello, world!
PyScript позволяет запускать Python в HTML.
100. Какие новые функции добавлены в Python 3.10?
What's New In Python 3.10 отслеживать изменения по версия можно здесь.
101. Почему иногда python так долго запускается (в Windows)?
Не сказал бы что Python работает под Windows медленно.
Другие публикации из блога
Как запустить, перезапустить, остановить, узнать статус Nginx в Ubuntu?
Systemctl: start\restart\stop\status sudo systemctl restart nginx sudo systemctl start nginx sudo systemctl sto…
Что такое сериализация (serialization) в Django?
Сериализация – это механизм перевода моделей Django в другие форматы, обычно текстовые (XML, JSON. ). Более под…
Выводим Inline формы в админке Django в зависимости от группы в которой находится пользователь
Начиная с версии Django 3.* доступен метод get_inlines. Допустим мы хотим показать Inline форму если пользователь на…
Ошибка 403 CSRF Protection при входе в админку Django 4.*
При попытке входа в админку Django 4.* возникает 403-я ошибка CSRF Protection. Согласно списку изменений CSRF_TRUSTE…
Инициализация, финализация и потоки
В приложении, включающем Python, функцию Py_Initialize() необходимо вызывать перед использованием любых других функций Python/C API; за исключением нескольких функций и global configuration variables .
Следующие функции можно безопасно вызвать до инициализации Python:
- Configuration functions:
- PyImport_AppendInittab()
- PyImport_ExtendInittab()
- PyInitFrozenExtensions()
- PyMem_SetAllocator()
- PyMem_SetupDebugHooks()
- PyObject_SetArenaAllocator()
- Py_SetPath()
- Py_SetProgramName()
- Py_SetPythonHome()
- Py_SetStandardStreamEncoding()
- PySys_AddWarnOption()
- PySys_AddXOption()
- PySys_ResetWarnOptions()
- Py_IsInitialized()
- PyMem_GetAllocator()
- PyObject_GetArenaAllocator()
- Py_GetBuildInfo()
- Py_GetCompiler()
- Py_GetCopyright()
- Py_GetPlatform()
- Py_GetVersion()
- Py_DecodeLocale()
- PyMem_RawMalloc()
- PyMem_RawRealloc()
- PyMem_RawCalloc()
- PyMem_RawFree()
Глобальные переменные конфигурации
Python имеет переменные для глобальной конфигурации для управления различными функциями и опциями. По умолчанию эти флаги контролируются command line options .
Если флаг установлен опцией, значение флага равно количеству раз, когда опция была установлена. Например, -b устанавливает Py_BytesWarningFlag в 1, а -bb устанавливает Py_BytesWarningFlag в 2.
Выдает предупреждение при сравнении bytes или bytearray с str или bytes с int . Выдайте ошибку, если значение 2 больше или равно 2 .
Включить вывод отладки парсера (только для экспертов, в зависимости от компиляции options).
Задается опцией -d и переменной среды PYTHONDEBUG .
Если установлено значение, отличное от нуля, Python не будет пытаться записывать файлы .pyc при импорте исходных модулей.
Задается опцией -B и переменной среды PYTHONDONTWRITEBYTECODE .
Подавить сообщения об ошибках при вычислении пути поиска модуля в Py_GetPath() .
Флаг Private, используемый программами _freeze_module и frozenmain .
Установите значение 1 , если для переменной среды PYTHONHASHSEED задана непустая строка.
Если флаг не равен нулю, прочитайте переменную среды PYTHONHASHSEED , чтобы инициализировать секретное начальное значение хеша.
Игнорируйте все переменные среды PYTHON* , например PYTHONPATH и PYTHONHOME , которые могут быть установлены.
Устанавливается опциями П14153П и К25387К.
Если сценарий передается в качестве первого аргумента или используется опция -c , войдите в интерактивный режим после выполнения сценария или команды, даже если sys.stdin не выглядит как терминал.
Задается опцией -i и переменной среды PYTHONINSPECT .
Запустите Python в изолированном режиме. В изолированном режиме sys.path не содержит ни каталога сценария, ни каталога пакетов сайта пользователя.
Новое в версии 3.4.
int Py_LegacyWindowsFSEncodingFlag
Если флаг ненулевой, используйте кодировку mbcs с обработчиком ошибок replace вместо кодировки UTF-8 с обработчиком ошибок surrogatepass для filesystem encoding and error handler .
Установите значение 1 , если для переменной среды PYTHONLEGACYWINDOWSFSENCODING задана непустая строка.
Более подробную информацию см. в PEP 529 .
Если флаг ненулевой, используйте io.FileIO вместо WindowsConsoleIO для стандартных потоков sys .
Установите значение 1 , если для переменной среды PYTHONLEGACYWINDOWSSTDIO задана непустая строка.
Более подробную информацию см. в PEP 528 .
Отключите импорт модуля site и связанные с ним манипуляции с sys.path , зависящие от сайта. Также отключите эти манипуляции, если site будет явно импортирован позже (вызовите site.main() , если вы хотите, чтобы они были triggered).
Задается параметрами -s и -I , а также переменной среды PYTHONNOUSERSITE .
Задается опцией -O и переменной среды PYTHONOPTIMIZE .
Не отображайте сообщения об авторских правах и версии даже в интерактивном режиме.
Новое в версии 3.2.
int Py_UnbufferedStdioFlag
Принудительно отключить буферизацию потоков stdout и stderr.
Задается опцией -u и переменной среды PYTHONUNBUFFERED .
Выводите сообщение каждый раз при инициализации модуля, показывая место (имя файла или встроенный модуль), из которого он загружен. Если значение 2 больше или равно, выведите сообщение для каждого файла, который проверяется при поиске модуля. Также предоставляет информацию об очистке модуля при выходе.
Задается опцией -v и переменной среды PYTHONVERBOSE .
Инициализация и финализация интерпретатора
void Py_Initialize() Часть К14153К.
Инициализируйте интерпретатор Python. В приложении, встраивающем Python, ее следует вызывать перед использованием любых других функций Python/C API; за некоторыми исключениями см. Before Python Initialization .
Это инициализирует таблицу загруженных модулей ( sys.modules ) и создает базовые модули builtins , __main__ и sys . Он также инициализирует путь поиска модуля ( sys.path ). Он не устанавливает sys.argv ; используйте для этого PySys_SetArgvEx() . При втором вызове эта операция не выполняется (без вызова Py_FinalizeEx() first).). Возвращаемого значения нет; в случае сбоя инициализации это является фатальной ошибкой.
В Windows меняет режим консоли с O_TEXT на O_BINARY , что также повлияет на использование консоли non-Python с использованием среды выполнения C.
void Py_InitializeEx(int initsigs) Часть К14153К.
Эта функция работает как Py_Initialize() , если initsigs — 1 . Если initsigs равен 0 , он пропускает регистрацию инициализации обработчиков сигналов, что может быть полезно при встроенном Python.
int Py_IsInitialized() Часть К14153К.
Возвращайте true (не ноль), если интерпретатор Python был инициализирован, и false (ноль), если нет. После вызова Py_FinalizeEx() возвращается false до тех пор, пока Py_Initialize() не будет вызван снова.
int Py_FinalizeEx() Часть Stable ABI начиная с версии 3.6..
Отмените все инициализации, выполненные Py_Initialize() , и последующее использование функций Python/C API, а также уничтожьте все подинтерпретаторы (см. Py_NewInterpreter() ниже), которые были созданы и еще не уничтожены с момента последнего вызова Py_Initialize() . В идеале это освобождает всю память, выделенную интерпретатором Python. Это неактивная операция при втором вызове (без повторного вызова Py_Initialize() first).. Обычно возвращаемое значение — 0 . Если во время финализации возникли ошибки (очистка буферизованного data),, возвращается -1 ).
Эта функция предусмотрена по ряду причин. Встраиваемому приложению может потребоваться перезапустить Python без перезапуска самого приложения. Приложение, которое загрузило интерпретатор Python из динамически загружаемого library (или DLL)), может захотеть освободить всю память, выделенную Python, перед выгрузкой DLL.. Во время поиска утечек памяти в приложении разработчик может захотеть освободить всю память, выделенную Python, перед выгрузкой. выход из приложения.
Баги и предостережения: Уничтожение модулей и объектов в модулях производится в случайном порядке; это может привести к сбою деструкторов (методов __del__() ), когда они зависят от других объектов (даже функций) или модулей. Динамически загружаемые модули расширения, загруженные Python, не выгружаются. Небольшие объемы памяти, выделенные интерпретатором Python, могут быть не освобождены (при обнаружении утечки сообщите об этом it).). Память, связанная циклическими ссылками между объектами, не освобождается. Некоторая память, выделенная модулями расширения, может не освобождаться. Некоторые расширения могут не освобождаться. работают правильно, если их процедура инициализации вызывается более одного раза; это может произойти, если приложение вызывает Py_Initialize() и Py_FinalizeEx() более одного раза.
Вызывает auditing event cpython._PySys_ClearAuditHooks без аргументов.
Новое в версии 3.6.
void Py_Finalize() Часть К14153К.
Это обратно совместимая версия Py_FinalizeEx() , которая игнорирует возвращаемое значение.
Process-wide parameters
int Py_SetStandardStreamEncoding(const char *encoding, const char *errors)
Этот API сохранен для обратной совместимости: вместо него следует использовать настройки PyConfig.stdio_encoding и PyConfig.stdio_errors , см. Python Initialization Configuration .
Эту функцию следует вызывать до Py_Initialize() , если она вообще вызывается. Он определяет, какую кодировку и обработку ошибок использовать со стандартом IO, с теми же значениями, что и в str.encode() .
Он переопределяет значения PYTHONIOENCODING и позволяет встраивать код для управления кодировкой ввода-вывода, когда переменная среды не работает.
Ошибки кодирования and/or могут быть NULL , чтобы использовать значения по умолчанию PYTHONIOENCODING and/or (в зависимости от других settings).).
Обратите внимание, что sys.stderr всегда использует обработчик ошибок «обратная косая черта», независимо от этого (или любого другого) параметра.
Если вызывается Py_FinalizeEx() , эту функцию необходимо будет вызвать еще раз, чтобы повлиять на последующие вызовы Py_Initialize() .
В случае успеха возвращает 0 , ненулевое значение при вызове ошибки (e.g. после того, как интерпретатор уже был initialized)..
Новое в версии 3.4.
Устарело с версии 3.11..
void Py_SetProgramName(const wchar_t *name) Часть К14153К.
Этот API сохранен для обратной совместимости: вместо него следует использовать настройку PyConfig.program_name , см. Python Initialization Configuration .
Эту функцию следует вызывать до первого вызова Py_Initialize() , если она вообще вызывается. Он сообщает интерпретатору значение аргумента argv[0] функции main() программы (преобразованное в широкий characters).. Это используется Py_GetPath() и некоторыми другими функциями ниже, чтобы найти libraries времени выполнения Python относительно исполняемого файла интерпретатора. Значение по умолчанию: 'python' Аргумент должен указывать на строку широких символов с нулевым завершением в хранилище static, содержимое которой не изменится во время выполнения программы. Никакой код в интерпретаторе Python не изменит содержимое этого хранилища.
Используйте Py_DecodeLocale() для декодирования строки байтов и получения строки wchar_*.
Устарело с версии 3.11..
wchar *Py_GetProgramName() Часть К14153К.
Верните имя программы, установленное с помощью Py_SetProgramName() или значение по умолчанию. Возвращенная строка указывает на хранилище static; вызывающая сторона не должна изменять его значение.
Эту функцию не следует вызывать до Py_Initialize() , иначе она вернет NULL .
Изменено в версии 3.10:. Теперь она возвращает NULL , если вызывается до Py_Initialize() .
wchar_t *Py_GetPrefix() Часть К14153К.
Возвращает префикс для установленных файлов, независимых от платформы. Это получено с помощью ряда сложных правил из имени программы, заданного с помощью Py_SetProgramName() , и некоторых переменных среды; например, если имя программы — '/usr/local/bin/python' , префикс — '/usr/local' . Возвращенная строка указывает на хранилище static; вызывающая сторона не должна изменять его значение. Это соответствует переменной префикса в Makefile верхнего уровня и аргументу --prefix сценария настройки во время сборки. Значение доступно для кода Python как sys.prefix . Это полезно только в Unix. См. также следующую функцию.
Эту функцию не следует вызывать до Py_Initialize() , иначе она вернет NULL .
Изменено в версии 3.10:. Теперь она возвращает NULL , если вызывается до Py_Initialize() .
wchar_t *Py_GetExecPrefix() Часть К14153К.
Возвращает префикс exec для установленных файлов, зависящих от платформы. Это получается с помощью ряда сложных правил из имени программы, заданного с помощью Py_SetProgramName() , и некоторых переменных среды; например, если имя программы — '/usr/local/bin/python' , префикс exec — '/usr/local' . Возвращенная строка указывает на хранилище static; вызывающая сторона не должна изменять его значение. Это соответствует переменной exec_prefix в Makefile верхнего уровня и аргументу --exec-prefix сценария настройки во время сборки. Значение доступно для кода Python как sys.exec_prefix . Это полезно только в Unix.
Справочная информация: префикс exec-prefix отличается от префикса, когда файлы, зависящие от платформы (например, исполняемые файлы и общий libraries), устанавливаются в другое дерево каталогов. При типичной установке файлы, зависящие от платформы, могут быть установлены в поддереве /usr/local/plat , а независимые от платформы файлы могут быть установлены в /usr/local .
Вообще говоря, платформа представляет собой комбинацию семейств аппаратного и программного обеспечения, например, машины Sparc под управлением операционной системы Solaris 2.x считаются одной и той же платформой, но машины Intel под управлением Solaris 2.x — это другая платформа, а машины Intel под управлением Linux — это еще одна платформа. Различные основные версии одной и той же операционной системы обычно образуют разные платформы. Операционные системы Non-Unix — это отдельная история; стратегии установки в этих системах настолько различны, что префикс и префикс exec не имеют смысла и устанавливаются в пустую строку. Обратите внимание, что скомпилированные файлы байт-кода Python не зависят от платформы (но не являются независимыми от версии Python, в которой они были compiled!).).
Системные администраторы знают, как настроить программы монтирования или автоматического монтирования для совместного использования /usr/local между платформами, при этом /usr/local/plat будет отдельной файловой системой для каждой платформы.
Эту функцию не следует вызывать до Py_Initialize() , иначе она вернет NULL .
Изменено в версии 3.10:. Теперь она возвращает NULL , если вызывается до Py_Initialize() .
wchar_t *Py_GetProgramFullPath() Часть К14153К.
Вернуть полное имя исполняемого файла Python; это вычисляется как побочный эффект получения пути поиска модуля по умолчанию из имени программы (заданного Py_SetProgramName() above).). Возвращенная строка указывает на хранилище static; вызывающая сторона не должна изменять ее значение. Значение доступно для кода Python как sys.executable .
Эту функцию не следует вызывать до Py_Initialize() , иначе она вернет NULL .
Изменено в версии 3.10:. Теперь она возвращает NULL , если вызывается до Py_Initialize() .
wchar_t *Py_GetPath() Часть К14153К.
Вернуть путь поиска модуля по умолчанию; это вычисляется на основе имени программы (установленного выше Py_SetProgramName() ) и некоторых переменных среды. Возвращенная строка состоит из серии имен каталогов, разделенных символом-разделителем, зависящим от платформы. Символом-разделителем является ':' в Unix и macOS, ';' в Windows. Возвращенная строка указывает на хранилище static; вызывающая сторона не должна изменять его значение. Список sys.path инициализируется этим значением при запуске интерпретатора; его можно (и обычно это делается) изменить позже, чтобы изменить путь поиска для загрузки модулей.
Эту функцию не следует вызывать до Py_Initialize() , иначе она вернет NULL .
Изменено в версии 3.10:. Теперь она возвращает NULL , если вызывается до Py_Initialize() .
void Py_SetPath(const wchar_t*) Часть Stable ABI начиная с версии 3.7..
Этот API сохранен для обратной совместимости: вместо него следует использовать настройки PyConfig.module_search_paths и PyConfig.module_search_paths_set , см. Python Initialization Configuration .
Установите путь поиска модулей по умолчанию. Если эта функция вызывается до Py_Initialize() , то Py_GetPath() не будет пытаться вычислить путь поиска по умолчанию, а вместо этого будет использовать предоставленный. Это полезно, если Python встроен в приложение, которое полностью знает расположение всех модулей. Компоненты пути должны быть разделены символом-разделителем, зависящим от платформы: ':' в Unix и macOS, ';' в Windows.
Это также приводит к тому, что sys.executable будет установлен на полный путь программы (см. Py_GetProgramFullPath() ), а sys.prefix и sys.exec_prefix станут пустыми. Вызывающая сторона может изменить их, если потребуется, после вызова Py_Initialize() .
Используйте Py_DecodeLocale() для декодирования строки байтов и получения строки wchar_*.
Аргумент пути копируется внутри, поэтому вызывающая сторона может освободить его после завершения вызова.
Изменено в версии 3.8:. Теперь для sys.executable вместо имени программы используется полный путь к программе.
Устарело с версии 3.11..
const char *Py_GetVersion() Часть К14153К.
Верните версию этого интерпретатора Python. Это строка, которая выглядит примерно так
"3.0a5+ (py3k:63103M, May 12 2008, 00:53:55) \n[GCC 4.2.3]"
Первое слово (до первого пробела) — это текущая версия Python; первые символы — это основная и второстепенная версия, разделенные точкой. Возвращенная строка указывает на хранилище static; вызывающая сторона не должна изменять его значение. Значение доступно для кода Python как sys.version .
См. также константу Py_Version .
const char *Py_GetPlatform() Часть К14153К.
Возвращает идентификатор текущей платформы. В Unix оно формируется из «официального» названия операционной системы, преобразованного в нижний регистр, за которым следует основной номер версии; например, для Solaris 2.x,, который также известен как SunOS 5.x,, значение равно 'sunos5' . На macOS, это 'darwin' . В Windows это 'win' . Возвращенная строка указывает на хранилище static; вызывающая сторона не должна изменять его значение. Значение доступно для кода Python как sys.platform .
const char *Py_GetCopyright() Часть К14153К.
Возвратите официальную строку авторских прав для текущей версии Python, например
'Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam'
Возвращенная строка указывает на хранилище static; вызывающая сторона не должна изменять его значение. Значение доступно для кода Python как sys.copyright .
const char *Py_GetCompiler() Часть К14153К.
Возвращает указание компилятора, использованного для сборки текущей версии Python, в квадратных скобках, например:
"[GCC 2.7.2.2]"
Возвращенная строка указывает на хранилище static; вызывающая сторона не должна изменять его значение. Значение доступно для кода Python как часть переменной sys.version .
const char *Py_GetBuildInfo() Часть К14153К.
Возвращает информацию о порядковом номере, дате и времени сборки текущего экземпляра интерпретатора Python, например
"#67, Aug 1 1997, 22:34:28"
Возвращенная строка указывает на хранилище static; вызывающая сторона не должна изменять его значение. Значение доступно для кода Python как часть переменной sys.version .
void PySys_SetArgvEx(int argc, wchar_t **argv, int updatepath) Часть К14153К.
Этот API сохранен для обратной совместимости: вместо него следует использовать настройки PyConfig.argv , PyConfig.parse_argv и PyConfig.safe_path , см. Python Initialization Configuration .
Установите sys.argv на основе argc и argv. Эти параметры аналогичны параметрам, передаваемым в функцию программы main() , с той разницей, что первая запись должна относиться к файлу сценария, который должен быть выполнен, а не к исполняемому файлу, в котором размещен интерпретатор Python. Если скрипта, который будет выполняться, нет, первая запись в argv может быть пустой строкой. Если этой функции не удается инициализировать sys.argv , с помощью Py_FatalError() сигнализируется о фатальном состоянии.
Если updatepath равен нулю, это все, что делает функция. Если updatepath не равен нулю, функция также изменяет sys.path согласно следующему алгоритму:
- Если имя существующего сценария передается в argv[0] , абсолютный путь к каталогу, в котором находится сценарий, добавляется к sys.path .
- В противном случае (то есть, если argc имеет значение 0 или argv[0] не указывает на существующий файл name),, перед sys.path добавляется пустая строка, что аналогично добавлению текущего рабочего каталога ( "." ).
Используйте Py_DecodeLocale() для декодирования строки байтов и получения строки wchar_*.
Приложениям, встраивающим интерпретатор Python для целей, отличных от выполнения одного сценария, рекомендуется передавать 0 в качестве пути обновления и при необходимости самостоятельно обновлять sys.path . См. CVE-2008-5983 .
В версиях до 3.1.3, вы можете добиться того же эффекта, вручную извлекая первый элемент sys.path после вызова PySys_SetArgv() , например, используя:
PyRun_SimpleString("import sys; sys.path.pop(0)\n");
Новое в версии 3.1.3.
Устарело с версии 3.11..
void PySys_SetArgv(int argc, wchar_t **argv) Часть К14153К.
Этот API сохранен для обратной совместимости: вместо него следует использовать настройки PyConfig.argv и PyConfig.parse_argv , см. Python Initialization Configuration .
Эта функция работает как PySys_SetArgvEx() с параметром updatepath, установленным на 1 , если только интерпретатор python не был запущен с -I .
Используйте Py_DecodeLocale() для декодирования строки байтов и получения строки wchar_*.
Изменено в версии 3.4:. Значение updatepath зависит от -I .
Устарело с версии 3.11..
void Py_SetPythonHome(const wchar_t *home) Часть К14153К.
Этот API сохранен для обратной совместимости: вместо него следует использовать настройку PyConfig.home , см. Python Initialization Configuration .
Установите «домашний» каталог по умолчанию, то есть расположение стандартного Python libraries. См. PYTHONHOME о значении строки аргумента.
Аргумент должен указывать на строку символов с нулевым завершением в хранилище static, содержимое которой не изменится во время выполнения программы. Никакой код в интерпретаторе Python не изменит содержимое этого хранилища.
Используйте Py_DecodeLocale() для декодирования строки байтов и получения строки wchar_*.
Устарело с версии 3.11..
w_char *Py_GetPythonHome() Часть К14153К.
Верните «домой» по умолчанию, то есть значение, установленное предыдущим вызовом Py_SetPythonHome() , или значение переменной среды PYTHONHOME , если она установлена.
Эту функцию не следует вызывать до Py_Initialize() , иначе она вернет NULL .
Изменено в версии 3.10:. Теперь она возвращает NULL , если вызывается до Py_Initialize() .
Состояние потока и глобальная блокировка интерпретатора
Интерпретатор Python не является полностью потокобезопасным. Для поддержки многопоточных программ Python существует глобальная блокировка, называемая global interpreter lock или GIL , которая должна удерживаться текущим потоком, прежде чем он сможет безопасно получить доступ к объектам Python. Без блокировки даже самые простые операции могут вызвать проблемы в многопоточной программе: например, когда два потока одновременно увеличивают счетчик ссылок одного и того же объекта, счетчик ссылок может увеличиться только один раз, а не дважды.
Таким образом, существует правило, согласно которому только поток, получивший GIL , может работать с объектами Python или вызывать функции Python/C API. Чтобы эмулировать параллельное выполнение, интерпретатор регулярно пытается переключить потоки (см. sys.setswitchinterval() ). Блокировка также снимается вокруг потенциальной блокировки операций I/O, таких как чтение или запись файла, чтобы в это время могли выполняться другие потоки Python.
Интерпретатор Python хранит некоторую учетную информацию, специфичную для потока, внутри структуры данных под названием PyThreadState . Также есть одна глобальная переменная, указывающая на текущий PyThreadState : ее можно получить с помощью PyThreadState_Get() .
Освобождение GIL от дополнительного кода
Большая часть кода расширения, управляющего GIL , имеет следующую простую структуру:
Save the thread state in a local variable. Release the global interpreter lock. . Do some blocking I/O operation . Reacquire the global interpreter lock. Restore the thread state from the local variable.
Это настолько распространено, что существует пара макросов для упрощения:
Py_BEGIN_ALLOW_THREADS . Do some blocking I/O operation . Py_END_ALLOW_THREADS
Макрос Py_BEGIN_ALLOW_THREADS открывает новый блок и объявляет скрытую локальную переменную; макрос Py_END_ALLOW_THREADS закрывает блок.
Блок выше расширяется до следующего кода:
PyThreadState *_save; _save = PyEval_SaveThread(); . Do some blocking I/O operation . PyEval_RestoreThread(_save);
Вот как работают эти функции: глобальная блокировка интерпретатора используется для защиты указателя на текущее состояние потока. При снятии блокировки и сохранении состояния потока указатель текущего состояния потока должен быть получен до того, как блокировка будет снята (поскольку другой поток может немедленно получить блокировку и сохранить свое собственное состояние потока в глобальном variable).. И наоборот, при получении блокировки и восстановлении блокировки состояния потока, блокировка должна быть получена перед сохранением указателя состояния потока.
Вызов системных функций I/O является наиболее распространенным вариантом использования для выпуска GIL,, но он также может быть полезен перед вызовом длительных вычислений, которым не требуется доступ к объектам Python, например, функции сжатия или шифрования, работающие с буферами памяти. Например, стандартные модули zlib и hashlib освобождают GIL при сжатии или хешировании данных.
Non-Python создал темы
Когда потоки создаются с использованием выделенного Python APIs (например, threading module),, состояние потока автоматически связывается с ними, и поэтому код, показанный выше, является правильным. Однако, когда потоки создаются из C (например, library стороннего производителя с свой собственный поток management), они не содержат GIL, и для них не существует структуры состояния потока.
Если вам нужно вызвать код Python из этих потоков (часто это будет частью обратного вызова API, предоставляемого вышеупомянутым сторонним library),, вы должны сначала зарегистрировать эти потоки в интерпретаторе, создав структуру данных состояния потока, затем получив GIL, и наконец, сохраните указатель состояния потока, прежде чем вы сможете начать использовать Python/C API. Когда вы закончите, вам следует сбросить указатель состояния потока, освободить GIL, и, наконец, освободить структуру данных состояния потока.
Функции PyGILState_Ensure() и PyGILState_Release() выполняют все вышеперечисленное автоматически. Типичная идиома для вызова Python из потока C:
PyGILState_STATE gstate; gstate = PyGILState_Ensure(); /* Здесь выполняем действия Python. */ result = CallSomeFunction(); /* оцениваем результат или обрабатываем исключение */ /* Освободить поток. За пределами этой точки разрешено использование Python API. */ PyGILState_Release(gstate);
Обратите внимание, что функции PyGILState_* предполагают, что существует только один глобальный интерпретатор (созданный автоматически Py_Initialize() ). Python поддерживает создание дополнительных интерпретаторов (с использованием Py_NewInterpreter() ), но объединение нескольких интерпретаторов и PyGILState_* API не поддерживается.
Предостережения относительно fork()
Еще одна важная вещь, которую следует отметить в отношении потоков, — это их поведение при вызове C fork() . В большинстве систем с fork() после разветвления процесса будет существовать только поток, выдавший разветвление. Это оказывает конкретное влияние как на то, как должны обрабатываться блокировки, так и на все сохраненные состояния во время выполнения CPython’s.
Тот факт, что остается только «текущий» поток, означает, что любые блокировки, удерживаемые другими потоками, никогда не будут сняты. Python решает эту проблему для os.fork() , получая блокировки, которые он использует внутри, перед разветвлением и освобождая их после этого. Кроме того, он сбрасывает любой Lock Objects в дочернем. При расширении или внедрении Python нет возможности сообщить Python о дополнительных блокировках (non-Python), которые необходимо получить до или сбросить после разветвления. Для достижения той же цели необходимо будет использовать такие средства ОС, как pthread_atfork() . Кроме того, при расширении или внедрении Python вызов fork() напрямую, а не через os.fork() (и возврат или вызов Python) может привести к взаимоблокировке из-за того, что одна из внутренних блокировок Python удерживается потоком, который прекратил свое существование после разветвления. PyOS_AfterFork_Child() пытается сбросить необходимые блокировки, но это не всегда удается.
Тот факт, что все остальные потоки go удалены, также означает, что состояние времени выполнения CPython’s должно быть должным образом очищено, что и делает os.fork() . Это означает финализацию всех остальных объектов PyThreadState , принадлежащих текущему интерпретатору, и всех остальных объектов PyInterpreterState . Из-за этого, а также из-за особого характера “main” interpreter , fork() следует вызывать только в «основном» потоке интерпретатора, где изначально была инициализирована глобальная среда выполнения CPython. Единственное исключение — если сразу после этого будет вызван exec() .
High-level API
Это наиболее часто используемые типы и функции при написании кода расширения C или при внедрении интерпретатора Python:
type PyInterpreterState Часть Limited API (как непрозрачный struct).
Эта структура данных представляет состояние, разделяемое несколькими взаимодействующими потоками. Потоки, принадлежащие одному и тому же интерпретатору, совместно используют административные модули и некоторые другие внутренние элементы. В этой структуре нет членов public.
Потоки, принадлежащие разным интерпретаторам, изначально не имеют ничего общего, кроме состояния процесса, такого как доступная память, дескрипторы открытых файлов и т. д. Глобальная блокировка интерпретатора также используется всеми потоками, независимо от того, какому интерпретатору они принадлежат.
type PyThreadState Часть Limited API (как непрозрачный struct).
Эта структура данных представляет состояние одного потока. Единственным элементом данных public является interp ( PyInterpreterState *),, который указывает на состояние интерпретатора этого потока.
void PyEval_InitThreads() Часть К14153К.
Устаревшая функция, которая ничего не делает.
В Python 3.6 и более ранних версиях эта функция создавала GIL, если он не существовал.
Изменено в версии 3.9: Функция теперь ничего не делает.
Изменено в версии 3.7:. Эта функция теперь вызывается Py_Initialize() , поэтому вам больше не придется вызывать ее самостоятельно.
Изменено в версии 3.2:. Эту функцию больше нельзя вызывать до Py_Initialize() .
Устарело, начиная с версии 3.9..
int PyEval_ThreadsInitialized() Часть К14153К.
Возвращает ненулевое значение, если был вызван PyEval_InitThreads() . Эту функцию можно вызвать без удержания GIL, и, следовательно, ее можно использовать, чтобы избежать вызовов блокировки API при однопоточной работе.
Изменено в версии 3.7:. GIL теперь инициализируется Py_Initialize() .
Устарело, начиная с версии 3.9..
PyThreadState *PyEval_SaveThread() Часть К14153К.
Снимите глобальную блокировку интерпретатора (если она была создана) и сбросьте состояние потока на NULL , вернув предыдущее состояние потока (которое не является NULL ). Если блокировка была создана, текущий поток должен ее получить.
void PyEval_RestoreThread(PyThreadState *tstate) Часть К14153К.
Получите глобальную блокировку интерпретатора (если она была создана) и установите состояние потока в tstate, которое не должно быть NULL . Если блокировка была создана, текущий поток не должен ее захватить, иначе возникнет взаимоблокировка.
Вызов этой функции из потока во время завершения среды выполнения завершит поток, даже если поток не был создан Python. Вы можете использовать _Py_IsFinalizing() или sys.is_finalizing() , чтобы проверить, находится ли интерпретатор в процессе завершения, прежде чем вызывать эту функцию, чтобы избежать нежелательного завершения.
PyThreadState *PyThreadState_Get() Часть К14153К.
Вернуть текущее состояние потока. Глобальная блокировка интерпретатора должна быть удержана. Когда текущее состояние потока — NULL , это приводит к фатальной ошибке (поэтому вызывающей стороне не нужно проверять наличие NULL ).
PyThreadState *PyThreadState_Swap(PyThreadState *tstate) Часть К14153К.
Поменяйте местами текущее состояние потока на состояние потока, заданное аргументом tstate, которое может быть NULL . Глобальная блокировка интерпретатора должна удерживаться и не сниматься.
Следующие функции используют локальное хранилище потока и несовместимы с подинтерпретаторами:
PyGILState_STATE PyGILState_Ensure() Часть К14153К.
Убедитесь, что текущий поток готов вызвать Python C API независимо от текущего состояния Python или глобальной блокировки интерпретатора. Поток может вызывать его столько раз, сколько пожелает, при условии, что каждый вызов соответствует вызову PyGILState_Release() . В общем, между вызовами PyGILState_Ensure() и PyGILState_Release() можно использовать другой код APIs, связанный с потоком, при условии, что состояние потока восстанавливается до предыдущего состояния до Release().. Например, нормальное использование макросов Py_BEGIN_ALLOW_THREADS и Py_END_ALLOW_THREADS является приемлемым.
Возвращаемое значение представляет собой непрозрачный «дескриптор» состояния потока при вызове PyGILState_Ensure() и должно быть передано в PyGILState_Release() , чтобы гарантировать, что Python останется в том же состоянии. Несмотря на то, что рекурсивные вызовы разрешены, эти дескрипторы не могут быть общими — каждый уникальный вызов PyGILState_Ensure() должен сохранять дескриптор для вызова PyGILState_Release() .
Когда функция вернется, текущий поток будет содержать GIL и сможет вызывать произвольный код Python. Неудача – это фатальная ошибка.
Вызов этой функции из потока во время завершения среды выполнения завершит поток, даже если поток не был создан Python. Вы можете использовать _Py_IsFinalizing() или sys.is_finalizing() , чтобы проверить, находится ли интерпретатор в процессе завершения, прежде чем вызывать эту функцию, чтобы избежать нежелательного завершения.
void PyGILState_Release(PyGILState_STATE) Часть К14153К.
Освободите все ранее приобретенные ресурсы. После этого вызова состояние Python будет таким же, как и до соответствующего вызова PyGILState_Ensure() (но обычно это состояние будет неизвестно вызывающему абоненту, поэтому используется GILState API).).
Каждый вызов PyGILState_Ensure() должен сопровождаться вызовом PyGILState_Release() в том же потоке.
PyThreadState *PyGILState_GetThisThreadState() Часть К14153К.
Получите текущее состояние потока для этого потока. Может вернуть NULL , если в текущем потоке не использовался GILState API. Обратите внимание, что основной поток всегда имеет такое состояние потока, даже если в основном потоке не было выполнено никакого автоматического вызова состояния потока. В основном это функция helper/diagnostic.
Верните 1 , если текущий поток удерживает GIL, и 0 в противном случае. Эту функцию можно вызвать из любого потока в любое время. Только если состояние потока Python было инициализировано и в настоящее время удерживается GIL, он вернет 1 . В основном это функция helper/diagnostic. Это может быть полезно, например, в контекстах обратного вызова или функциях распределения памяти, когда знание того, что GIL заблокирован, может позволить вызывающей стороне выполнять конфиденциальные действия или иным образом вести себя по-другому.
Новое в версии 3.4.
Следующие макросы обычно используются без точки с запятой в конце; посмотрите пример использования в исходном дистрибутиве Python.
Py_BEGIN_ALLOW_THREADS Часть К14153К.
Py_END_ALLOW_THREADS Часть К14153К.
Этот макрос расширяется до PyEval_RestoreThread(_save); > . Обратите внимание, что он содержит закрывающую скобку; он должен соответствовать более раннему макросу Py_BEGIN_ALLOW_THREADS . См. выше дальнейшее обсуждение этого макроса.
Py_BLOCK_THREADS Часть К14153К.
Этот макрос расширяется до PyEval_RestoreThread(_save); : он эквивалентен Py_END_ALLOW_THREADS без закрывающей скобки.
Py_UNBLOCK_THREADS Часть К14153К.
Этот макрос расширяется до _save = PyEval_SaveThread(); : он эквивалентен Py_BEGIN_ALLOW_THREADS без открывающей скобки и объявления переменной.
Low-level API
Все следующие функции должны вызываться после Py_Initialize() .
Изменено в версии 3.7:. Py_Initialize() теперь инициализирует GIL .
PyInterpreterState *PyInterpreterState_New() Часть К14153К.
Создайте новый объект состояния интерпретатора. Глобальную блокировку интерпретатора удерживать не обязательно, но ее можно удерживать, если необходимо сериализовать вызовы этой функции.
Вызывает auditing event cpython.PyInterpreterState_New без аргументов.
void PyInterpreterState_Clear(PyInterpreterState *interp) Часть К14153К.
Сброс всей информации в объекте состояния интерпретатора. Глобальная блокировка интерпретатора должна быть удержана.
Вызывает auditing event cpython.PyInterpreterState_Clear без аргументов.
void PyInterpreterState_Delete(PyInterpreterState *interp) Часть К14153К.
Уничтожить объект состояния интерпретатора. Глобальную блокировку интерпретатора удерживать не требуется. Состояние интерпретатора должно быть сброшено предыдущим вызовом PyInterpreterState_Clear() .
PyThreadState *PyThreadState_New(PyInterpreterState *interp) Часть К14153К.
Создайте новый объект состояния потока, принадлежащий данному объекту интерпретатора. Глобальную блокировку интерпретатора удерживать не обязательно, но ее можно удерживать, если необходимо сериализовать вызовы этой функции.
void PyThreadState_Clear(PyThreadState *tstate) Часть К14153К.
Сброс всей информации в объекте состояния потока. Глобальная блокировка интерпретатора должна быть удержана.
Изменено в версии 3.9:. Эта функция теперь вызывает обратный вызов PyThreadState.on_delete . Ранее это произошло в PyThreadState_Delete() .
void PyThreadState_Delete(PyThreadState *tstate) Часть К14153К.
Уничтожить объект состояния потока. Глобальную блокировку интерпретатора удерживать не требуется. Состояние потока должно быть сброшено предыдущим вызовом PyThreadState_Clear() .
Уничтожьте текущее состояние потока и снимите глобальную блокировку интерпретатора. Как и PyThreadState_Delete() , глобальную блокировку интерпретатора удерживать не требуется. Состояние потока должно быть сброшено предыдущим вызовом PyThreadState_Clear() .
PyFrameObject *PyThreadState_GetFrame(PyThreadState *tstate) Часть Stable ABI начиная с версии 3.10..
Получите текущий кадр состояния потока Python tstate.
Верните strong reference . Верните NULL , если в данный момент не выполняется ни один кадр.
tstate не должно быть NULL .
Новое в версии 3.9.
uint64_t PyThreadState_GetID(PyThreadState *tstate) Часть Stable ABI начиная с версии 3.10..
Получите уникальный идентификатор состояния потока Python tstate.
tstate не должно быть NULL .
Новое в версии 3.9.
PyInterpreterState *PyThreadState_GetInterpreter(PyThreadState *tstate) Часть Stable ABI начиная с версии 3.10..
Получите интерпретатор состояния потока Python tstate.
tstate не должно быть NULL .
Новое в версии 3.9.
void PyThreadState_EnterTracing(PyThreadState *tstate)
Приостановить трассировку и профилирование в состоянии потока Python tstate.
Возобновите их с помощью функции К14153К.
Новое в версии 3.11.
void PyThreadState_LeaveTracing(PyThreadState *tstate)
Возобновите трассировку и профилирование в состоянии потока Python, которое было приостановлено функцией PyThreadState_EnterTracing() .
Новое в версии 3.11.
PyInterpreterState *PyInterpreterState_Get(void) Часть Stable ABI начиная с версии 3.9..
Получите текущий интерпретатор.
Выдает фатальную ошибку, если нет текущего состояния потока Python или текущего интерпретатора. Он не может вернуть NULL..
Вызывающий абонент должен удерживать GIL..
Новое в версии 3.9.
int64_t PyInterpreterState_GetID(PyInterpreterState *interp) Часть Stable ABI начиная с версии 3.7..
Возвращает уникальный код интерпретатора ID.. Если при этом произошла какая-либо ошибка, возвращается -1 и устанавливается ошибка.
Вызывающий абонент должен удерживать GIL..
Новое в версии 3.7.
PyObject *PyInterpreterState_GetDict(PyInterpreterState *interp) Часть Stable ABI начиная с версии 3.8..
Возвращает словарь, в котором могут храниться данные, специфичные для интерпретатора. Если эта функция возвращает NULL , то исключение не было создано, и вызывающая сторона должна предполагать, что словарь, специфичный для интерпретатора, недоступен.
Это не замена PyModule_GetState() , расширения которого следует использовать для хранения информации о состоянии, специфичной для интерпретатора.
Новое в версии 3.8.
typedef PyObject *(*_PyFrameEvalFunction)(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag)
Тип функции оценки кадра.
Параметр throwflag используется методом генераторов throw() : если он не равен нулю, обрабатывается текущее исключение.
Изменено в версии 3.9:. Функция теперь принимает параметр tstate.
Изменено в версии 3.11: Параметр кадра изменен с PyFrameObject* на _PyInterpreterFrame* .
_PyFrameEvalFunction _PyInterpreterState_GetEvalFrameFunc(PyInterpreterState *interp)
Получите функцию оценки кадра.
См. PEP 523 “Adding, оценка кадра от API до CPython”..
Новое в версии 3.9.
void _PyInterpreterState_SetEvalFrameFunc(PyInterpreterState *interp, _PyFrameEvalFunction eval_frame)
Установите функцию оценки кадра.
См. PEP 523 “Adding, оценка кадра от API до CPython”..
Новое в версии 3.9.
PyObject *PyThreadState_GetDict() Возвращаемое значение: Заимствованная ссылка. Часть К14153К.
Возвращает словарь, в котором расширения могут хранить информацию о состоянии конкретного потока. Каждое расширение должно использовать уникальный ключ для хранения состояния в словаре. Эту функцию можно вызывать, когда текущее состояние потока недоступно. Если эта функция возвращает NULL , исключение не возникло, и вызывающая сторона должна предположить, что текущее состояние потока недоступно.
int PyThreadState_SetAsyncExc(unsigned long id, PyObject *exc) Часть К14153К.
Асинхронно вызвать исключение в потоке. Аргумент id — это идентификатор целевого потока; exc — объект исключения, который необходимо вызвать. Эта функция не крадет ссылки на exc. Чтобы предотвратить наивное неправильное использование, вы должны написать собственное расширение C для вызова этого. Необходимо вызывать, удерживая GIL. Возвращает количество измененных состояний потока; Обычно это единица, но будет равна нулю, если идентификатор потока не найден. Если exc — NULL , ожидающее исключение (если оно есть) для потока очищается. Это не вызывает исключений.
Изменено в версии 3.7: Тип параметра id изменен с long на unsignedlong.
void PyEval_AcquireThread(PyThreadState *tstate) Часть К14153К.
Получите глобальную блокировку интерпретатора и установите текущее состояние потока в tstate, которое не должно быть NULL . Замок должен быть создан ранее. Если этот поток уже имеет блокировку, возникает взаимоблокировка.
Вызов этой функции из потока во время завершения среды выполнения завершит поток, даже если поток не был создан Python. Вы можете использовать _Py_IsFinalizing() или sys.is_finalizing() , чтобы проверить, находится ли интерпретатор в процессе завершения, прежде чем вызывать эту функцию, чтобы избежать нежелательного завершения.
Изменено в версии 3.8:. Обновлено для совместимости с PyEval_RestoreThread() , Py_END_ALLOW_THREADS() и PyGILState_Ensure() и завершает текущий поток, если он вызывается во время завершения интерпретатора.
PyEval_RestoreThread() — это функция более высокого уровня, которая доступна всегда (даже если потоки не были initialized).).
void PyEval_ReleaseThread(PyThreadState *tstate) Часть К14153К.
Сбросьте текущее состояние потока на NULL и снимите глобальную блокировку интерпретатора. Блокировка должна быть создана ранее и удерживаться текущим потоком. Аргумент tstate, который не должен быть NULL , используется только для проверки того, что он представляет текущее состояние потока — если это не так, сообщается о фатальной ошибке.
PyEval_SaveThread() — функция более высокого уровня, которая доступна всегда (даже если потоки не были initialized).).
void PyEval_AcquireLock() Часть К14153К.
Получите глобальную блокировку переводчика. Замок должен быть создан ранее. Если этот поток уже имеет блокировку, возникает взаимоблокировка.
Устарела, начиная с версии 3.2:. Эта функция не обновляет текущее состояние потока. Вместо этого используйте PyEval_RestoreThread() или PyEval_AcquireThread() .
Вызов этой функции из потока во время завершения среды выполнения завершит поток, даже если поток не был создан Python. Вы можете использовать _Py_IsFinalizing() или sys.is_finalizing() , чтобы проверить, находится ли интерпретатор в процессе завершения, прежде чем вызывать эту функцию, чтобы избежать нежелательного завершения.
Изменено в версии 3.8:. Обновлено для совместимости с PyEval_RestoreThread() , Py_END_ALLOW_THREADS() и PyGILState_Ensure() и завершает текущий поток, если он вызывается во время завершения интерпретатора.
void PyEval_ReleaseLock() Часть К14153К.
Снимите глобальную блокировку интерпретатора. Замок должен быть создан ранее.
Устарела с версии 3.2:. Эта функция не обновляет текущее состояние потока. Вместо этого используйте PyEval_SaveThread() или PyEval_ReleaseThread() .
Sub-interpreter support
Хотя в большинстве случаев вам понадобится встроить только один интерпретатор Python, в некоторых случаях вам необходимо создать несколько независимых интерпретаторов в одном процессе и, возможно, даже в одном потоке. Субинтерпретаторы позволяют вам это сделать.
«Основной» интерпретатор создается первым при инициализации среды выполнения. Обычно это единственный интерпретатор Python в процессе. В отличие от субинтерпретаторов, главный интерпретатор имеет уникальные глобальные обязанности, такие как обработка сигналов. Он также отвечает за выполнение во время инициализации среды выполнения и обычно является активным интерпретатором во время финализации среды выполнения. Функция PyInterpreterState_Main() возвращает указатель на свое состояние.
Переключаться между субинтерпретаторами можно с помощью функции PyThreadState_Swap() . Вы можете создавать и уничтожать их, используя следующие функции:
PyThreadState *Py_NewInterpreter() Часть К14153К.
Создайте новый субинтерпретатор. Это (почти) полностью отдельная среда для выполнения кода Python. В частности, в новом интерпретаторе имеются отдельные, независимые версии всех импортированных модулей, включая фундаментальные модули builtins , __main__ и sys . Таблица загруженных модулей ( sys.modules ) и путь поиска модулей ( sys.path ) также разделены. В новой среде нет переменной sys.argv . Он имеет новые стандартные объекты потокового файла I/O sys.stdin , sys.stdout и sys.stderr (однако они относятся к одному и тому же базовому файлу descriptors).).
Возвращаемое значение указывает на первое состояние потока, созданное в новом субинтерпретаторе. Это состояние потока создается в текущем состоянии потока. Обратите внимание, что фактический поток не создается; см. обсуждение состояний потоков ниже. Если создание нового интерпретатора не удалось, возвращается NULL ; исключение не устанавливается, поскольку состояние исключения сохраняется в текущем состоянии потока, и текущего состояния потока может не быть. (Like для всех остальных функций Python/C API глобальная блокировка интерпретатора должна удерживаться перед вызовом этой функции и по-прежнему удерживаться при ее возврате; однако, в отличие от большинства других функций Python/C API, на entry.) не обязательно должно быть текущее состояние потока.
Модули расширения используются в (sub-)interpreters следующим образом:
- Для модулей, использующих многофазную инициализацию, например PyModule_FromDefAndSpec() , для каждого интерпретатора создается и инициализируется отдельный объект модуля. Только static уровня C и глобальные переменные являются общими для этих объектов модуля.
- Для модулей, использующих однофазную инициализацию, например PyModule_Create() , при первом импорте конкретного расширения оно инициализируется обычным образом, а (неполная) копия словаря его модуля сохраняется. Когда то же расширение импортируется другим (sub-)interpreter,, новый модуль инициализируется и заполняется содержимым этой копии; функция init расширения не вызывается. Таким образом, объекты в словаре модуля становятся общими для (sub-)interpreters,, что может вызвать нежелательное поведение (см. Bugs and caveats below).). Обратите внимание, что это отличается от того, что происходит, когда расширение импортируется после полной повторной инициализации интерпретатора путем вызова Py_FinalizeEx() и Py_Initialize() ; в этом случае функция расширения initmodule вызывается снова. Как и в случае с многофазной инициализацией, это означает, что между этими модулями используются только static уровня C и глобальные переменные.
Уничтожьте (sub-)interpreter, представленный данным состоянием потока. Данное состояние потока должно быть текущим состоянием потока. См. обсуждение состояний потоков ниже. Когда вызов возвращается, текущее состояние потока — NULL . Все состояния потока, связанные с этим интерпретатором, уничтожаются. (The Глобальная блокировка интерпретатора должна быть удержана перед вызовом этой функции и по-прежнему удерживается, когда она вызывается. returns.) Py_FinalizeEx() уничтожит все субинтерпретаторы, которые не были явно уничтожены в этот момент.
Ошибки и предостережения
Поскольку субинтерпретаторы (и основной интерпретатор) являются частью одного и того же процесса, изоляция между ними не идеальна — например, используя низкоуровневые файловые операции, такие как os.close() , они могут (случайно или злонамеренно) влиять на открытые файлы друг друга. Из-за способа совместного использования расширений между (sub-)interpreters, некоторые расширения могут работать некорректно; это особенно вероятно при использовании однофазной инициализации или глобальных переменных (static). Можно вставлять объекты, созданные в одном субинтерпретаторе, в пространство имен другого (sub-)interpreter;, этого следует избегать, если это возможно.
Следует проявлять особую осторожность, чтобы избежать совместного использования определяемых пользователем функций, методов, экземпляров или классов субинтерпретаторами, поскольку операции импорта, выполняемые такими объектами, могут повлиять на неправильный словарь (sub-)interpreter’s загруженных модулей. Не менее важно избегать совместного использования объектов, из которых доступно вышеперечисленное.
Также обратите внимание, что объединение этой функциональности с PyGILState_* APIs является деликатным делом, поскольку эти APIs предполагают биекцию между состояниями потоков Python и потоками OS-level, и это предположение нарушается наличием субинтерпретаторов. Настоятельно рекомендуется не переключать субинтерпретаторы между парой совпадающих вызовов PyGILState_Ensure() и PyGILState_Release() . Кроме того, расширения (такие как ctypes ), использующие эти APIs для вызова кода Python из потоков, созданных non-Python, вероятно, будут нарушены при использовании субинтерпретаторов.
Asynchronous Notifications
Предусмотрен механизм отправки асинхронных уведомлений в основной поток интерпретатора. Эти уведомления принимают форму указателя на функцию и аргумента-указателя void.
int Py_AddPendingCall(int (*func)(void*), void *arg) Часть К14153К.
Запланируйте вызов функции из основного потока интерпретатора. В случае успеха возвращается 0 и функция func ставится в очередь для вызова в основном потоке. В случае сбоя возвращается -1 без установки каких-либо исключений.
При успешной постановке в очередь func в конечном итоге будет вызван из основного потока интерпретатора с аргументом arg. Он будет вызываться асинхронно по отношению к нормально работающему коду Python, но при соблюдении обоих этих условий:
- на границе К14153К;
- с основным потоком, удерживающим global interpreter lock (поэтому функция может использовать полный C API).
func должна возвращать 0 в случае успеха или -1 в случае неудачи с набором исключений. func не будет прерываться для рекурсивного выполнения другого асинхронного уведомления, но его все равно можно прервать для переключения потоков, если глобальная блокировка интерпретатора снята.
Для запуска этой функции не требуется текущее состояние потока и не требуется глобальная блокировка интерпретатора.
Чтобы вызвать эту функцию в субинтерпретаторе, вызывающая сторона должна удерживать GIL.. В противном случае вызов функции func может быть запланирован из неправильного интерпретатора.
Это низкоуровневая функция, полезная только в особых случаях. Нет никакой гарантии, что func будет вызван как можно быстрее. Если основной поток занят выполнением системного вызова, func не будет вызываться до возврата системного вызова. Эта функция обычно не подходит для вызова кода Python из произвольных потоков C. Вместо этого используйте PyGILState API .
Изменено в версии 3.9: Если эта функция вызывается в подинтерпретаторе, функция func теперь запланирована для вызова из подинтерпретатора, а не из основного интерпретатора. У каждого субпереводчика теперь есть свой список запланированных вызовов.
Новое в версии 3.1.
Профилирование и отслеживание
Интерпретатор Python обеспечивает некоторую низкоуровневую поддержку для подключения средств профилирования и отслеживания выполнения. Они используются для инструментов профилирования, отладки и анализа покрытия.
Этот интерфейс C позволяет коду профилирования или трассировки избежать накладных расходов на вызов через вызываемые объекты уровня Python, вместо этого выполняя прямой вызов функции C. Основные характеристики объекта не изменились; интерфейс позволяет устанавливать функции трассировки для каждого потока, а основные события, сообщаемые функции трассировки, такие же, как и функции трассировки уровня Python в предыдущих версиях.
typedef int (*Py_tracefunc)(PyObject *obj, PyFrameObject *frame, int what, PyObject *arg)
Тип функции трассировки, зарегистрированной с помощью PyEval_SetProfile() и PyEval_SetTrace() . Первый параметр — это объект, передаваемый в функцию регистрации как obj,frame — это объект кадра, к которому относится событие, какая из констант PyTrace_CALL , PyTrace_EXCEPTION , PyTrace_LINE , PyTrace_RETURN , PyTrace_C_CALL , PyTrace_C_EXCEPTION , PyTrace_C_RETURN или PyTrace_OPCODE и arg зависит от стоимости чего: