Math

Делаем обвязку над библиотекой на языке C

Пишем библиотеку на C

pyfoo_c_01

foo.h

foo.cpp

main.c

Makefile

foofoo

foo_c_01/
└── foo
    ├── foo.c
    ├── foo.h
    ├── main.c
    └── Makefile

foo

binfoo

foo_c_01/
└── foo
    ├── bin
    │   ├── foo.o
    │   ├── libfoo.a
    │   ├── main
    │   └── main.o
    ├── foo.c
    ├── foo.h
    ├── main.c
    └── Makefile

mainmain

Основы работы с SIP

pyproject.tomlpyfoo_cfoofoobinfoo

pyproject.toml

pyproject.tomlpyproject.tomlpyproject.tomlfoo

foo_c_01/
├── foo
│   ├── bin
│   │   ├── foo.o
│   │   ├── libfoo.a
│   │   ├── main
│   │   └── main.o
│   ├── foo.c
│   ├── foo.h
│   ├── main.c
│   └── Makefile
└── pyproject.toml

pyproject.toml

  • build-backend описывает, с помощью чего мы будем собирать наш пакет. Строго говоря, этот параметр в виде строки должен содержать полное название Python-объекта, который будет заниматься сборкой. Если не задумываться над глубоким содержимым этого параметра, то для пакетов, собираемых с помощью SIP, это значение должно равняться «sipbuild.api».

pyfoopyfoo


  • headers — список заголовочных файлов, которые необходимы для использования библиотеки foo.
  • libraries — список объектных файлов, скомпилированных для статической линковки.
  • include-dirs — путь, где искать дополнительные заголовочные файлы помимо тех, что прилагаются к компилятору C. В данном случае, где искать файл foo.h.
  • library-dirs — путь, где искать дополнительные объектные файлы помимо тех, что прилагаются к компилятору C. В данном случае это папка, в которой создается скомпилированный файл библиотеки foo.

pyfoo.sip

pyfoo.sippyproject.toml

foo_c_01/
├── foo
│   ├── bin
│   │   ├── foo.o
│   │   ├── libfoo.a
│   │   ├── main
│   │   └── main.o
│   ├── foo.c
│   ├── foo.h
│   ├── main.c
│   └── Makefile
├── pyfoo.sip
└── pyproject.toml

pyfoo.sippyproject.tomlpyfoonamepyfoo.sip

pyfoo.sip%Module(name=foo, language=«C»)namelanguage%Modulenamefoofoo

foo

language%Modulepyfoo.sip

Собираем и проверяем

pyfoo_c_01/foo/make

foobinlibfoo.apyfoo_c_01

  • sip-build. Создает объектный файл Python-расширения (Python extension).
  • sip-install. Создает объектный файл Python-расширения и устанавливает его.
  • sip-sdist. Создает пакет в виде архива .tar.gz, который можно установить с помощью pip.
  • sip-wheel. Создает пакет в формате wheel (файл с расширением .whl).
  • sip-module. Создает модуль, в который включается только служебные инструменты, необходимые самому SIP. Это нужно, если вы создаете библиотеку, разбитую на несколько пакетов. В этой статье мы не будем рассматривать такой случай, мы будем создавать только так называемый standalone project, то есть наш пакет будет единый, он будет включать и библиотеку, для которой мы делаем обвязку, и все служебные инструменты.
  • sip-distinfo. Создает и заполняет папку .dist-info, которая используется в пакете в формате wheel.

pyproject.tomlsip-build—verbosesip-build —verbosebuild/foo/

