Написание расширений Python, несомненно, является одним из способов улучшения Python, но вы также можете разветвить репозиторий и создать свою версию интерпретатора Python.

На практике вам, вероятно, нечасто приходится создавать Python самостоятельно как пользователю. Но полезно поиграть с ним, чтобы ознакомиться с кодовой базой Python и понять, что делает Python C API. Этот учебный опыт, несомненно, поможет вам писать более качественный код расширения и придаст вам больше уверенности при оценке правильности вашего кода.

Собрать Python

git clone [email protected]:python/cpython.git --depth=1
cd cpython
mkdir debug # Built artifacts are stored in the debug folder
cd debug
../configure --with-pydebug --enable-optimizations --with-lto
make -j8 # change 8 to the number of CPUs on your workstation

После создания вы можете найти исполняемый файл с именем python по текущему пути (в папке debug).

Обновите реализацию встроенного модуля Python.

Теперь давайте настроим модуль словаря, изменив его строку документации, определенную в этой переменной C.

# You should be in the `debug` folder
# Edit Objects/dictobject.c and update `dictionary_doc`
vim ../Objects/dictobject.c
make -j8 build_all
./python
>>> print(dict.__doc__) <- You should be able to see the new string

Поздравляем! Вы только что внесли первое изменение в интерпретатор Python!

Адаптация встроенных модулей Python

Мы также можем удалить определенные модули, обновив файл конфигурации Setup.

# You should be in the `debug` folder
# Comment out the last line (pwd module) by prepending #
vi Modules/Setup.bootstrap
# Rebuild Python
make -j8 build_all
# Now let's verify that the pwd module no longer exists
./python
>>> import pwd
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'pwd'

Добавление нового встроенного модуля

В кодовой базе CPython уже есть исчерпывающий пример встроенного модуля с именем xxmodule.c (обратите внимание, что этот файл находится в родительской папке, а не в папке debug). Этот пример также можно использовать в качестве шаблона при написании новых модулей.

Давайте включим xxmodule, чтобы вы могли видеть, что происходит. Я настоятельно рекомендую вам ознакомиться с его исходным кодом, так как pybind11 является хорошей оболочкой C++ для этих кодов C, и вы узнаете, как был разработан код оболочки позже в этой серии.

# Still in the `debug` folder
# Enable xxmodule by adding a new line with content `xx xxmodule.c`. 
# Make sure to finish this file with an empty space
vi Modules/Setup.local
# Rebuild Python
make -j8 build_all
./python
>>> import xx
>>> xx.Str('hi')
'hi'
>>> xx.bug([1,2,3])
1

Тест: после прочтения исходного кода вы поняли, в чем ошибка метода bug?

Ответ: это очень тонкая ошибка, связанная с подсчетом ссылок. Проверьте здесь для отличного объяснения. И ниже приведен пример кода, который вызывает эту ошибку:

>>> class ValueA:
...     def __del__(self):
...             print("A is removed\n")
... 
>>> class ValueB:
...     def __del__(self):
...             print("B is removed\n")
...             global input
...             del input[0]
... 
>>> input = [ValueA(), ValueB()]
>>> xx.bug(input)
B is removed
A is removed
<refcnt -2459565876494606883 at 0x7f8b47e61c30> <- weird output
>>> input
[0]
>>> input = [ValueA(), ValueB()]
>>> input
[<__main__.ValueA object at 0x7f8b47b4fbc0>, <__main__.ValueB object at 0x7f8b47b4edb0>]
>>> input[0]
<__main__.ValueA object at 0x7f8b47b4fbc0> <- expected output

Если вам интересно, в той же папке есть другие примеры, такие как xxlimited.c и xxsubtype.c, для справки.

›› Глава 3: Ваше первое расширение