• Like
Беглый обзор "внутренностей" Python
Upcoming SlideShare
Loading in...5
×

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

  • 1,102 views
Uploaded on

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

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

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
1,102
On Slideshare
0
From Embeds
0
Number of Embeds
3

Actions

Shares
Downloads
56
Comments
0
Likes
4

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Беглый обзор “внутренностей” PythonНикита Лесников
  • 2. Почему это может быть полезно?Часто у программиста, использующего в работе Python, возникаетодин из следующих вопросов:какой из двух способов решения проблемы потребляет меньшепамяти?Беглый обзор “внутренностей” Python 2
  • 3. Почему это может быть полезно?Часто у программиста, использующего в работе Python, возникаетодин из следующих вопросов:какой из двух способов решения проблемы потребляет меньшепамяти?какой из двух способов работает быстрее?Беглый обзор “внутренностей” Python 2
  • 4. Почему это может быть полезно?Часто у программиста, использующего в работе Python, возникаетодин из следующих вопросов:какой из двух способов решения проблемы потребляет меньшепамяти?какой из двух способов работает быстрее?как поведет себя определенная конструкция при измененииruntime среды?Беглый обзор “внутренностей” Python 2
  • 5. Почему это может быть полезно?Решаются они обычно одним из следующих способов:можно спросить у коллегБеглый обзор “внутренностей” Python 3
  • 6. Почему это может быть полезно?Решаются они обычно одним из следующих способов:можно спросить у коллегможно найти ответ на StackOverflowБеглый обзор “внутренностей” Python 3
  • 7. Почему это может быть полезно?Решаются они обычно одним из следующих способов:можно спросить у коллегможно найти ответ на StackOverflowможно замерить самомуБеглый обзор “внутренностей” Python 3
  • 8. Почему это может быть полезно?Однако понимание того, как работает интерпретатор на самыхнижних уровнях, позволяет обрести интуитивное пониманиенюансов работы многих конструкций языка.Беглый обзор “внутренностей” Python 4
  • 9. Почему это может быть полезно?Однако понимание того, как работает интерпретатор на самыхнижних уровнях, позволяет обрести интуитивное пониманиенюансов работы многих конструкций языка.Кроме того, в случае с Python абсолютное большинствопрограммистов обладает достаточной квалификацией, чтобы безпосредников найти ответ на свой вопрос в исходникахинтерпретатора.Беглый обзор “внутренностей” Python 4
  • 10. Почему это может быть полезно?Знакомство с “внутренностями” полезно также потому, что Pythonявляется open source проектом - кто знает, быть может именно вырешите одну из наболевших проблем? ;)Беглый обзор “внутренностей” Python 5
  • 11. Почему это может быть полезно?Знакомство с “внутренностями” полезно также потому, что Pythonявляется open source проектом - кто знает, быть может именно вырешите одну из наболевших проблем? ;)Кратким изучением наиболее характерных особенностейинтерпретатора мы сейчас и займемся.Беглый обзор “внутренностей” Python 5
  • 12. CPythonОсновная реализация Python на сегодняшний деньБеглый обзор “внутренностей” Python 6
  • 13. CPythonОсновная реализация Python на сегодняшний деньНаписана на C (не С++), кроссплатформенна и довольно легкопереносима на отличные от официально поддерживаемыхплатформыБеглый обзор “внутренностей” Python 6
  • 14. CPythonОсновная реализация Python на сегодняшний деньНаписана на C (не С++), кроссплатформенна и довольно легкопереносима на отличные от официально поддерживаемыхплатформыКод простой и понятныйБеглый обзор “внутренностей” Python 6
  • 15. CPythonОсновная реализация Python на сегодняшний деньНаписана на C (не С++), кроссплатформенна и довольно легкопереносима на отличные от официально поддерживаемыхплатформыКод простой и понятныйНет, серьезно, простой и понятный, даже для людей, неинтересующихся разработкой языков программирования ;)Беглый обзор “внутренностей” Python 6
  • 16. CPythonЕсть еще PyPy, IronPython, Jython, Boo (хотя это не совсемPython)Беглый обзор “внутренностей” Python 7
  • 17. CPythonЕсть еще PyPy, IronPython, Jython, Boo (хотя это не совсемPython)Они по своему интересны, но с CPython “внутри” у них малообщегоБеглый обзор “внутренностей” Python 7
  • 18. CPythonЕсть еще PyPy, IronPython, Jython, Boo (хотя это не совсемPython)Они по своему интересны, но с CPython “внутри” у них малообщегоПоэтому хотя они и могут быть очень полезны на практике,рассматривать их мы не будемБеглый обзор “внутренностей” Python 7
  • 19. Все - объект.Беглый обзор “внутренностей” Python 8
  • 20. Все - объектВ Python все является объектомБеглый обзор “внутренностей” Python 9
  • 21. Все - объектВ Python все является объектомНу то есть вообще все - от чисел до стакфреймовБеглый обзор “внутренностей” Python 9
  • 22. Все - объектВ Python все является объектомНу то есть вообще все - от чисел до стакфреймовНа C-уровне это выражено типом PyObject *Беглый обзор “внутренностей” Python 9
  • 23. Все - объектВ Python все является объектомНу то есть вообще все - от чисел до стакфреймовНа C-уровне это выражено типом PyObject *Любой PyObject имеет стандартный заголовок:#define PyObject_HEAD Py_ssize_t ob_refcnt; struct _typeobject *ob_type;Беглый обзор “внутренностей” Python 9
  • 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. 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. СсылкиPyIntTypeObjectPyIntObjectob_refcnt = 3ob_typeob_ival = 42a b c>>> a = b = c = 42Все три имени ссылаются на одинобъектЭтот факт можно установить припомощи isВ общем случае == и is неэквивалентныБеглый обзор “внутренностей” Python 11
  • 27. NoneNone - особый объектБеглый обзор “внутренностей” Python 12
  • 28. NoneNone - особый объектОн один на каждый инстанс интерпретатора. Совсем один.Беглый обзор “внутренностей” Python 12
  • 29. NoneNone - особый объектОн один на каждый инстанс интерпретатора. Совсем один.Поэтому для него is и == всегда эквивалентны.Беглый обзор “внутренностей” Python 12
  • 30. NoneNone - особый объектОн один на каждый инстанс интерпретатора. Совсем один.Поэтому для него is и == всегда эквивалентны.Вот так вот делать не стоит:if x == None:это медленно, бессмысленно и вообще плохой тон.Беглый обзор “внутренностей” Python 12
  • 31. intint - тип “малых” целых чиселБеглый обзор “внутренностей” Python 13
  • 32. intint - тип “малых” целых чиселВ Python целые - неизменяемый типБеглый обзор “внутренностей” Python 13
  • 33. intint - тип “малых” целых чиселВ Python целые - неизменяемый типПроверим как на них работает is:>>> int("100") is int("100")True>>> int("1000") is int("1000")FalseБеглый обзор “внутренностей” Python 13
  • 34. intint - тип “малых” целых чиселВ Python целые - неизменяемый типПроверим как на них работает is:>>> int("100") is int("100")True>>> int("1000") is int("1000")FalseКак это объяснить? Оказывается, интерпретатор “кеширует”объекты int со значениями от -5 до 256, для других значенийсоздаются самостоятельные объекты.Беглый обзор “внутренностей” Python 13
  • 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. intНо загадки на этом не заканчиваются:>>> (1000 is 1000, 1000+0 is 1000+0)(True, False)Беглый обзор “внутренностей” Python 14
  • 37. intНо загадки на этом не заканчиваются:>>> (1000 is 1000, 1000+0 is 1000+0)(True, False)Почему так - немного дальше.Беглый обзор “внутренностей” Python 14
  • 38. stringСтроки, как и целые, в Python неизменяемыБеглый обзор “внутренностей” Python 15
  • 39. stringСтроки, как и целые, в Python неизменяемыОднако sharing объектов их затрагивает куда сильнееБеглый обзор “внутренностей” Python 15
  • 40. stringСтроки, как и целые, в Python неизменяемыОднако sharing объектов их затрагивает куда сильнееИнтерпретатор поддерживает dict так называемых internedстрок, для каждой из которых гарантированно существует ровноодин объектБеглый обзор “внутренностей” Python 15
  • 41. stringСтроки, как и целые, в Python неизменяемыОднако sharing объектов их затрагивает куда сильнееИнтерпретатор поддерживает dict так называемых internedстрок, для каждой из которых гарантированно существует ровноодин объектВсе идентификаторы (имена переменных, модулей, методов),автоматически туда помещаютсяБеглый обзор “внутренностей” Python 15
  • 42. stringСтроки, как и целые, в Python неизменяемыОднако sharing объектов их затрагивает куда сильнееИнтерпретатор поддерживает dict так называемых internedстрок, для каждой из которых гарантированно существует ровноодин объектВсе идентификаторы (имена переменных, модулей, методов),автоматически туда помещаютсяОператор == для interned строк выраждается в is (сравнениеуказателей)Беглый обзор “внутренностей” Python 15
  • 43. stringСтроки, как и целые, в Python неизменяемыОднако sharing объектов их затрагивает куда сильнееИнтерпретатор поддерживает dict так называемых internedстрок, для каждой из которых гарантированно существует ровноодин объектВсе идентификаторы (имена переменных, модулей, методов),автоматически туда помещаютсяОператор == для interned строк выраждается в is (сравнениеуказателей)Помимо этого, по аналогии с int со значениями [−5, 256]разделяются объекты пустой и всех возможных однобуквенныхстрокБеглый обзор “внутренностей” Python 15
  • 44. Краткий выводНе используйте is для чего-то кроме NoneБеглый обзор “внутренностей” Python 16
  • 45. Краткий выводНе используйте is для чего-то кроме NoneUnless you know what you’re doingБеглый обзор “внутренностей” Python 16
  • 46. БайткодБеглый обзор “внутренностей” Python 17
  • 47. Виртуальная машина“Железный” процессор не может напрямую выполнять код на CБеглый обзор “внутренностей” Python 18
  • 48. Виртуальная машина“Железный” процессор не может напрямую выполнять код на CЕго сначала нужно “разжевать” до очень простых операций,работающих с адресами и регистрамиБеглый обзор “внутренностей” Python 18
  • 49. Виртуальная машина“Железный” процессор не может напрямую выполнять код на CЕго сначала нужно “разжевать” до очень простых операций,работающих с адресами и регистрамиКод на Python также не годится для непосредственногоисполненияБеглый обзор “внутренностей” Python 18
  • 50. Виртуальная машина“Железный” процессор не может напрямую выполнять код на CЕго сначала нужно “разжевать” до очень простых операций,работающих с адресами и регистрамиКод на Python также не годится для непосредственногоисполненияПоэтому скрипт при загрузке транслируется в байткодвиртуальной машиныБеглый обзор “внутренностей” Python 18
  • 51. Виртуальная машина“Железный” процессор не может напрямую выполнять код на CЕго сначала нужно “разжевать” до очень простых операций,работающих с адресами и регистрамиКод на Python также не годится для непосредственногоисполненияПоэтому скрипт при загрузке транслируется в байткодвиртуальной машиныВиртуальная машина (VM) - это подпрограмма интерпретатора,испоняющая байткод.Беглый обзор “внутренностей” Python 18
  • 52. Виртуальная машина“Железный” процессор не может напрямую выполнять код на CЕго сначала нужно “разжевать” до очень простых операций,работающих с адресами и регистрамиКод на Python также не годится для непосредственногоисполненияПоэтому скрипт при загрузке транслируется в байткодвиртуальной машиныВиртуальная машина (VM) - это подпрограмма интерпретатора,испоняющая байткод.Но кроме отсутствия “железного” воплощения, концептуальныхразличий с процессором нет.Беглый обзор “внутренностей” Python 18
  • 53. БайткодКоманды CPython VM кодируются одним байтомБеглый обзор “внутренностей” Python 19
  • 54. БайткодКоманды CPython VM кодируются одним байтомКаждая из них может иметь опциональный 16-битный аргументБеглый обзор “внутренностей” Python 19
  • 55. БайткодКоманды CPython VM кодируются одним байтомКаждая из них может иметь опциональный 16-битный аргументКонцептуально VM является стековой машиной (уместныаналогии с обратной польской записью и языком Forth) -значения кладутся на value stack (не путать с call stack), операцииих снимают с вершины и кладут результат обратно.Беглый обзор “внутренностей” Python 19
  • 56. БайткодКоманды CPython VM кодируются одним байтомКаждая из них может иметь опциональный 16-битный аргументКонцептуально VM является стековой машиной (уместныаналогии с обратной польской записью и языком Forth) -значения кладутся на value stack (не путать с call stack), операцииих снимают с вершины и кладут результат обратно.При помощи модуля dis можно посмотреть на байткод “живых”функцийБеглый обзор “внутренностей” Python 19
  • 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. Константыdef f(): return (1,"abc", 3.0)Беглый обзор “внутренностей” Python 21
  • 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. Константы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. Code objectКак мы видим, байткод это не просто строка байт, так как16-битных целых недостаточно, чтобы кодировать любыеPython-объектыБеглый обзор “внутренностей” Python 22
  • 62. Code objectКак мы видим, байткод это не просто строка байт, так как16-битных целых недостаточно, чтобы кодировать любыеPython-объектыОднако их достаточно, чтобы кодировать смещения. Например, всписок констант co_constsБеглый обзор “внутренностей” Python 22
  • 63. Code objectКак мы видим, байткод это не просто строка байт, так как16-битных целых недостаточно, чтобы кодировать любыеPython-объектыОднако их достаточно, чтобы кодировать смещения. Например, всписок констант co_constsНаряду с другой метаинформацией (количество локальныхпеременных, количество параметров, глубина стека) байткодформирует code objectБеглый обзор “внутренностей” Python 22
  • 64. Code objectКак мы видим, байткод это не просто строка байт, так как16-битных целых недостаточно, чтобы кодировать любыеPython-объектыОднако их достаточно, чтобы кодировать смещения. Например, всписок констант co_constsНаряду с другой метаинформацией (количество локальныхпеременных, количество параметров, глубина стека) байткодформирует code objectИменно code object являет собой единицу существованиякомпилированного кода в PythonБеглый обзор “внутренностей” Python 22
  • 65. Code objectКак мы видим, байткод это не просто строка байт, так как16-битных целых недостаточно, чтобы кодировать любыеPython-объектыОднако их достаточно, чтобы кодировать смещения. Например, всписок констант co_constsНаряду с другой метаинформацией (количество локальныхпеременных, количество параметров, глубина стека) байткодформирует code objectИменно code object являет собой единицу существованиякомпилированного кода в PythonОни же сериализуются в .pyc файлыБеглый обзор “внутренностей” Python 22
  • 66. Code objectcode object являются неизменяемымиБеглый обзор “внутренностей” Python 23
  • 67. Code objectcode object являются неизменяемымиВ “нормальном” коде они строятся единожды - при загрузкемодуляБеглый обзор “внутренностей” Python 23
  • 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. КомпиляцияПроцесс преобразования исходного кода модуля в наборcode object называется компиляциейБеглый обзор “внутренностей” Python 24
  • 70. КомпиляцияПроцесс преобразования исходного кода модуля в наборcode object называется компиляциейОднако компилятор у Python очень рудиментарный - фактически1-в-1 отображение конструкций в байткодБеглый обзор “внутренностей” Python 24
  • 71. КомпиляцияПроцесс преобразования исходного кода модуля в наборcode object называется компиляциейОднако компилятор у Python очень рудиментарный - фактически1-в-1 отображение конструкций в байткодОптимизаций уровня байткода почти нетБеглый обзор “внутренностей” Python 24
  • 72. КомпиляцияПроцесс преобразования исходного кода модуля в наборcode object называется компиляциейОднако компилятор у Python очень рудиментарный - фактически1-в-1 отображение конструкций в байткодОптимизаций уровня байткода почти нетuncompyle, open source декомпилятор Python, способенвосстановить исходный код по .pyc файлу почти всегда, потерявпри этом разве что комментарииБеглый обзор “внутренностей” Python 24
  • 73. КомпиляцияПроцесс преобразования исходного кода модуля в наборcode object называется компиляциейОднако компилятор у Python очень рудиментарный - фактически1-в-1 отображение конструкций в байткодОптимизаций уровня байткода почти нетuncompyle, open source декомпилятор Python, способенвосстановить исходный код по .pyc файлу почти всегда, потерявпри этом разве что комментарииТак как компилятор неоптимизирующий, ему почти всегда можнопомочь (если надо)Беглый обзор “внутренностей” Python 24
  • 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. Loop hoistingLOAD_ATTR по идее выполняется небыстро - это поиск строки сименем метода в дикте (хеш-таблице). Строка interned, но всеравно это долго.Беглый обзор “внутренностей” Python 26
  • 76. Loop hoistingLOAD_ATTR по идее выполняется небыстро - это поиск строки сименем метода в дикте (хеш-таблице). Строка interned, но всеравно это долго.Логично не выполнять эту операцию в теле цикла, а сделать ееединожды до начала выполнения.Беглый обзор “внутренностей” Python 26
  • 77. Loop hoistingLOAD_ATTR по идее выполняется небыстро - это поиск строки сименем метода в дикте (хеш-таблице). Строка interned, но всеравно это долго.Логично не выполнять эту операцию в теле цикла, а сделать ееединожды до начала выполнения.Эта оптимизация известна как loop hoisting, но рудиментарныйкомпилятор Python ее не делает.Беглый обзор “внутренностей” Python 26
  • 78. Loop hoistingLOAD_ATTR по идее выполняется небыстро - это поиск строки сименем метода в дикте (хеш-таблице). Строка interned, но всеравно это долго.Логично не выполнять эту операцию в теле цикла, а сделать ееединожды до начала выполнения.Эта оптимизация известна как loop hoisting, но рудиментарныйкомпилятор Python ее не делает.Поможем ему!Беглый обзор “внутренностей” Python 26
  • 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. LIST_APPENDДобавление к списку - частая операция, и потому в CPython VMесть особый опкод LIST_APPENDБеглый обзор “внутренностей” Python 28
  • 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. НеймспейсыБеглый обзор “внутренностей” Python 29
  • 83. Дикты и строкиМногие знакомые с семантикой Python люди шутят, что Гвидо ванРоссум написал dict (открытую хеш-таблицу) и string(неизменяемые строки), после чего решил больше ничего неписатьБеглый обзор “внутренностей” Python 30
  • 84. Дикты и строкиМногие знакомые с семантикой Python люди шутят, что Гвидо ванРоссум написал dict (открытую хеш-таблицу) и string(неизменяемые строки), после чего решил больше ничего неписатьДействительно, объекты, модули и неймспейсы - все это обычныедикты cо строковыми ключамиБеглый обзор “внутренностей” Python 30
  • 85. Дикты и строкиМногие знакомые с семантикой Python люди шутят, что Гвидо ванРоссум написал dict (открытую хеш-таблицу) и string(неизменяемые строки), после чего решил больше ничего неписатьДействительно, объекты, модули и неймспейсы - все это обычныедикты cо строковыми ключамиГлобальное пространство имен модуля, следовательно, тоже дикт,а обращение к переменной - это lookup в дикте.Беглый обзор “внутренностей” Python 30
  • 86. Дикты и строкиМногие знакомые с семантикой Python люди шутят, что Гвидо ванРоссум написал dict (открытую хеш-таблицу) и string(неизменяемые строки), после чего решил больше ничего неписатьДействительно, объекты, модули и неймспейсы - все это обычныедикты cо строковыми ключамиГлобальное пространство имен модуля, следовательно, тоже дикт,а обращение к переменной - это lookup в дикте.Всегда ли это так?Беглый обзор “внутренностей” Python 30
  • 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. LOAD_FAST и LOAD_GLOBAL3 6 LOAD_FAST 0 (a)9 LOAD_GLOBAL 0 (b)Для имен a и b компилятор использовал разные опкоды. Почему?Беглый обзор “внутренностей” Python 32
  • 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. 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. LOAD_FAST и LOAD_GLOBALLOAD_FAST, как нетрудно догадаться, просто берет значение поиндексу из массиваБеглый обзор “внутренностей” Python 33
  • 92. LOAD_FAST и LOAD_GLOBALLOAD_FAST, как нетрудно догадаться, просто берет значение поиндексу из массиваLOAD_GLOBAL же вынужден брать строку с именем из co_consts,после чего искать по этому ключу в дикте неймспейсаБеглый обзор “внутренностей” Python 33
  • 93. LOAD_FAST и LOAD_GLOBALLOAD_FAST, как нетрудно догадаться, просто берет значение поиндексу из массиваLOAD_GLOBAL же вынужден брать строку с именем из co_consts,после чего искать по этому ключу в дикте неймспейсаОбе операции выполняются с ожидаемой стоимостью O(1), но умассива константа явно лучшеБеглый обзор “внутренностей” Python 33
  • 94. LOAD_FAST и LOAD_GLOBALLOAD_FAST, как нетрудно догадаться, просто берет значение поиндексу из массиваLOAD_GLOBAL же вынужден брать строку с именем из co_consts,после чего искать по этому ключу в дикте неймспейсаОбе операции выполняются с ожидаемой стоимостью O(1), но умассива константа явно лучшеПоэтому закешировать что-то в локальную переменную не самаяплохая идея в плане производительностиБеглый обзор “внутренностей” Python 33
  • 95. Lexical scopingГлобальный и локальный неймспейсы достаточно очевидны самипо себеБеглый обзор “внутренностей” Python 34
  • 96. Lexical scopingГлобальный и локальный неймспейсы достаточно очевидны самипо себеОднако Python позволяет делать вот так:def f():a = 1def g(): return areturn g>>> t = f()>>> t()1Беглый обзор “внутренностей” Python 34
  • 97. Lexical scopingГлобальный и локальный неймспейсы достаточно очевидны самипо себеОднако Python позволяет делать вот так:def f():a = 1def g(): return areturn g>>> t = f()>>> t()1Как возвращенной функции удается вернуть значение изразрушенного фрейма?Беглый обзор “внутренностей” Python 34
  • 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. 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. Lexical scopingcell object по времени жизни не привязан к фрейму, в которомон создан, и собирается сборщиком мусора только тогда, когдастанет недостижим.Беглый обзор “внутренностей” Python 36
  • 101. Lexical scopingcell object по времени жизни не привязан к фрейму, в которомон создан, и собирается сборщиком мусора только тогда, когдастанет недостижим.Это “взрослая” реализация лексической области видимостиБеглый обзор “внутренностей” Python 36
  • 102. Lexical scopingcell object по времени жизни не привязан к фрейму, в которомон создан, и собирается сборщиком мусора только тогда, когдастанет недостижим.Это “взрослая” реализация лексической области видимостиОднако, к сожалению, она неполна:def f():a = 1if True: a = 2; print aprint aэтот код выведет 2 2. Аналогичный код на C++, или, скажем,Lua, выдаст 2 1Беглый обзор “внутренностей” Python 36
  • 103. Lexical scopingcell object по времени жизни не привязан к фрейму, в которомон создан, и собирается сборщиком мусора только тогда, когдастанет недостижим.Это “взрослая” реализация лексической области видимостиОднако, к сожалению, она неполна:def f():a = 1if True: a = 2; print aprint aэтот код выведет 2 2. Аналогичный код на C++, или, скажем,Lua, выдаст 2 1Это источник трудноуловимых баговБеглый обзор “внутренностей” Python 36
  • 104. Выстрелить в ногу!Что выведет это код?l = [lambda: x for x in "abcdefg"]for r in l: print r(),Беглый обзор “внутренностей” Python 37
  • 105. Выстрелить в ногу!Что выведет это код?l = [lambda: x for x in "abcdefg"]for r in l: print r(),Наверное a b c d e f g?Беглый обзор “внутренностей” Python 37
  • 106. Выстрелить в ногу!Что выведет это код?l = [lambda: x for x in "abcdefg"]for r in l: print r(),Наверное a b c d e f g?Реальность жестока.Беглый обзор “внутренностей” Python 37
  • 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. Lexical scopingК сожалению, различие поведения областей видимости на макро-и микроуровнях настолько глубоко зашито в архитектуре языка,что поправить его практически нереальноБеглый обзор “внутренностей” Python 38
  • 109. Lexical scopingК сожалению, различие поведения областей видимости на макро-и микроуровнях настолько глубоко зашито в архитектуре языка,что поправить его практически нереальноДаже если это сделать - это с большой вероятностью приведет к“ломанию” имеющегося кодаБеглый обзор “внутренностей” Python 38
  • 110. Lexical scopingК сожалению, различие поведения областей видимости на макро-и микроуровнях настолько глубоко зашито в архитектуре языка,что поправить его практически нереальноДаже если это сделать - это с большой вероятностью приведет к“ломанию” имеющегося кодаПоэтому про данную особенность надо просто помнить, и несильно увлекаться функциональным программированием наPython ;)Беглый обзор “внутренностей” Python 38
  • 111. ЗаключениеБеглый обзор “внутренностей” Python 39
  • 112. ЗаключениеК сожалению, регламент данного доклада не дает слишкомразогнаться. За бортом остаются многие интересные темы:Беглый обзор “внутренностей” Python 40
  • 113. ЗаключениеК сожалению, регламент данного доклада не дает слишкомразогнаться. За бортом остаются многие интересные темы:Global Interpreter Lock (GIL), или почему программы на Python нанескольких ядрах работают медленнее, чем на одномБеглый обзор “внутренностей” Python 40
  • 114. ЗаключениеК сожалению, регламент данного доклада не дает слишкомразогнаться. За бортом остаются многие интересные темы:Global Interpreter Lock (GIL), или почему программы на Python нанескольких ядрах работают медленнее, чем на одномframe object и их список, или как greenlet умудряетсяэмулировать кооперативную многозадачность “срезанием” стекаБеглый обзор “внутренностей” Python 40
  • 115. ЗаключениеК сожалению, регламент данного доклада не дает слишкомразогнаться. За бортом остаются многие интересные темы:Global Interpreter Lock (GIL), или почему программы на Python нанескольких ядрах работают медленнее, чем на одномframe object и их список, или как greenlet умудряетсяэмулировать кооперативную многозадачность “срезанием” стекаООП в Python, или почему у object нельзя выставить атрибут, ау унаследованного от него пустого класса - можноБеглый обзор “внутренностей” Python 40
  • 116. ЗаключениеК сожалению, регламент данного доклада не дает слишкомразогнаться. За бортом остаются многие интересные темы:Global Interpreter Lock (GIL), или почему программы на Python нанескольких ядрах работают медленнее, чем на одномframe object и их список, или как greenlet умудряетсяэмулировать кооперативную многозадачность “срезанием” стекаООП в Python, или почему у object нельзя выставить атрибут, ау унаследованного от него пустого класса - можноГенераторы, или как “шаманство” с фреймами позволяетсоздавать видимость их (генераторов) наличия, но с серьезнымиограничениямиБеглый обзор “внутренностей” Python 40
  • 117. ЗаключениеОднако большой проблемы нет - это не сакральное знаниеБеглый обзор “внутренностей” Python 41
  • 118. ЗаключениеОднако большой проблемы нет - это не сакральное знаниеВсе, что я вам сегодня рассказал, я не прочитал в интернете и наStackOverflow - я прочитал это в исходных кодах интерпретатораБеглый обзор “внутренностей” Python 41
  • 119. ЗаключениеОднако большой проблемы нет - это не сакральное знаниеВсе, что я вам сегодня рассказал, я не прочитал в интернете и наStackOverflow - я прочитал это в исходных кодах интерпретатораОни действительно неплохо структурированы и их довольнопросто читать, даже людям без соответствующего бэкграундаБеглый обзор “внутренностей” Python 41
  • 120. ЗаключениеОднако большой проблемы нет - это не сакральное знаниеВсе, что я вам сегодня рассказал, я не прочитал в интернете и наStackOverflow - я прочитал это в исходных кодах интерпретатораОни действительно неплохо структурированы и их довольнопросто читать, даже людям без соответствующего бэкграундаМне это помогло глубже понять как работает тот код, который япишу, что порой оказывалось очень полезным при профайлингекритичных местБеглый обзор “внутренностей” Python 41
  • 121. ЗаключениеОднако большой проблемы нет - это не сакральное знаниеВсе, что я вам сегодня рассказал, я не прочитал в интернете и наStackOverflow - я прочитал это в исходных кодах интерпретатораОни действительно неплохо структурированы и их довольнопросто читать, даже людям без соответствующего бэкграундаМне это помогло глубже понять как работает тот код, который япишу, что порой оказывалось очень полезным при профайлингекритичных местЭто как раз тот случай, когда вместо того, чтобы лезть наStackOverflow, полезнее и интереснее разобраться самому. Такчто дерзайте!Беглый обзор “внутренностей” Python 41
  • 122. Благодарю за внимание!
  • 123. Вопросы?