pyfoo_c_01
├── build
│   └── foo
│       ├── apiversions.c
│       ├── array.c
│       ├── array.h
│       ├── bool.cpp
│       ├── build
│       │   └── temp.linux-x86_64-3.8
│       │       ├── apiversions.o
│       │       ├── array.o
│       │       ├── bool.o
│       │       ├── descriptors.o
│       │       ├── int_convertors.o
│       │       ├── objmap.o
│       │       ├── qtlib.o
│       │       ├── sipfoocmodule.o
│       │       ├── siplib.o
│       │       ├── threads.o
│       │       └── voidptr.o
│       ├── descriptors.c
│       ├── foo.cpython-38-x86_64-linux-gnu.so
│       ├── int_convertors.c
│       ├── objmap.c
│       ├── qtlib.c
│       ├── sipAPIfoo.h
│       ├── sipfoocmodule.c
│       ├── sip.h
│       ├── sipint.h
│       ├── siplib.c
│       ├── threads.c
│       └── voidptr.c
├── foo
│   ├── bin
│   │   ├── foo.o
│   │   ├── libfoo.a
│   │   ├── main
│   │   └── main.o
│   ├── foo.c
│   ├── foo.h
│   ├── main.c
│   └── Makefile
├── pyfoo.sip
└── pyproject.toml

build/foosipfoocmodule.cfoo

func_foobuild/foo/foo.cpython-38-x86_64-linux-gnu.sosip-install—target-dirsip-sdist

pyfoo-0.1.tar.gz

pyfoofoofoo

char*char*strfoochar*foo

char*pyproject.toml

pyfoo-0.1-cp38-cp38-manylinux1_x86_64.whl

—upgradefoopyfoo

Вызов функции

Рассмотрим полную версию программы с функцией:

def countFood():
    a = int(input())
    b = int(input())
    print("Всего", a+b, "шт.")
 
print("Сколько бананов и ананасов для обезьян?")
countFood()
 
print("Сколько жуков и червей для ежей?")
countFood()
 
print("Сколько рыб и моллюсков для выдр?")
countFood()

После вывода на экран каждого информационного сообщения осуществляется вызов функции, который выглядит просто как упоминание ее имени со скобками. Поскольку в функцию мы ничего не передаем скобки опять же пустые. В приведенном коде функция вызывается три раза.

Когда функция вызывается, поток выполнения программы переходит к ее определению и начинает исполнять ее тело. После того, как тело функции исполнено, поток выполнения возвращается в основной код в то место, где функция вызывалась. Далее исполняется следующее за вызовом выражение.

В языке Python определение функции должно предшествовать ее вызовам. Это связано с тем, что интерпретатор читает код строка за строкой и о том, что находится ниже по течению, ему еще неизвестно. Поэтому если вызов функции предшествует ее определению, то возникает ошибка (выбрасывается исключение NameError):

print("Сколько бананов и ананасов для обезьян?")
countFood()
 
print("Сколько жуков и червей для ежей?")
countFood()
 
print("Сколько рыб и моллюсков для выдр?")
countFood()
 
def countFood():
    a = int(input())
    b = int(input())
    print("Всего", a+b, "шт.")

Результат:

Сколько бананов и ананасов для обезьян?
Traceback (most recent call last):
  File "test.py", line 2, in <module>
    countFood()
NameError: name 'countFood' is not defined

Для многих компилируемых языков это не обязательное условие. Там можно определять и вызывать функцию в произвольных местах программы. Однако для удобочитаемости кода программисты даже в этом случае предпочитают соблюдать определенные правила.

The importance of real code

During the development of this PEP many people (supporters and critics both) have had a tendency to focus on toy examples on the one hand, and on overly complex examples on the other.

The danger of toy examples is twofold: they are often too abstract to make anyone go «ooh, that’s compelling», and they are easily refuted with «I would never write it that way anyway».

The danger of overly complex examples is that they provide a convenient strawman for critics of the proposal to shoot down («that’s obfuscated»).

Yet there is some use for both extremely simple and extremely complex examples: they are helpful to clarify the intended semantics. Therefore there will be some of each below.

However, in order to be compelling, examples should be rooted in real code, i.e. code that was written without any thought of this PEP, as part of a useful application, however large or small. Tim Peters has been extremely helpful by going over his own personal code repository and picking examples of code he had written that (in his view) would have been clearer if rewritten with (sparing) use of assignment expressions. His conclusion: the current proposal would have allowed a modest but clear improvement in quite a few bits of code.

Another use of real code is to observe indirectly how much value programmers place on compactness. Guido van Rossum searched through a Dropbox code base and discovered some evidence that programmers value writing fewer lines over shorter lines.

