Взаимодействие с внешними приложениями#
Исполнение программ#
Запуск с ожиданием завершения#
Простейший метод запуска программы метод os.system
:
import os
ret = os.system("dir")
# ret - код завершения программы
# 0 - успешно, иначе ошибка
basics1.ipynb calc2.ipynb data2.ipynb gph.ipynb intro.md net.ipynb _templates
basics2.ipynb conf.py de.ipynb _images locale plot.ipynb
calc1.ipynb data1.ipynb ext.ipynb index.rst mt.ipynb _static
Вызов блокирующий - программа ждет завершения команды вызванной через os.system
.
Запуск с получением вывода#
import subprocess
output = subprocess.check_output(['ping', '127.0.0.1', '-c', '3'])
print(output.decode())
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.044 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.042 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.043 ms
--- 127.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2058ms
rtt min/avg/max/mdev = 0.042/0.043/0.044/0.000 ms
Вызов блокирующий - программа ждет завершения команды вызванной через subprocess.check_output
.
Запуск без ожидания завершения#
Метод subprocess.Popen
по умолчанию запускает процесс без ожидания завершения:
import subprocess
from time import sleep
q = subprocess.Popen("ping 127.0.0.1 -c 3", shell=True)
while q.poll() is None:
print('waiting...')
sleep(1)
print("Finished with code", q.poll())
waiting...
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.037 ms
waiting...
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.041 ms
waiting...
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.048 ms
--- 127.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2043ms
rtt min/avg/max/mdev = 0.037/0.042/0.048/0.004 ms
Finished with code 0
Что еще нужно знать о запуске внешних программ#
Если вы нажимаете Ctrl-C пока внешняя программа запущена через os.system, то этот сигнал уйдет ей, а не Python.
Можно взаимодействовать с интерактивными консольными приложениями пока те запущены через
Popen
. В частности их можно принудительно завершить или прочитать вывод до завершения самой программы.Большинство приложений с графическим интерфейсом в качестве первого и единственного аргумента принимают имя файла, который должен быть в них открыт.
Загрузка С библиотек#
Python может загружать библиотеки на С и вызывать функции из них. Для этого можно воспользоваться модулем стандартной библиотеки ctypes
или модулем cffi
.
Модуль ctypes требует явного описания сигнатуры (списка аргументов и возвращаемого значения) вызываемой функции на Python, cffi, же включает синтаксический анализатор C, благодаря которому может разбирать заголовочные файлы .h
и получать оттуда информацию о структурах.
Загрузка библиотеки и вызов функции с использованием ctypes#
import ctypes
# В *nix разделяемые библиотеки - файлы .so
lib = ctypes.CDLL('./mylib.so')
# В Windows - файлы .dll
lib = ctypes.CDLL('./mylib.dll')
# Вызов функции в ctypes
foo = lib.foo
foo.restype = ctypes.c_double
foo.argtypes = [ctypes.c_int, ctypes.c_double]
# Теперь функцию foo можно вызывать, как обычную функцию Python.
ret = foo(5, 3.2)
print(ret)
Некоторые популярные типы ctypes#
Тип ctypes |
Тип C |
Тип Python |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Так как строки в Python являются константами, для создания изменяемого массива типа c_char_p
используется метод create_string_buffer
. Подробнее в документации.
Загрузка библиотеки и вызов функции в cffi#
import cffi
# Создаем объект пространства имен cffi
ffi = cffi.FFI()
# Загружаем заголовочный файл
with open("mylib_header.h") as f:
ffi.cdef(f.read())
# Загружаем библиотеку
lib = ffi.dlopen("mylib.so")
# Вызываем функцию
result = lib.foo(5, 3.2)
# Создаем объект - структуру данных объявленную в заголовочном файле
my_data_struct = self.ffi.new("DataStruct *")
my_data_struct.a = 15 # Работаем с полями структуры на Python
ffi.buffer(my_data_struct) # Получаем bytes представление структуры
Что еще нужно знать о вызове С функций#
Написание критичных с точки зрения производительности участков кода на C — один из стандартных способов разогнать Python.
Ошибка в С библиотеке не может быть обработана на уровне Python и приведет к аварийному завершению интерпретатора.
Анализатор С pycparser который использует
cffi
поддерживает не все возможности языка С, а также не умеет обрабатывать директивы препроцессора (#include
,#ifdef
и т.п.).Для CPython можно писать модули С — CPython Extensions.