IPython — расширенная среда интерпретатора REPL для Python с дополнительными функциями для интерактивных и исследовательских вычислений. Например, IPython предоставляет улучшенный просмотр истории команд (также между сессиями), систему кэшировния ввода и вывода, улучшенное автодополнение, более подробные и полезные отслеживания исключений и многое другое. Фактически, IPython теперь намного больше, чем расширенный интерфейс командной строки Python.
Выполнение команды ipython
запускает интерпретатор IPython:
Terminal> ipython
Python 3.8.1 (default, Jan 22 2020, 06:38:00)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.12.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]:
1: Для дополнительной информации смотрите официальную страницу проекта IPython http://ipython.org/ и официальную документацию.
В консоли IPython подсказка ввода обозначается как In[1]:
, а
соответствующий вывод обозначается как Out[1]:
, где число в
квадратных скобках увеличивается для каждого нового ввода и
вывода. Эти подсказки ввода и вывода называются ячейками в
IPython. Значения как входных так и выходныех предыдущих ячеек можно
потом использовать через переменные In
и Out
, которые
автоматически создаются IPtyhon. Переменные In
и Out
— это список
и словарь соответственно, которые могут быть проиндексированы индексом
ячейки. Например, рассмотрим следующую сессию IPython:
In [1]: 3*3
Out[1]: 9
In [2]: In[1]
Out[2]: '3*3'
In [3]: Out[1]
Out[3]: 9
In [4]: In
Out[4]: ['', '3*3', 'In[1]', 'Out[1]', 'In']
In [5]: Out
Out[5]: {1: 9, 2: '3*3', 3: 9, 4: ['', '3*3', 'In[1]', 'Out[1]', 'In', 'Out']}
Кэширование ввода и вывода часто полезно при интерактивных и исследовательских вычислениях, так как результат вычислений может быть доступен даже если он не был явно присвоен переменной.
Отметим, что когда ячейка выполнена, значение последнего выражения в
ячейке ввода по-умолчанию отображается в соответствующей ячейке
вывода, если выражение не является присваиванием или если значение не
равно None
. Вывод можно пресечь посредством ввода точки с запятой в
конце инструкции:
In [6]: 1+2
Out[6]: 3
In [7]: 1+2;
In [8]: x=1
In [9]: x=2; x
Out[9]: 2
В Ipython нажатие клавиши TAB
активирует автодополнение, которое
отображает список символов (переменных, функций, классов и т.д.) с
именами, которые доступны для дополнения того, что уже было
набрано. Автодополнение в IPython является контекстным, и оно будет
искать совпадающие переменные и функции в текущем пространстве имен
или среди атрибутов и методов класса при вызове после имени экземпляра
класса. Например, os.<TAB>
даст список переменных, функций и классов
из модуля os
, а нажатие TAB
после того, как набрали os.w
даст
список символов из модуля os
, которые начинаются с w
. Эта функция
называется самоанализом объекта. Она очень полезна для
интерактивного изучения свойств объекта Python.
Самоанализ объекта удобен для изучения API модулей и содержащихся в
них классов и функций, и вместе со строками документации или
«docstrings», которые обычно представлены в коде Python, он
предоставляет встроенное динамическое справочное руководство для почти
всех модулей, которые установлены и могут быть импортированы. Если
закончить объект Python знаком вопроса и выполнить команду, то будет
выведена строка документации для объекта. Это аналогично вызову
Python-функции help
. Ввод объекта можно закончить двумя знаками
вопроса. В этом случае IPython пытается отобразить более детальную
документацию, вколючая (если возможно) исходный код. Например, для
того чтобы получить описание функции cos
из модуля math
можно
сделать следующее:
In [10]: import math
In [11]: math.cos?
Signature: math.cos(x, /)
Docstring: Return the cosine of x (measured in radians).
Type: builtin_function_or_method
Строки документации могут быть заданы для модулей, функций, классов и их атрибутов и методов. Хорошо документированные модули включают документацию своего API в исходный код.
IPython предоставляет также расширение языка Python, которое делает его удобным для взаимодействия с оболочкой системы, в которой он запущен. Все, что следует за восклицательным знаком выполняется с помощью системной оболочки. Например, в UNIX-подобных системах, таких как Linux или Mac OS X, список файлов из текущего каталога можно получить следующим образом:
In [12]: !ls
hello.py test.py
В Windows эквивалентная команда — !dir
. Этот метод взаимодействия с
операционной системой является очень мощной функцией, которая дает
возможность легкой навигации по файловой системе и использования
IPython в качестве консольной системной оболочки. Вывод команд,
следующих за восклицательным знаком, может быть легко записан в
переменную Python. Например, список файлов, полученный командой !ls
можно сохранить в список следующим образом:
In [13]: files = !ls
In [14]: len(files)
Out[14]: 2
In [15]: files
Out[15]: ['hello.py', 'test.py']
Аналогично, мы можем передать значения переменных Python командам
оболочки, поставив перед именем переменной знак $
:
In [16]: file = "test.py"
In [17]: !ls -l $file
-rw-r--r-- 1 svl users 29 фев 17 13:59 test.py
Такое двухстороннее взаимодействие между консолью IPython и системной оболочкой может оказаться очень удобным когда, например, мы обрабатываем файлы с данными.
IPython предоставляет команды расширения, так называемые магические
команды. Эти команды начинаются со знака %
. Один знак %
используется для однострочных команд, а два знака %
используются для
команд, которые действуют на нескольких ячейках (многострочные
команды). Чтобы получить полный список доступных команд расширения,
наберите %lsmagic
, а документацию для каждой команды можно получить
заканчивая магическую команду знаком вопроса:
In [10]: %lsmagic?
Docstring: List currently available magic functions.
File: /usr/lib/python3.8/site-packages/IPython/core/magics/basic.py
Кроме взаимодействия с системной оболочкой IPython предоставляет
команды для навигации по файловой системе. Команды будут знакомы
пользователям UNIX-подобных систем: %ls
(список файлов), %pwd
(вернуть текущий рабочий каталог), %cd
(изменить рабочий каталог),
%cp
(копировать файл), %less
(вывести содержимое файла) и
%writefile filename
(записать содержимое ячейки в файл
filename
). Отметим, что в IPython автодополнение работает и для имен
файлов в текущем работчем каталоге.
Команда %run
— важное и полезное расширение. С помощью этой команды
можно выполнять внешние файлы со сценариями Python в интерактивной
сессии IPython. Оставляя сессию активной между несколькими запусками
сценария, мы можем анализировать переменные и функции, определенные в
сценарии, интерактивно после окончания выполнения сценария. Рассмотрим
файл
# -*- coding: utf-8 -*-
def fib(n):
"""
Возвращает список первых n чисел Фибоначи
"""
f0, f1 = 0, 1
f = [1]*n
for i in range(1, n):
f[i] = f0 + f1
f0, f1 = f1, f[i]
return f
print(fib(10))
Сценарий определеяет функцию, которая генерирует последовательность из \( n \) чисел Фибоначчи, и выводит результат при \( n=10 \). Его можно запустить в системной консоли, используя стандартный интерпретатор Python:
Terminal> python fib.py
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
Также этот файл со сценарием можно выполнить в интерактивной сессии
IPython, которая выполнит тот же вывод, а также добавит символы
определенные в файле в локальное пространство имен, так что функция
fib
будет доступна для использования в интерактивной сессий после
выполнения команды %run
.
In [1]: %run fib.py
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
In [2]: %who
fib
In [3]: fib(6)
Out[3]: [1, 1, 2, 3, 5, 8]
В предыдущем примере мы также использовали команду %who
, которая
перечисляет все определенные символы (переменные и функции).
Команда %whos
аналогична, но также предоставляет более подробную
информацию о типе и значении каждого символа, когда это применимо.
IPython включает режим отладки, который может быть вызван после того
как возникло исключение Python (ошибка). После того, как в консоли
IPythob было напечатано сообщение об ошибке (Traceback) не
перехваченного исключения, можно непосредственно перейти в отладчик с
помощью команды %debug
. Такая возможность позволяет не перезапускать
программу с самого начала с помощью отладчика или с использованием
метода отладки с добавлением операторов печати в код. Если исключение
было неожиданным и произошло после длительных вычислений, это может
значительно сэкономить время.
Для того, чтобы увидеть как может работать команда %debug
,
рассмотрим следующий некорректный вызов функции fib
, определенной
выше. Вызов будет некорректным, так как мы передадим значение типа
float
в то время, как функция реализована в предположении, что
передаваемый аргумент имеет тип int
.`
In [4]: fib(1.0)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-4-da4664af34ee> in <module>
----> 1 fib(1.0)
~/Projects/Doconce/python-course/src/chapters/intro/src-intro/fib.py in fib(n)
5 """
6 f0, f1 = 0, 1
----> 7 f = [1]*n
8 for i in range(1, n):
9 f[i] = f0 + f1
TypeError: can't multiply sequence by non-int of type 'float'
В строке с номером 7 код сталкивается с ошибкой типа, и интерпретатор
Python вызывает исключение типа TypeError
. IPython перехватывает
исключение и выводит полезную трассировку вызовов в консоль. Если мы
не понимаем, почему код в строке 7 содержит ошибку, полезно запустить
отладчик с помощью команды %debug
. Тогда мы можем получить доступ к
локальному пространству имен в источнике исключения, который позволит
нам изучить более детально, почему возникло исключение.
In [5]: %debug
> /home/svl/Projects/Doconce/python-course/src/chapters/intro/src-intro/fib.py(7)fib()
5 """
6 f0, f1 = 0, 1
----> 7 f = [1]*n
8 for i in range(1, n):
9 f[i] = f0 + f1
ipdb> print(n)
1.0
Наберите знак вопроса в строке отладчика для того, чтобы получить подсказку с перечислением доступных команд.
ipydb> ?
Больше информации об отладчике Python и его возможностях можно получить в документации Python по адресу http://docs.python.org/3/library/pdb.html.
Сброс пространства имен сессии IPython часто полезен для гарантии
того, что программа запускается в нетронутой среде, не перегруженной
существующими переменными и функциями. Команда %reset
предоставляет
такую возможность (используйте аргумент -f
для принудительного
сброса). Использование этой команды часто устраняет необходимость в
других распространенных циклах выхода-перезапуска консоли. Несмотря на
то, что после выполнения команды %reset
необходимо заново
импортировать модули, важно знать, что даже если модули изменялись с
момента последнего импорта, новый импорт после команды %reset
не
будет импортировать новый модуль, а, скорее всего, включит
кэшированную версию модуля из предыдущего импорта. Такое поведение не
желательно при разработке новых модулей Python. В этом случае
повторный импорт ранее импортированного (и с тех пор обновленного)
модуля часто может быть достигнут с помощью функции reload
из
IPython.lib.deepreload
. Однако, этот метод не всегда работает, так
как некоторые библиотеки выполняют код во время импорта, который
должен выполняться только один раз. В этом случае есть только один
способ: остановить и заново запустить консоль IPython.
Команды %timeit
и %time
предоставляют простые средства сравнения,
которые полезны при попытках оптимизировать код. Команда %timeit
запускает оператор Python несколько раз и дает оценку времени
выполнения (используйте %%timeit
, чтобы сделать
то же самое для многострочной ячейки). Точное количество раз
выполнения оператора, определяется эвристически, если явно не
установлено с помощью флагов -n
и -r
. Команда %timeit
не
возвращает значение выражения. Если необходим результат выполнения
вычислений, можно использовать команды %time
или %%time
(для
многострочной ячейки), но эти команды запускают выражение только один
раз и, следовательно, дают менее точную оценку времени выполнения.
Следующий пример демонстрирует типичное использование команд %timit
и %time
In [6]: %timeit fib(100)
8.15 µs ± 196 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [7]: result = %time fib(100)
CPU times: user 19 µs, sys: 0 ns, total: 19 µs
Wall time: 23.1 µs
Хотя команды %timeit
и %time
полезны для измерения времени
выполнения вычислений, они не дают никакой подробной информации о том,
какая часть вычислений занимает больше времени. Такой анализ требует
более сложного профилировщика кода, такого, например, как cProfile
,
предоставляемого стандартной библиотекой Python. Профилировщик Python
доступен в IPython с помощью команд %prun
(для операторов) и %run
с аргументом -p
(для запуска внешних сценариев). Вывод
профилировщика довольно многословен и может быть настроен с помощью
опций команд %prun
и %run -p
(см. %prun?
).
В качестве примера рассмотрим функцию, которая моделирует \( N \) случайных ходоков, которые делают по \( M \) шагов, а затем вычисляет самое дальнее расстояние от начальной точки, достигнутой любым из случайных ходоков.
# -*- coding: utf-8 -*-
import numpy as np
def random_walker_max_distance(M, N):
trajectories = [np.random.randn(M).cumsum() for _ in range(N)]
return np.max(np.abs(trajectories))
Вызов этой функции с использованием профилировщика дает генерирует
следующий вывод, который включает информацию о том, как много раз
вызывается каждая функция и разбивку общего и совокупного времени,
потраченного на каждую функцию. По этой информации мы можем сделать
вывод, что в нашем простом примере вызовы функции np.random.rand
потребляет большую часть времени вычислений.
In [15]: %prun random_walker_max_distance(400, 10000)
20013 function calls in 0.257 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
10000 0.145 0.000 0.145 0.000 {method 'randn' of 'numpy.random.mtrand.RandomState' objects}
1 0.056 0.056 0.254 0.254 walkers.py:4(random_walker_max_distance)
10000 0.042 0.000 0.042 0.000 {method 'cumsum' of 'numpy.ndarray' objects}
1 0.008 0.008 0.195 0.195 walkers.py:5(<listcomp>)
1 0.003 0.003 0.003 0.003 {method 'reduce' of 'numpy.ufunc' objects}
1 0.003 0.003 0.257 0.257 <string>:1(<module>)
1 0.000 0.000 0.257 0.257 {built-in method builtins.exec}
1 0.000 0.000 0.003 0.003 fromnumeric.py:73(_wrapreduction)
1 0.000 0.000 0.003 0.003 <__array_function__ internals>:2(amax)
1 0.000 0.000 0.003 0.003 fromnumeric.py:2551(amax)
1 0.000 0.000 0.003 0.003 {built-in method numpy.core._multiarray_umath.implement_array_function}
1 0.000 0.000 0.000 0.000 fromnumeric.py:74(<dictcomp>)
1 0.000 0.000 0.000 0.000 fromnumeric.py:2546(_amax_dispatcher)
1 0.000 0.000 0.000 0.000 {method 'items' of 'dict' objects}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
В принципе, интерпретаторы Python и IPython и хороший текстовый редактор — это все, что нужно для полной среды разработки на Python. Эта простая связка, по сути, является предпочтительной средой разработки для многих опытных программистов. Однако в следующих разделах мы рассмотрим Jupyter Notebook и интегрированную среду разработки Spyder. Эти среды предоставляют более широкие возможности, которые повышают производительность при работе с интерактивными и исследовательскими приложениями.