Case in point: Guido found several examples where a programmer repeated a subexpression, slowing down the program, in order to save one line of code, e.g. instead of writing:

match = re.match(data)
group = match.group(1) if match else None

they would write:

group = re.match(data).group(1) if re.match(data) else None

Another example illustrates that programmers sometimes do more work to save an extra level of indentation:

match1 = pattern1.match(data)
match2 = pattern2.match(data)
if match1:
    result = match1.group(1)
elif match2:
    result = match2.group(2)
else:
    result = None

This code tries to match pattern2 even if pattern1 has a match (in which case the match on pattern2 is never used). The more efficient rewrite would have been:

Customizing the % operator for your own classes

Of course you can customize how your own classes work when the operator is applied to them. Generally you should only use it to implement modulo operations! But that’s a guideline, not a hard rule.


Just to provide a simple example that shows how it works:

This example isn’t really useful, it just prints and then delegates the operator to the stored value, but it shows that is called when is applied to an instance:

Note that it also works for without explicitly needing to implement :

However you could also implement explicitly to overwrite the augmented assignment:

Now is explicitly overwritten to work in-place:

Как писать программы на Python

Интерактивный режим

Грубо говоря, интерпретатор выполняет команды построчно. Пишешь строку, нажимаешь Enter, интерпретатор выполняет ее, наблюдаешь результат.

Это удобно, когда изучаешь особенности языка или тестирует какую-нибудь небольшую часть кода. Ведь если работать на компилируемом языке, то пришлось бы сначала создать файл с кодом на исходном языке программирования, затем передать его компилятору, получить от него исполняемый файл и только потом выполнить программу и оценить результат. К счастью, даже в случае с компилируемыми языками все эти действия выполняет среда разработки, что упрощает жизнь программиста.

В операционных системах на базе ядра Linux можно программировать на Python в интерактивном режиме с помощью приложения «Терминал», в котором работает командная оболочка Bash. Здесь, чтобы запустить интерпретатор, надо выполнить команду .


Скорее всего запустится интерпретатор второй ветки Питона, что можно увидеть в первой информационной строке. В данном случае запустилась версия 2.7.12. Первое число «2» указывает на то, что это интерпретатор для языка программирования Python 2. Последняя строка с тремя угловыми скобками () – это приглашение для ввода команд. Поскольку в данном курсе будет использоваться язык Python 3, выйдем из интерпретатора с помощью команды (exit – выход). После чего выполним в терминале команду .

Есть вероятность, что пакет python3 может быть не установлен. Вам придется самостоятельно установить его.

