Successfully reported this slideshow.

Беглый обзор "внутренностей" Python

2,356 views

Published on

Беглый обзор "внутренностей" Python.
Автор: Никита Лесников

Published in: Technology

Беглый обзор "внутренностей" Python

  1. 1. Беглый обзор “внутренностей” PythonНикита Лесников
  2. 2. Почему это может быть полезно?Часто у программиста, использующего в работе Python, возникаетодин из следующих вопросов:какой из двух способов решения проблемы потребляет меньшепамяти?Беглый обзор “внутренностей” Python 2
  3. 3. Почему это может быть полезно?Часто у программиста, использующего в работе Python, возникаетодин из следующих вопросов:какой из двух способов решения проблемы потребляет меньшепамяти?какой из двух способов работает быстрее?Беглый обзор “внутренностей” Python 2
  4. 4. Почему это может быть полезно?Часто у программиста, использующего в работе Python, возникаетодин из следующих вопросов:какой из двух способов решения проблемы потребляет меньшепамяти?какой из двух способов работает быстрее?как поведет себя определенная конструкция при измененииruntime среды?Беглый обзор “внутренностей” Python 2
  5. 5. Почему это может быть полезно?Решаются они обычно одним из следующих способов:можно спросить у коллегБеглый обзор “внутренностей” Python 3
  6. 6. Почему это может быть полезно?Решаются они обычно одним из следующих способов:можно спросить у коллегможно найти ответ на StackOverflowБеглый обзор “внутренностей” Python 3
  7. 7. Почему это может быть полезно?Решаются они обычно одним из следующих способов:можно спросить у коллегможно найти ответ на StackOverflowможно замерить самомуБеглый обзор “внутренностей” Python 3
  8. 8. Почему это может быть полезно?Однако понимание того, как работает интерпретатор на самыхнижних уровнях, позволяет обрести интуитивное пониманиенюансов работы многих конструкций языка.Беглый обзор “внутренностей” Python 4
  9. 9. Почему это может быть полезно?Однако понимание того, как работает интерпретатор на самыхнижних уровнях, позволяет обрести интуитивное пониманиенюансов работы многих конструкций языка.Кроме того, в случае с Python абсолютное большинствопрограммистов обладает достаточной квалификацией, чтобы безпосредников найти ответ на свой вопрос в исходникахинтерпретатора.Беглый обзор “внутренностей” Python 4
  10. 10. Почему это может быть полезно?Знакомство с “внутренностями” полезно также потому, что Pythonявляется open source проектом - кто знает, быть может именно вырешите одну из наболевших проблем? ;)Беглый обзор “внутренностей” Python 5
  11. 11. Почему это может быть полезно?Знакомство с “внутренностями” полезно также потому, что Pythonявляется open source проектом - кто знает, быть может именно вырешите одну из наболевших проблем? ;)Кратким изучением наиболее характерных особенностейинтерпретатора мы сейчас и займемся.Беглый обзор “внутренностей” Python 5
  12. 12. CPythonОсновная реализация Python на сегодняшний деньБеглый обзор “внутренностей” Python 6
  13. 13. CPythonОсновная реализация Python на сегодняшний деньНаписана на C (не С++), кроссплатформенна и довольно легкопереносима на отличные от официально поддерживаемыхплатформыБеглый обзор “внутренностей” Python 6
  14. 14. CPythonОсновная реализация Python на сегодняшний деньНаписана на C (не С++), кроссплатформенна и довольно легкопереносима на отличные от официально поддерживаемыхплатформыКод простой и понятныйБеглый обзор “внутренностей” Python 6
  15. 15. CPythonОсновная реализация Python на сегодняшний деньНаписана на C (не С++), кроссплатформенна и довольно легкопереносима на отличные от официально поддерживаемыхплатформыКод простой и понятныйНет, серьезно, простой и понятный, даже для людей, неинтересующихся разработкой языков программирования ;)Беглый обзор “внутренностей” Python 6
  16. 16. CPythonЕсть еще PyPy, IronPython, Jython, Boo (хотя это не совсемPython)Беглый обзор “внутренностей” Python 7
  17. 17. CPythonЕсть еще PyPy, IronPython, Jython, Boo (хотя это не совсемPython)Они по своему интересны, но с CPython “внутри” у них малообщегоБеглый обзор “внутренностей” Python 7
  18. 18. CPythonЕсть еще PyPy, IronPython, Jython, Boo (хотя это не совсемPython)Они по своему интересны, но с CPython “внутри” у них малообщегоПоэтому хотя они и могут быть очень полезны на практике,рассматривать их мы не будемБеглый обзор “внутренностей” Python 7
  19. 19. Все - объект.Беглый обзор “внутренностей” Python 8
  20. 20. Все - объектВ Python все является объектомБеглый обзор “внутренностей” Python 9
  21. 21. Все - объектВ Python все является объектомНу то есть вообще все - от чисел до стакфреймовБеглый обзор “внутренностей” Python 9
  22. 22. Все - объектВ Python все является объектомНу то есть вообще все - от чисел до стакфреймовНа C-уровне это выражено типом PyObject *Беглый обзор “внутренностей” Python 9
  23. 23. Все - объектВ Python все является объектомНу то есть вообще все - от чисел до стакфреймовНа C-уровне это выражено типом PyObject *Любой PyObject имеет стандартный заголовок:#define PyObject_HEAD Py_ssize_t ob_refcnt; struct _typeobject *ob_type;Беглый обзор “внутренностей” Python 9
  24. 24. Все - объектВ Python все является объектомНу то есть вообще все - от чисел до стакфреймовНа C-уровне это выражено типом PyObject *Любой PyObject имеет стандартный заголовок:#define PyObject_HEAD Py_ssize_t ob_refcnt; struct _typeobject *ob_type;Поэтому в 64-битном Python число не может быть меньше 24байт. Deal with it.Беглый обзор “внутренностей” Python 9
  25. 25. PyObject *#define PyObject_HEAD Py_ssize_t ob_refcnt; struct _typeobject *ob_type;ob_refcnt - reference counterob_type - type object, определяющий поведение объекта изначение полей struct’а, идущих после заголовка (например,PyStringObject, PyIntObject)PyObject * - указатель. Поэтому все значения в Python передаютсяпо ссылке. No exceptions.Беглый обзор “внутренностей” Python 10
  26. 26. СсылкиPyIntTypeObjectPyIntObjectob_refcnt = 3ob_typeob_ival = 42a b c>>> a = b = c = 42Все три имени ссылаются на одинобъектЭтот факт можно установить припомощи isВ общем случае == и is неэквивалентныБеглый обзор “внутренностей” Python 11
  27. 27. NoneNone - особый объектБеглый обзор “внутренностей” Python 12
  28. 28. NoneNone - особый объектОн один на каждый инстанс интерпретатора. Совсем один.Беглый обзор “внутренностей” Python 12
  29. 29. NoneNone - особый объектОн один на каждый инстанс интерпретатора. Совсем один.Поэтому для него is и == всегда эквивалентны.Беглый обзор “внутренностей” Python 12
  30. 30. NoneNone - особый объектОн один на каждый инстанс интерпретатора. Совсем один.Поэтому для него is и == всегда эквивалентны.Вот так вот делать не стоит:if x == None:это медленно, бессмысленно и вообще плохой тон.Беглый обзор “внутренностей” Python 12
  31. 31. intint - тип “малых” целых чиселБеглый обзор “внутренностей” Python 13
  32. 32. intint - тип “малых” целых чиселВ Python целые - неизменяемый типБеглый обзор “внутренностей” Python 13
  33. 33. intint - тип “малых” целых чиселВ Python целые - неизменяемый типПроверим как на них работает is:>>> int("100") is int("100")True>>> int("1000") is int("1000")FalseБеглый обзор “внутренностей” Python 13
  34. 34. intint - тип “малых” целых чиселВ Python целые - неизменяемый типПроверим как на них работает is:>>> int("100") is int("100")True>>> int("1000") is int("1000")FalseКак это объяснить? Оказывается, интерпретатор “кеширует”объекты int со значениями от -5 до 256, для других значенийсоздаются самостоятельные объекты.Беглый обзор “внутренностей” Python 13
  35. 35. intint - тип “малых” целых чиселВ Python целые - неизменяемый типПроверим как на них работает is:>>> int("100") is int("100")True>>> int("1000") is int("1000")FalseКак это объяснить? Оказывается, интерпретатор “кеширует”объекты int со значениями от -5 до 256, для других значенийсоздаются самостоятельные объекты.Поэтому список intов размером до байта будет иметь overhead в 8байт на элемент (указатель), а больших intов - до 32 байт наэлемент (указатель + объект).Беглый обзор “внутренностей” Python 13
  36. 36. intНо загадки на этом не заканчиваются:>>> (1000 is 1000, 1000+0 is 1000+0)(True, False)Беглый обзор “внутренностей” Python 14
  37. 37. intНо загадки на этом не заканчиваются:>>> (1000 is 1000, 1000+0 is 1000+0)(True, False)Почему так - немного дальше.Беглый обзор “внутренностей” Python 14
  38. 38. stringСтроки, как и целые, в Python неизменяемыБеглый обзор “внутренностей” Python 15
  39. 39. stringСтроки, как и целые, в Python неизменяемыОднако sharing объектов их затрагивает куда сильнееБеглый обзор “внутренностей” Python 15
  40. 40. stringСтроки, как и целые, в Python неизменяемыОднако sharing объектов их затрагивает куда сильнееИнтерпретатор поддерживает dict так называемых internedстрок, для каждой из которых гарантированно существует ровноодин объектБеглый обзор “внутренностей” Python 15
  41. 41. stringСтроки, как и целые, в Python неизменяемыОднако sharing объектов их затрагивает куда сильнееИнтерпретатор поддерживает dict так называемых internedстрок, для каждой из которых гарантированно существует ровноодин объектВсе идентификаторы (имена переменных, модулей, методов),автоматически туда помещаютсяБеглый обзор “внутренностей” Python 15
  42. 42. stringСтроки, как и целые, в Python неизменяемыОднако sharing объектов их затрагивает куда сильнееИнтерпретатор поддерживает dict так называемых internedстрок, для каждой из которых гарантированно существует ровноодин объектВсе идентификаторы (имена переменных, модулей, методов),автоматически туда помещаютсяОператор == для interned строк выраждается в is (сравнениеуказателей)Беглый обзор “внутренностей” Python 15
  43. 43. stringСтроки, как и целые, в Python неизменяемыОднако sharing объектов их затрагивает куда сильнееИнтерпретатор поддерживает dict так называемых internedстрок, для каждой из которых гарантированно существует ровноодин объектВсе идентификаторы (имена переменных, модулей, методов),автоматически туда помещаютсяОператор == для interned строк выраждается в is (сравнениеуказателей)Помимо этого, по аналогии с int со значениями [−5, 256]разделяются объекты пустой и всех возможных однобуквенныхстрокБеглый обзор “внутренностей” Python 15
  44. 44. Краткий выводНе используйте is для чего-то кроме NoneБеглый обзор “внутренностей” Python 16
  45. 45. Краткий выводНе используйте is для чего-то кроме NoneUnless you know what you’re doingБеглый обзор “внутренностей” Python 16
  46. 46. БайткодБеглый обзор “внутренностей” Python 17
  47. 47. Виртуальная машина“Железный” процессор не может напрямую выполнять код на CБеглый обзор “внутренностей” Python 18
  48. 48. Виртуальная машина“Железный” процессор не может напрямую выполнять код на CЕго сначала нужно “разжевать” до очень простых операций,работающих с адресами и регистрамиБеглый обзор “внутренностей” Python 18
  49. 49. Виртуальная машина“Железный” процессор не может напрямую выполнять код на CЕго сначала нужно “разжевать” до очень простых операций,работающих с адресами и регистрамиКод на Python также не годится для непосредственногоисполненияБеглый обзор “внутренностей” Python 18
  50. 50. Виртуальная машина“Железный” процессор не может напрямую выполнять код на CЕго сначала нужно “разжевать” до очень простых операций,работающих с адресами и регистрамиКод на Python также не годится для непосредственногоисполненияПоэтому скрипт при загрузке транслируется в байткодвиртуальной машиныБеглый обзор “внутренностей” Python 18
  51. 51. Виртуальная машина“Железный” процессор не может напрямую выполнять код на CЕго сначала нужно “разжевать” до очень простых операций,работающих с адресами и регистрамиКод на Python также не годится для непосредственногоисполненияПоэтому скрипт при загрузке транслируется в байткодвиртуальной машиныВиртуальная машина (VM) - это подпрограмма интерпретатора,испоняющая байткод.Беглый обзор “внутренностей” Python 18
  52. 52. Виртуальная машина“Железный” процессор не может напрямую выполнять код на CЕго сначала нужно “разжевать” до очень простых операций,работающих с адресами и регистрамиКод на Python также не годится для непосредственногоисполненияПоэтому скрипт при загрузке транслируется в байткодвиртуальной машиныВиртуальная машина (VM) - это подпрограмма интерпретатора,испоняющая байткод.Но кроме отсутствия “железного” воплощения, концептуальныхразличий с процессором нет.Беглый обзор “внутренностей” Python 18
  53. 53. БайткодКоманды CPython VM кодируются одним байтомБеглый обзор “внутренностей” Python 19
  54. 54. БайткодКоманды CPython VM кодируются одним байтомКаждая из них может иметь опциональный 16-битный аргументБеглый обзор “внутренностей” Python 19
  55. 55. БайткодКоманды CPython VM кодируются одним байтомКаждая из них может иметь опциональный 16-битный аргументКонцептуально VM является стековой машиной (уместныаналогии с обратной польской записью и языком Forth) -значения кладутся на value stack (не путать с call stack), операцииих снимают с вершины и кладут результат обратно.Беглый обзор “внутренностей” Python 19
  56. 56. БайткодКоманды CPython VM кодируются одним байтомКаждая из них может иметь опциональный 16-битный аргументКонцептуально VM является стековой машиной (уместныаналогии с обратной польской записью и языком Forth) -значения кладутся на value stack (не путать с call stack), операцииих снимают с вершины и кладут результат обратно.При помощи модуля dis можно посмотреть на байткод “живых”функцийБеглый обзор “внутренностей” Python 19
  57. 57. def f():a = 1b = 2c = 3return a + b * c2 0 LOAD_CONST 1 (1)3 STORE_FAST 0 (a)3 6 LOAD_CONST 2 (2)9 STORE_FAST 1 (b)4 12 LOAD_CONST 3 (3)15 STORE_FAST 2 (c)5 18 LOAD_FAST 0 (a)21 LOAD_FAST 1 (b)24 LOAD_FAST 2 (c)27 BINARY_MULTIPLY28 BINARY_ADD29 RETURN_VALUEБеглый обзор “внутренностей” Python 20
  58. 58. Константыdef f(): return (1,"abc", 3.0)Беглый обзор “внутренностей” Python 21
  59. 59. Константыdef f(): return (1,"abc", 3.0)>>> dis.dis(f)2 0 LOAD_CONST 4 ((1, ’abc’, 3.0))3 RETURN_VALUEБеглый обзор “внутренностей” Python 21
  60. 60. Константыdef f(): return (1,"abc", 3.0)>>> dis.dis(f)2 0 LOAD_CONST 4 ((1, ’abc’, 3.0))3 RETURN_VALUE>>> f.__code__.co_consts(None, 1, ’abc’, 3.0, (1, ’abc’, 3.0))Беглый обзор “внутренностей” Python 21
  61. 61. Code objectКак мы видим, байткод это не просто строка байт, так как16-битных целых недостаточно, чтобы кодировать любыеPython-объектыБеглый обзор “внутренностей” Python 22
  62. 62. Code objectКак мы видим, байткод это не просто строка байт, так как16-битных целых недостаточно, чтобы кодировать любыеPython-объектыОднако их достаточно, чтобы кодировать смещения. Например, всписок констант co_constsБеглый обзор “внутренностей” Python 22
  63. 63. Code objectКак мы видим, байткод это не просто строка байт, так как16-битных целых недостаточно, чтобы кодировать любыеPython-объектыОднако их достаточно, чтобы кодировать смещения. Например, всписок констант co_constsНаряду с другой метаинформацией (количество локальныхпеременных, количество параметров, глубина стека) байткодформирует code objectБеглый обзор “внутренностей” Python 22
  64. 64. Code objectКак мы видим, байткод это не просто строка байт, так как16-битных целых недостаточно, чтобы кодировать любыеPython-объектыОднако их достаточно, чтобы кодировать смещения. Например, всписок констант co_constsНаряду с другой метаинформацией (количество локальныхпеременных, количество параметров, глубина стека) байткодформирует code objectИменно code object являет собой единицу существованиякомпилированного кода в PythonБеглый обзор “внутренностей” Python 22
  65. 65. Code objectКак мы видим, байткод это не просто строка байт, так как16-битных целых недостаточно, чтобы кодировать любыеPython-объектыОднако их достаточно, чтобы кодировать смещения. Например, всписок констант co_constsНаряду с другой метаинформацией (количество локальныхпеременных, количество параметров, глубина стека) байткодформирует code objectИменно code object являет собой единицу существованиякомпилированного кода в PythonОни же сериализуются в .pyc файлыБеглый обзор “внутренностей” Python 22
  66. 66. Code objectcode object являются неизменяемымиБеглый обзор “внутренностей” Python 23
  67. 67. Code objectcode object являются неизменяемымиВ “нормальном” коде они строятся единожды - при загрузкемодуляБеглый обзор “внутренностей” Python 23
  68. 68. Code objectcode object являются неизменяемымиВ “нормальном” коде они строятся единожды - при загрузкемодуляЭто такой же объект как и все:def f():def g(): passreturn g2 0 LOAD_CONST 1 (<code object f ...>)3 MAKE_FUNCTION 06 STORE_FAST 0 (f)3 9 LOAD_GLOBAL 0 (g)12 RETURN_VALUEБеглый обзор “внутренностей” Python 23
  69. 69. КомпиляцияПроцесс преобразования исходного кода модуля в наборcode object называется компиляциейБеглый обзор “внутренностей” Python 24
  70. 70. КомпиляцияПроцесс преобразования исходного кода модуля в наборcode object называется компиляциейОднако компилятор у Python очень рудиментарный - фактически1-в-1 отображение конструкций в байткодБеглый обзор “внутренностей” Python 24
  71. 71. КомпиляцияПроцесс преобразования исходного кода модуля в наборcode object называется компиляциейОднако компилятор у Python очень рудиментарный - фактически1-в-1 отображение конструкций в байткодОптимизаций уровня байткода почти нетБеглый обзор “внутренностей” Python 24
  72. 72. КомпиляцияПроцесс преобразования исходного кода модуля в наборcode object называется компиляциейОднако компилятор у Python очень рудиментарный - фактически1-в-1 отображение конструкций в байткодОптимизаций уровня байткода почти нетuncompyle, open source декомпилятор Python, способенвосстановить исходный код по .pyc файлу почти всегда, потерявпри этом разве что комментарииБеглый обзор “внутренностей” Python 24
  73. 73. КомпиляцияПроцесс преобразования исходного кода модуля в наборcode object называется компиляциейОднако компилятор у Python очень рудиментарный - фактически1-в-1 отображение конструкций в байткодОптимизаций уровня байткода почти нетuncompyle, open source декомпилятор Python, способенвосстановить исходный код по .pyc файлу почти всегда, потерявпри этом разве что комментарииТак как компилятор неоптимизирующий, ему почти всегда можнопомочь (если надо)Беглый обзор “внутренностей” Python 24
  74. 74. Компиляцияdef f():l = []for i in xrange(10000):l.append(i)>>> timeit.timeit("""....""")0.09371685981750488>>> dis.dis(f) (... оставлено только тело цикла ...)4 25 LOAD_FAST 0 (l)28 LOAD_ATTR 1 (append)31 LOAD_FAST 1 (i)34 CALL_FUNCTION 1Беглый обзор “внутренностей” Python 25
  75. 75. Loop hoistingLOAD_ATTR по идее выполняется небыстро - это поиск строки сименем метода в дикте (хеш-таблице). Строка interned, но всеравно это долго.Беглый обзор “внутренностей” Python 26
  76. 76. Loop hoistingLOAD_ATTR по идее выполняется небыстро - это поиск строки сименем метода в дикте (хеш-таблице). Строка interned, но всеравно это долго.Логично не выполнять эту операцию в теле цикла, а сделать ееединожды до начала выполнения.Беглый обзор “внутренностей” Python 26
  77. 77. Loop hoistingLOAD_ATTR по идее выполняется небыстро - это поиск строки сименем метода в дикте (хеш-таблице). Строка interned, но всеравно это долго.Логично не выполнять эту операцию в теле цикла, а сделать ееединожды до начала выполнения.Эта оптимизация известна как loop hoisting, но рудиментарныйкомпилятор Python ее не делает.Беглый обзор “внутренностей” Python 26
  78. 78. Loop hoistingLOAD_ATTR по идее выполняется небыстро - это поиск строки сименем метода в дикте (хеш-таблице). Строка interned, но всеравно это долго.Логично не выполнять эту операцию в теле цикла, а сделать ееединожды до начала выполнения.Эта оптимизация известна как loop hoisting, но рудиментарныйкомпилятор Python ее не делает.Поможем ему!Беглый обзор “внутренностей” Python 26
  79. 79. Loop hoistingdef f():l = []la = l.appendfor i in xrange(10000):la(i)>>> timeit.timeit("""....""")0.08451047520987543>>> dis.dis(f) (... оставлено только тело цикла ...)5 34 LOAD_FAST 1 (la)37 LOAD_FAST 2 (i)40 CALL_FUNCTION 1До оптимизации было 0.09371685981750488. Разница - 10%.Беглый обзор “внутренностей” Python 27
  80. 80. LIST_APPENDДобавление к списку - частая операция, и потому в CPython VMесть особый опкод LIST_APPENDБеглый обзор “внутренностей” Python 28
  81. 81. LIST_APPENDДобавление к списку - частая операция, и потому в CPython VMесть особый опкод LIST_APPENDКак показывает изучение исходников компилятора, используетсяэтот опкод только для компиляции list comprehensions:def f():return [x for x in xrange(10000)]>>> timeit.timeit("""....""")0.08152854398842842>>> dis.dis(f) (... оставлено только тело цикла ...)16 STORE_FAST 0 (x)19 LOAD_FAST 0 (x)22 LIST_APPEND 2Беглый обзор “внутренностей” Python 28
  82. 82. НеймспейсыБеглый обзор “внутренностей” Python 29
  83. 83. Дикты и строкиМногие знакомые с семантикой Python люди шутят, что Гвидо ванРоссум написал dict (открытую хеш-таблицу) и string(неизменяемые строки), после чего решил больше ничего неписатьБеглый обзор “внутренностей” Python 30
  84. 84. Дикты и строкиМногие знакомые с семантикой Python люди шутят, что Гвидо ванРоссум написал dict (открытую хеш-таблицу) и string(неизменяемые строки), после чего решил больше ничего неписатьДействительно, объекты, модули и неймспейсы - все это обычныедикты cо строковыми ключамиБеглый обзор “внутренностей” Python 30
  85. 85. Дикты и строкиМногие знакомые с семантикой Python люди шутят, что Гвидо ванРоссум написал dict (открытую хеш-таблицу) и string(неизменяемые строки), после чего решил больше ничего неписатьДействительно, объекты, модули и неймспейсы - все это обычныедикты cо строковыми ключамиГлобальное пространство имен модуля, следовательно, тоже дикт,а обращение к переменной - это lookup в дикте.Беглый обзор “внутренностей” Python 30
  86. 86. Дикты и строкиМногие знакомые с семантикой Python люди шутят, что Гвидо ванРоссум написал dict (открытую хеш-таблицу) и string(неизменяемые строки), после чего решил больше ничего неписатьДействительно, объекты, модули и неймспейсы - все это обычныедикты cо строковыми ключамиГлобальное пространство имен модуля, следовательно, тоже дикт,а обращение к переменной - это lookup в дикте.Всегда ли это так?Беглый обзор “внутренностей” Python 30
  87. 87. Дикты и строкиdef f():a = 3return a + b2 0 LOAD_CONST 1 (1)3 STORE_FAST 0 (a)3 6 LOAD_FAST 0 (a)9 LOAD_GLOBAL 0 (b)12 BINARY_ADD13 RETURN_VALUEБеглый обзор “внутренностей” Python 31
  88. 88. LOAD_FAST и LOAD_GLOBAL3 6 LOAD_FAST 0 (a)9 LOAD_GLOBAL 0 (b)Для имен a и b компилятор использовал разные опкоды. Почему?Беглый обзор “внутренностей” Python 32
  89. 89. LOAD_FAST и LOAD_GLOBAL3 6 LOAD_FAST 0 (a)9 LOAD_GLOBAL 0 (b)Для имен a и b компилятор использовал разные опкоды. Почему?Оказывается, на этапе компиляции в code object собираются всеимена локальных переменных (то есть присваиваемые в этомблоке кода и не помеченные явно при помощи global), изаносятся в список co_locals.Беглый обзор “внутренностей” Python 32
  90. 90. LOAD_FAST и LOAD_GLOBAL3 6 LOAD_FAST 0 (a)9 LOAD_GLOBAL 0 (b)Для имен a и b компилятор использовал разные опкоды. Почему?Оказывается, на этапе компиляции в code object собираются всеимена локальных переменных (то есть присваиваемые в этомблоке кода и не помеченные явно при помощи global), изаносятся в список co_locals.Впоследствии для каждого фрейма стека создается массивлокальных переменных, и обращение к ним происходит по ихиндексу в co_locals. Этот индекс неизменен и “зашит” в байткод.Беглый обзор “внутренностей” Python 32
  91. 91. LOAD_FAST и LOAD_GLOBALLOAD_FAST, как нетрудно догадаться, просто берет значение поиндексу из массиваБеглый обзор “внутренностей” Python 33
  92. 92. LOAD_FAST и LOAD_GLOBALLOAD_FAST, как нетрудно догадаться, просто берет значение поиндексу из массиваLOAD_GLOBAL же вынужден брать строку с именем из co_consts,после чего искать по этому ключу в дикте неймспейсаБеглый обзор “внутренностей” Python 33
  93. 93. LOAD_FAST и LOAD_GLOBALLOAD_FAST, как нетрудно догадаться, просто берет значение поиндексу из массиваLOAD_GLOBAL же вынужден брать строку с именем из co_consts,после чего искать по этому ключу в дикте неймспейсаОбе операции выполняются с ожидаемой стоимостью O(1), но умассива константа явно лучшеБеглый обзор “внутренностей” Python 33
  94. 94. LOAD_FAST и LOAD_GLOBALLOAD_FAST, как нетрудно догадаться, просто берет значение поиндексу из массиваLOAD_GLOBAL же вынужден брать строку с именем из co_consts,после чего искать по этому ключу в дикте неймспейсаОбе операции выполняются с ожидаемой стоимостью O(1), но умассива константа явно лучшеПоэтому закешировать что-то в локальную переменную не самаяплохая идея в плане производительностиБеглый обзор “внутренностей” Python 33
  95. 95. Lexical scopingГлобальный и локальный неймспейсы достаточно очевидны самипо себеБеглый обзор “внутренностей” Python 34
  96. 96. Lexical scopingГлобальный и локальный неймспейсы достаточно очевидны самипо себеОднако Python позволяет делать вот так:def f():a = 1def g(): return areturn g>>> t = f()>>> t()1Беглый обзор “внутренностей” Python 34
  97. 97. Lexical scopingГлобальный и локальный неймспейсы достаточно очевидны самипо себеОднако Python позволяет делать вот так:def f():a = 1def g(): return areturn g>>> t = f()>>> t()1Как возвращенной функции удается вернуть значение изразрушенного фрейма?Беглый обзор “внутренностей” Python 34
  98. 98. Lexical scopingОказывается, такая ситуация детектируется при компиляции ирешается при помощи создания cell object (в функциональныхязыках подобные объекты называют замыканиями или closures):2 0 LOAD_CONST 1 (1)3 STORE_DEREF 0 (a)3 6 LOAD_CLOSURE 0 (a)9 BUILD_TUPLE 112 LOAD_CONST 2 (<code object ...>)15 MAKE_CLOSURE 018 STORE_FAST 0 (g)4 21 LOAD_FAST 0 (g)24 RETURN_VALUEБеглый обзор “внутренностей” Python 35
  99. 99. Lexical scopingОказывается, такая ситуация детектируется при компиляции ирешается при помощи создания cell object (в функциональныхязыках подобные объекты называют замыканиями или closures):2 0 LOAD_CONST 1 (1)3 STORE_DEREF 0 (a)3 6 LOAD_CLOSURE 0 (a)9 BUILD_TUPLE 112 LOAD_CONST 2 (<code object ...>)15 MAKE_CLOSURE 018 STORE_FAST 0 (g)4 21 LOAD_FAST 0 (g)24 RETURN_VALUEcell object - это код функции вместе со значениями “внешних”(свободных, free) переменныхБеглый обзор “внутренностей” Python 35
  100. 100. Lexical scopingcell object по времени жизни не привязан к фрейму, в которомон создан, и собирается сборщиком мусора только тогда, когдастанет недостижим.Беглый обзор “внутренностей” Python 36
  101. 101. Lexical scopingcell object по времени жизни не привязан к фрейму, в которомон создан, и собирается сборщиком мусора только тогда, когдастанет недостижим.Это “взрослая” реализация лексической области видимостиБеглый обзор “внутренностей” Python 36
  102. 102. Lexical scopingcell object по времени жизни не привязан к фрейму, в которомон создан, и собирается сборщиком мусора только тогда, когдастанет недостижим.Это “взрослая” реализация лексической области видимостиОднако, к сожалению, она неполна:def f():a = 1if True: a = 2; print aprint aэтот код выведет 2 2. Аналогичный код на C++, или, скажем,Lua, выдаст 2 1Беглый обзор “внутренностей” Python 36
  103. 103. Lexical scopingcell object по времени жизни не привязан к фрейму, в которомон создан, и собирается сборщиком мусора только тогда, когдастанет недостижим.Это “взрослая” реализация лексической области видимостиОднако, к сожалению, она неполна:def f():a = 1if True: a = 2; print aprint aэтот код выведет 2 2. Аналогичный код на C++, или, скажем,Lua, выдаст 2 1Это источник трудноуловимых баговБеглый обзор “внутренностей” Python 36
  104. 104. Выстрелить в ногу!Что выведет это код?l = [lambda: x for x in "abcdefg"]for r in l: print r(),Беглый обзор “внутренностей” Python 37
  105. 105. Выстрелить в ногу!Что выведет это код?l = [lambda: x for x in "abcdefg"]for r in l: print r(),Наверное a b c d e f g?Беглый обзор “внутренностей” Python 37
  106. 106. Выстрелить в ногу!Что выведет это код?l = [lambda: x for x in "abcdefg"]for r in l: print r(),Наверное a b c d e f g?Реальность жестока.Беглый обзор “внутренностей” Python 37
  107. 107. Выстрелить в ногу!Что выведет это код?l = [lambda: x for x in "abcdefg"]for r in l: print r(),Наверное a b c d e f g?Реальность жестока.Правильный ответ - g g g g g g g.Беглый обзор “внутренностей” Python 37
  108. 108. Lexical scopingК сожалению, различие поведения областей видимости на макро-и микроуровнях настолько глубоко зашито в архитектуре языка,что поправить его практически нереальноБеглый обзор “внутренностей” Python 38
  109. 109. Lexical scopingК сожалению, различие поведения областей видимости на макро-и микроуровнях настолько глубоко зашито в архитектуре языка,что поправить его практически нереальноДаже если это сделать - это с большой вероятностью приведет к“ломанию” имеющегося кодаБеглый обзор “внутренностей” Python 38
  110. 110. Lexical scopingК сожалению, различие поведения областей видимости на макро-и микроуровнях настолько глубоко зашито в архитектуре языка,что поправить его практически нереальноДаже если это сделать - это с большой вероятностью приведет к“ломанию” имеющегося кодаПоэтому про данную особенность надо просто помнить, и несильно увлекаться функциональным программированием наPython ;)Беглый обзор “внутренностей” Python 38
  111. 111. ЗаключениеБеглый обзор “внутренностей” Python 39
  112. 112. ЗаключениеК сожалению, регламент данного доклада не дает слишкомразогнаться. За бортом остаются многие интересные темы:Беглый обзор “внутренностей” Python 40
  113. 113. ЗаключениеК сожалению, регламент данного доклада не дает слишкомразогнаться. За бортом остаются многие интересные темы:Global Interpreter Lock (GIL), или почему программы на Python нанескольких ядрах работают медленнее, чем на одномБеглый обзор “внутренностей” Python 40
  114. 114. ЗаключениеК сожалению, регламент данного доклада не дает слишкомразогнаться. За бортом остаются многие интересные темы:Global Interpreter Lock (GIL), или почему программы на Python нанескольких ядрах работают медленнее, чем на одномframe object и их список, или как greenlet умудряетсяэмулировать кооперативную многозадачность “срезанием” стекаБеглый обзор “внутренностей” Python 40
  115. 115. ЗаключениеК сожалению, регламент данного доклада не дает слишкомразогнаться. За бортом остаются многие интересные темы:Global Interpreter Lock (GIL), или почему программы на Python нанескольких ядрах работают медленнее, чем на одномframe object и их список, или как greenlet умудряетсяэмулировать кооперативную многозадачность “срезанием” стекаООП в Python, или почему у object нельзя выставить атрибут, ау унаследованного от него пустого класса - можноБеглый обзор “внутренностей” Python 40
  116. 116. ЗаключениеК сожалению, регламент данного доклада не дает слишкомразогнаться. За бортом остаются многие интересные темы:Global Interpreter Lock (GIL), или почему программы на Python нанескольких ядрах работают медленнее, чем на одномframe object и их список, или как greenlet умудряетсяэмулировать кооперативную многозадачность “срезанием” стекаООП в Python, или почему у object нельзя выставить атрибут, ау унаследованного от него пустого класса - можноГенераторы, или как “шаманство” с фреймами позволяетсоздавать видимость их (генераторов) наличия, но с серьезнымиограничениямиБеглый обзор “внутренностей” Python 40
  117. 117. ЗаключениеОднако большой проблемы нет - это не сакральное знаниеБеглый обзор “внутренностей” Python 41
  118. 118. ЗаключениеОднако большой проблемы нет - это не сакральное знаниеВсе, что я вам сегодня рассказал, я не прочитал в интернете и наStackOverflow - я прочитал это в исходных кодах интерпретатораБеглый обзор “внутренностей” Python 41
  119. 119. ЗаключениеОднако большой проблемы нет - это не сакральное знаниеВсе, что я вам сегодня рассказал, я не прочитал в интернете и наStackOverflow - я прочитал это в исходных кодах интерпретатораОни действительно неплохо структурированы и их довольнопросто читать, даже людям без соответствующего бэкграундаБеглый обзор “внутренностей” Python 41
  120. 120. ЗаключениеОднако большой проблемы нет - это не сакральное знаниеВсе, что я вам сегодня рассказал, я не прочитал в интернете и наStackOverflow - я прочитал это в исходных кодах интерпретатораОни действительно неплохо структурированы и их довольнопросто читать, даже людям без соответствующего бэкграундаМне это помогло глубже понять как работает тот код, который япишу, что порой оказывалось очень полезным при профайлингекритичных местБеглый обзор “внутренностей” Python 41
  121. 121. ЗаключениеОднако большой проблемы нет - это не сакральное знаниеВсе, что я вам сегодня рассказал, я не прочитал в интернете и наStackOverflow - я прочитал это в исходных кодах интерпретатораОни действительно неплохо структурированы и их довольнопросто читать, даже людям без соответствующего бэкграундаМне это помогло глубже понять как работает тот код, который япишу, что порой оказывалось очень полезным при профайлингекритичных местЭто как раз тот случай, когда вместо того, чтобы лезть наStackOverflow, полезнее и интереснее разобраться самому. Такчто дерзайте!Беглый обзор “внутренностей” Python 41
  122. 122. Благодарю за внимание!
  123. 123. Вопросы?

×