Консоль IPython

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

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 автодополнение работает и для имен файлов в текущем работчем каталоге.

Запуск сценариев из консоли 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. Эти среды предоставляют более широкие возможности, которые повышают производительность при работе с интерактивными и исследовательскими приложениями.