Для операционных систем семейства Windows надо скачать интерпретатор с официального сайта языка (https://www.python.org/downloads/windows/). После установки он будет запускаться по ярлыку. Использовать командную оболочку здесь не требуется.

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

Бывает, что в процессе ввода была допущена ошибка или требуется повторить ранее используемую команду. Чтобы заново не вводить строку, в консоли можно прокручивать историю команд, используя для этого стрелки вверх и вних на клавиатуре. В среде IDLE (в Windows) для этого используются сочетания клавиш (скорее всего Alt+N и Alt+P).

Создание скриптов

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

Существует целый ряд сред разработки для Python, например, PyCharm. Однако на первое время подойдет текстовый редактор с подсветкой синтаксиса, например, Geany.


Здесь создается и сохраняется файл с кодом. Далее его можно запустить на выполнение через терминал. При этом сначала указывается интерпретатор (в данном случае ), потом имя файла (если файл находится в другом каталоге, то указывается с адресом, или надо перейти в этот каталог с помощью команды оболочки Bash).

Однако в Geany можно дополнительно установить встроенный терминал (), что упростит работу.

Наконец, в редакторе можно просто нажать F5, что отправит файл на исполнение (терминал откроется сам, после выполнения программы и нажатия Enter закроется).

В Windows подготовить файлы можно в той же среде IDLE. Для этого в меню следует выбрать команду File → New Window (Crtl + N), откроется чистое (без приглашения ) новое окно. Желательно сразу сохранить файл с расширением .py, чтобы появилась подсветка синтаксиса. После того как код будет подготовлен, снова сохраните файл. Запуск скрипта выполняется командой Run → Run Module (F5). После этого в окне интерактивного режима появится результат выполнения кода.

Переход от raw_input() к input()

В версиях 2.x языка функция читает пользовательский ввод из потока стандартного ввода (sys.stdin) и возвращает строку без завершающего символа новой строки. В следующем примере строка считывается из консоли с помощью функции и передается в переменную .

>>>quest = raw_input("What is your quest? ")
What is your quest? To seek the holy grail.
>>>quest
'To seek the holy grail.'

Другая функция чтения ввода — — в Python 2.x ожидает ввода корректного выражения Python, например 3+5.

Изначально предлагалось исключить как так и из встроенного пространства имен Python и сделать таким образом возможности ввода доступными только после импортирования. Однако такой подход сочли нежелательным с педагогической точки зрения; вместо простого кода:

>>>quest = input("What is your quest?")

пришлось бы писать:

>>>import sys
>>>print("What is your quest?")
>>>quest = sys.stdin.readline()

Последний вариант содержит гораздо больше кода для такой простой операции. Кроме того, его гораздо дольше объяснять новичкам: необходимо рассказать о модулях и их импортировании, о выводе строк и операторе ‘.’. (Все это подозрительно напоминает язык Java…) В итоге в Python 3 функция переименовали в , и теперь, чтобы получить данные со стандартного ввода не нужно ничего предварительно импортировать. Если же вам нужна функциональность, которую в версиях 2.x предоставляла функция , используйте выражение .

Дзен Питона

Если интерпретатору Питона дать команду («импортируй это» здесь видимо следует понимать как «импортируй самого себя»), то выведется так называемый «Дзен Питона», иллюстрирующий идеологию и особенности данного языка. Понимание смысла этих постулатов в приложении к программированию придет тогда, когда вы освоите язык в полной мере и приобретете опыт практического программирования.

  • Beautiful is better than ugly. Красивое лучше уродливого.
  • Explicit is better than implicit. Явное лучше неявного.
  • Simple is better than complex. Простое лучше сложного.
  • Complex is better than complicated. Сложное лучше усложнённого.
  • Flat is better than nested. Плоское лучше вложенного.
  • Sparse is better than dense. Разрежённое лучше плотного.
  • Readability counts. Удобочитаемость важна.
  • Special cases aren’t special enough to break the rules. Частные случаи не настолько существенны, чтобы нарушать правила.
  • Although practicality beats purity. Однако практичность важнее чистоты.
  • Errors should never pass silently. Ошибки никогда не должны замалчиваться.
  • Unless explicitly silenced. За исключением замалчивания, которое задано явно.
  • In the face of ambiguity, refuse the temptation to guess. Перед лицом неоднозначности сопротивляйтесь искушению угадать.
  • There should be one — and preferably only one — obvious way to do it. Должен существовать один — и, желательно, только один — очевидный способ сделать это.
  • Although that way may not be obvious at first unless you’re Dutch. Хотя он может быть с первого взгляда не очевиден, если ты не голландец.
  • Now is better than never. Сейчас лучше, чем никогда.
  • Although never is often better than *right* now. Однако, никогда чаще лучше, чем прямо сейчас.
  • If the implementation is hard to explain, it’s a bad idea. Если реализацию сложно объяснить — это плохая идея.
  • If the implementation is easy to explain, it may be a good idea. Если реализацию легко объяснить — это может быть хорошая идея.
  • Namespaces are one honking great idea — let’s do more of those! Пространства имён — прекрасная идея, давайте делать их больше!

Why not use a sublocal scope and prevent namespace pollution?

Previous revisions of this proposal involved sublocal scope (restricted to a single statement), preventing name leakage and namespace pollution. While a definite advantage in a number of situations, this increases complexity in many others, and the costs are not justified by the benefits. In the interests of language simplicity, the name bindings created here are exactly equivalent to any other name bindings, including that usage at class or module scope will create externally-visible names. This is no different from for loops or other constructs, and can be solved the same way: del the name once it is no longer needed, or prefix it with an underscore.


С этим читают