关于
python-interpreter的搜索结果
问题
如何为Ansible playbook选择python解释器
我有python2.7和python3.5在我的ansible服务器,而执行playbooks它是使用python2.7。我希望ansible使用python3.5当执行剧本。
in order:
1 have se...
kun坤
2019-12-28 14:09:13
0 浏览量
回答数 1
回答
貌似是两个python
应该是有虚拟环境吧,pycharm设置一下
看一下Run/Debug Configurations
选择一个安装了BeautifulSoup的Interpreter
如果找不到安装BeautifulSoup的Interpreter,在下面位置进行设置
File-->setting-->project-->project Interpreter
Pycharn的Python没有安装BeautifulSoup模块,两个方法。一,切换Pycharm的Python版本到有BeautifulSoup的。二,Pycharm选择的Python版本安装BeautifulSoup模块
很可能是IDE的解析环境里面没有BS4这个模块
爱吃鱼的程序员
2020-06-07 18:25:29
0 浏览量
回答数 0
回答
使用conda环境时,需要安装python.app软件包,然后用于pythonw运行使用wxPython的应用程序。
在OSX,命令行是:(如图所示这里)
conda install -c anaconda python.app
您将看到以下消息;确认继续。
The following packages will be SUPERSEDED by a higher-priority channel:
python.app: 2-py37_9 --> 2-py37_9 anaconda
使用pycharm,需要更改项目解释器:
->转到:
preferences
project interpreter
add interpreter
conda environment
anaconda (or anaconda3)
bin
pythonw
select pythonw
apply
ok
祖安文状元
2020-02-22 15:24:33
0 浏览量
回答数 0
问题
使用cmake查找正确的Python框架
我正在Snow Leopard计算机上使用python的macports版本,并使用cmake为其构建跨平台扩展。我在CMakeLists.txt中使用以下命令在系统上搜索python解释器和库
include(FindPy...
祖安文状元
2020-02-21 14:17:54
0 浏览量
回答数 1
问题
mkvirtualenv创建虚拟环境提示404
root@iZ2ze2b0psb88pn1gzr0brZ:~# mkvirtualenv myenvRunning virtualenv with interpreter /usr/bin/python2New python executa...
quq
2019-12-01 20:20:23
5340 浏览量
回答数 1
问题
阿里云Ubuntu 16.04无法建立虚拟环境virtualenv,提示资源不存在
阿里云轻量应用服务器,Ubuntu 16.04系统,pip是最新版本,使用sudo apt install virtualenv成功安装了virtualenv。当用virtualenv venv命令试图新建一个虚拟环境时,报如下错误。我查看...
summer_x_sun
2019-12-01 19:40:13
2037 浏览量
回答数 2
回答
要在pycharm下使用tensorflow,要设置好pycharm下解释器interpreter的路径,这里也就是tensorflow的路径。如果是虚拟的env,或anaconda的env,那就在interpreter路径里添加对应Python bin。如果当前路径里没有解释器没有这个,就点击右侧add local在电脑里找。anaconda2--->envs--->tensorflow---->bin---->python2.7(或者Python3.5,用哪个选哪个)后来我自己又装的时候,不知道怎么的tensorflow的env建在了一个叫 .conda/envs/tensorflow的路径下面,并且这个.conda文件夹是不可见的,但是可以在pycharm里面导入,也可以用。这里面原因我不太懂,要是有懂的朋友希望可以指点我一下。更新:这个问题是由于建立tensorflow 虚拟环境的时候,当前路径是在home目录下,cd到anaconda/envs目录就可以了。
“答案来源于网络,供您参考” 希望以上信息可以帮到您!
牧明
2019-12-02 02:16:39
0 浏览量
回答数 0
回答
1)有ANSIBLE_PYTHON_INTERPRETER配置参数需要设置: 2) Python on controller的版本取决于Ansible是如何构建的。例如
$ uname -a
FreeBSD master.example.com 12.1-RELEASE FreeBSD 12.1-RELEASE r354233 GENERIC amd64
$ pkg info | grep ansible
py27-ansible-2.8.5 Radically simple IT automation
py36-ansible-2.8.5 Radically simple IT automation
kun坤
2019-12-28 14:09:19
0 浏览量
回答数 0
问题
云服务器 ECS YUM 在线安装软件方法和常见问题处理
YUM 在线安装软件方法
1、通过如下格式指令进行软件的安装:
yum install -y
操作示例:
2、通过如下格式指令进行软件卸载:
yum rem...
boxti
2019-12-01 22:02:32
2644 浏览量
回答数 0
回答
对于简单的C代码,构建一个自定义扩展模块是很容易的。 作为第一步,你需要确保你的C代码有一个正确的头文件。例如:
/* sample.h */
#include
extern int gcd(int, int);
extern int in_mandel(double x0, double y0, int n);
extern int divide(int a, int b, int *remainder);
extern double avg(double *a, int n);
typedef struct Point {
double x,y;
} Point;
extern double distance(Point *p1, Point *p2);
通常来讲,这个头文件要对应一个已经被单独编译过的库。 有了这些,下面我们演示下编写扩展函数的一个简单例子:
#include "Python.h"
#include "sample.h"
/* int gcd(int, int) */
static PyObject *py_gcd(PyObject *self, PyObject *args) {
int x, y, result;
if (!PyArg_ParseTuple(args,"ii", &x, &y)) {
return NULL;
}
result = gcd(x,y);
return Py_BuildValue("i", result);
}
/* int in_mandel(double, double, int) */
static PyObject *py_in_mandel(PyObject *self, PyObject *args) {
double x0, y0;
int n;
int result;
if (!PyArg_ParseTuple(args, "ddi", &x0, &y0, &n)) {
return NULL;
}
result = in_mandel(x0,y0,n);
return Py_BuildValue("i", result);
}
/* int divide(int, int, int *) */
static PyObject *py_divide(PyObject *self, PyObject *args) {
int a, b, quotient, remainder;
if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
return NULL;
}
quotient = divide(a,b, &remainder);
return Py_BuildValue("(ii)", quotient, remainder);
}
/* Module method table */
static PyMethodDef SampleMethods[] = {
{"gcd", py_gcd, METH_VARARGS, "Greatest common divisor"},
{"in_mandel", py_in_mandel, METH_VARARGS, "Mandelbrot test"},
{"divide", py_divide, METH_VARARGS, "Integer division"},
{ NULL, NULL, 0, NULL}
};
/* Module structure */
static struct PyModuleDef samplemodule = {
PyModuleDef_HEAD_INIT,
"sample", /* name of module */
"A sample module", /* Doc string (may be NULL) */
-1, /* Size of per-interpreter state or -1 */
SampleMethods /* Method table */
};
/* Module initialization function */
PyMODINIT_FUNC
PyInit_sample(void) {
return PyModule_Create(&samplemodule);
}
要绑定这个扩展模块,像下面这样创建一个 setup.py 文件:
# setup.py
from distutils.core import setup, Extension
setup(name='sample',
ext_modules=[
Extension('sample',
['pysample.c'],
include_dirs = ['/some/dir'],
define_macros = [('FOO','1')],
undef_macros = ['BAR'],
library_dirs = ['/usr/local/lib'],
libraries = ['sample']
)
]
)
为了构建最终的函数库,只需简单的使用 python3 buildlib.py build_ext --inplace 命令即可:
bash % python3 setup.py build_ext --inplace
running build_ext
building 'sample' extension
gcc -fno-strict-aliasing -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes
-I/usr/local/include/python3.3m -c pysample.c
-o build/temp.macosx-10.6-x86_64-3.3/pysample.o
gcc -bundle -undefined dynamic_lookup
build/temp.macosx-10.6-x86_64-3.3/pysample.o \
-L/usr/local/lib -lsample -o sample.so
bash %
如上所示,它会创建一个名字叫 sample.so 的共享库。当被编译后,你就能将它作为一个模块导入进来了:
>>> import sample
>>> sample.gcd(35, 42)
7
>>> sample.in_mandel(0, 0, 500)
1
>>> sample.in_mandel(2.0, 1.0, 500)
0
>>> sample.divide(42, 8)
(5, 2)
>>>
如果你是在Windows机器上面尝试这些步骤,可能会遇到各种环境和编译问题,你需要花更多点时间去配置。 Python的二进制分发通常使用了Microsoft Visual Studio来构建。 为了让这些扩展能正常工作,你需要使用同样或兼容的工具来编译它。 参考相应的 Python文档
哦哦喔
2020-04-17 18:10:44
0 浏览量
回答数 0
回答
你不能 这是那些想了解的人的理由:
1st- Python代码由其解释器执行,该解释器是用C编写的程序。(Python软件基金会的Interpreter恰当地称为“ Cpython”,以区别于也可以运行Python代码的第三方解释器)。Python代码由Cpython解释,并将其内容显示在python控制台中,例如“ Idle”或“ Terminal Emulator”,甚至直接在计算机窗口的屏幕上显示为图形,使用“ X11” [X Windows for Linux Unix] ,“ Quartz Compositor MacX” [Macintosh]或“ DWM” [停止Windows的Windows管理器]。(Cpython具有3个基本版本,每个版本都适用于Mac,Windows和Unix / Linux)
2-您也可以将Web浏览器视为程序解释器。但是它不能解释Python代码。他们只知道如何解释HTML,CSS和Javascript。但是,Web浏览器不像被馈送到第三方控制台或计算机Windowing System那样,而是像虚拟机一样工作,因为它可以在其自己的显示环境Web浏览器中解释和显示结果。不幸的是,这些浏览器都没有被编程来解释Python代码。
第三-Python 是一种非常高级的语言,这意味着它是由低级语言设计的。在发布Python之前必须有C。在发布C之前必须有Assembly。在进行汇编之前,必须要有机器语言。计算机代码的复杂性可以与生物学相提并论。Javascript也已经非常高度地发展了,但是它的发展完全取决于Web浏览器。Python的发展取决于终端仿真器和计算机的Windowing System [X11,MacX或DWM]
祖安文状元
2020-02-22 17:54:34
0 浏览量
回答数 0
回答
一、打开eclipse找到help中的Install New Software
二、点击add
然后在Name:后面输入pydev,
LocalHost:http://pydev.org/updates
然后点击ok
等待一会,
选中PyDev之后,点next,等待安装。
安装好pydev后,需要安装配置python解释器。菜单栏点击Windows ->Preferences,在检索栏输入python interpreter,点击new找到自己安装python的路径,再点击ok。安装就完成了
寒喵
2019-12-02 01:08:28
0 浏览量
回答数 0
问题
用python访问hive,只有select *执行成功,其他sql语句(select count(*))报错
WARN 2017-10-25 14:09:27,635 - Job 20171024-092857_1502983815 is finished, status: ERROR, exception: null, result: %text...
总是被人坑
2019-12-01 19:25:22
1742 浏览量
回答数 1
回答
1. 在C语言中调用Python非常简单,不过设计到一些小窍门。 下面的C代码告诉你怎样安全的调用:
#include
/* Execute func(x,y) in the Python interpreter. The
arguments and return result of the function must
be Python floats */
double call_func(PyObject *func, double x, double y) {
PyObject *args;
PyObject *kwargs;
PyObject *result = 0;
double retval;
/* Make sure we own the GIL */
PyGILState_STATE state = PyGILState_Ensure();
/* Verify that func is a proper callable */
if (!PyCallable_Check(func)) {
fprintf(stderr,"call_func: expected a callable\n");
goto fail;
}
/* Build arguments */
args = Py_BuildValue("(dd)", x, y);
kwargs = NULL;
/* Call the function */
result = PyObject_Call(func, args, kwargs);
Py_DECREF(args);
Py_XDECREF(kwargs);
/* Check for Python exceptions (if any) */
if (PyErr_Occurred()) {
PyErr_Print();
goto fail;
}
/* Verify the result is a float object */
if (!PyFloat_Check(result)) {
fprintf(stderr,"call_func: callable didn't return a float\n");
goto fail;
}
/* Create the return value */
retval = PyFloat_AsDouble(result);
Py_DECREF(result);
/* Restore previous GIL state and return */
PyGILState_Release(state);
return retval;
fail:
Py_XDECREF(result);
PyGILState_Release(state);
abort(); // Change to something more appropriate
}
要使用这个函数,你需要获取传递过来的某个已存在Python调用的引用。 有很多种方法可以让你这样做, 比如将一个可调用对象传给一个扩展模块或直接写C代码从已存在模块中提取出来。
下面是一个简单例子用来掩饰从一个嵌入的Python解释器中调用一个函数:
#include
/* Definition of call_func() same as above */
...
/* Load a symbol from a module */
PyObject *import_name(const char *modname, const char *symbol) {
PyObject *u_name, *module;
u_name = PyUnicode_FromString(modname);
module = PyImport_Import(u_name);
Py_DECREF(u_name);
return PyObject_GetAttrString(module, symbol);
}
/* Simple embedding example */
int main() {
PyObject *pow_func;
double x;
Py_Initialize();
/* Get a reference to the math.pow function */
pow_func = import_name("math","pow");
/* Call it using our call_func() code */
for (x = 0.0; x < 10.0; x += 0.1) {
printf("%0.2f %0.2f\n", x, call_func(pow_func,x,2.0));
}
/* Done */
Py_DECREF(pow_func);
Py_Finalize();
return 0;
}
要构建例子代码,你需要编译C并将它链接到Python解释器。 下面的Makefile可以教你怎样做(不过在你机器上面需要一些配置)。
all::
cc -g embed.c -I/usr/local/include/python3.3m \
-L/usr/local/lib/python3.3/config-3.3m -lpython3.3m
编译并运行会产生类似下面的输出:
0.00 0.00
0.10 0.01
0.20 0.04
0.30 0.09
0.40 0.16
...
下面是一个稍微不同的例子,展示了一个扩展函数, 它接受一个可调用对象和其他参数,并将它们传递给 call_func() 来做测试:
/* Extension function for testing the C-Python callback */
PyObject *py_call_func(PyObject *self, PyObject *args) {
PyObject *func;
double x, y, result;
if (!PyArg_ParseTuple(args,"Odd", &func,&x,&y)) {
return NULL;
}
result = call_func(func, x, y);
return Py_BuildValue("d", result);
}
使用这个扩展函数,你要像下面这样测试它:
>>> import sample
>>> def add(x,y):
... return x+y
...
>>> sample.call_func(add,3,4)
7.0
>>>
哦哦喔
2020-04-17 18:12:35
0 浏览量
回答数 0
回答
在批评Python的讨论中,常常说起Python多线程是多么的难用。还有人对 global interpreter lock(也被亲切的称为“GIL”)指指点点,说它阻碍了Python的多线程程序同时运行。因此,如果你是从其他语言(比如C++或Java)转过来的话,Python线程模块并不会像你想象的那样去运行。必须要说明的是,我们还是可以用Python写出能并发或并行的代码,并且能带来性能的显著提升,只要你能顾及到一些事情。如果你还没看过的话,我建议你看看Eqbal Quran的文章《Ruby中的并发和并行》。
在本文中,我们将会写一个小的Python脚本,用于下载Imgur上最热门的图片。我们将会从一个按顺序下载图片的版本开始做起,即一个一个地下载。在那之前,你得注册一个Imgur上的应用。如果你还没有Imgur账户,请先注册一个。
本文中的脚本在Python3.4.2中测试通过。稍微改一下,应该也能在Python2中运行——urllib是两个版本中区别最大的部分。
1、开始动手让我们从创建一个叫“download.py”的Python模块开始。这个文件包含了获取图片列表以及下载这些图片所需的所有函数。我们将这些功能分成三个单独的函数:
get_links
download_link
setup_download_dir
第三个函数,“setup_download_dir”,用于创建下载的目标目录(如果不存在的话)。
Imgur的API要求HTTP请求能支持带有client ID的“Authorization”头部。你可以从你注册的Imgur应用的面板上找到这个client ID,而响应会以JSON进行编码。我们可以使用Python的标准JSON库去解码。下载图片更简单,你只需要根据它们的URL获取图片,然后写入到一个文件即可。
代码如下:
import jsonimport loggingimport osfrom pathlib import Pathfrom urllib.request import urlopen, Request
logger = logging.getLogger(__name__)
def get_links(client_id): headers = {'Authorization': 'Client-ID {}'.format(client_id)} req = Request('https://api.imgur.com/3/gallery/', headers=headers, method='GET') with urlopen(req) as resp:
data = json.loads(resp.readall().decode('utf-8'))
return map(lambda item: item['link'], data['data'])
def download_link(directory, link): logger.info('Downloading %s', link) download_path = directory / os.path.basename(link) with urlopen(link) as image, download_path.open('wb') as f:
f.write(image.readall())
def setup_download_dir(): download_dir = Path('images') if not download_dir.exists():
download_dir.mkdir()
return download_dir接下来,你需要写一个模块,利用这些函数去逐个下载图片。我们给它命名为“single.py”。它包含了我们最原始版本的Imgur图片下载器的主要函数。这个模块将会通过环境变量“IMGUR_CLIENT_ID”去获取Imgur的client ID。它将会调用“setup_download_dir”去创建下载目录。最后,使用get_links函数去获取图片的列表,过滤掉所有的GIF和专辑URL,然后用“download_link”去将图片下载并保存在磁盘中。下面是“single.py”的代码:
import loggingimport osfrom time import time
from download import setup_download_dir, get_links, download_link
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')logging.getLogger('requests').setLevel(logging.CRITICAL)logger = logging.getLogger(__name__)
def main(): ts = time() client_id = os.getenv('IMGUR_CLIENT_ID') if not client_id:
raise Exception("Couldn't find IMGUR_CLIENT_ID environment variable!")
download_dir = setup_download_dir() links = [l for l in get_links(client_id) if l.endswith('.jpg')] for link in links:
download_link(download_dir, link)
print('Took {}s'.format(time() - ts))
if name == '__main__': main()注:为了测试方便,上面两段代码可以用如下代码替代演示:
coding=utf-8
测试utf-8编码
from time import sleep, timeimport sys, threading
reload(sys)sys.setdefaultencoding('utf-8')
def getNums(N):
return xrange(N)
def processNum(num):
num_add = num + 1
sleep(1)
print str(threading.current_thread()) + ": " + str(num) + " → " + str(num_add)
if name == "__main__":
t1 = time()
for i in getNums(3):
processNum(i)
print "cost time is: {:.2f}s".format(time() - t1)
结果:
<_mainthread started>: 0 → 1<_mainthread started>: 1 → 2<_mainthread started>: 2 → 3cost time is: 3.00s在我的笔记本上,这个脚本花了19.4秒去下载91张图片。请注意这些数字在不同的网络上也会有所不同。19.4秒并不是非常的长,但是如果我们要下载更多的图片怎么办呢?或许是900张而不是90张。平均下载一张图片要0.2秒,900张的话大概需要3分钟。那么9000张图片将会花掉30分钟。好消息是使用了并发或者并行后,我们可以将这个速度显著地提高。
接下来的代码示例将只会显示导入特有模块和新模块的import语句。所有相关的Python脚本都可以在这方便地找到this GitHub repository。
2、使用线程线程是最出名的实现并发和并行的方式之一。操作系统一般提供了线程的特性。线程比进程要小,而且共享同一块内存空间。
在这里,我们将写一个替代“single.py”的新模块。它将创建一个有八个线程的池,加上主线程的话总共就是九个线程。之所以是八个线程,是因为我的电脑有8个CPU内核,而一个工作线程对应一个内核看起来还不错。在实践中,线程的数量是仔细考究的,需要考虑到其他的因素,比如在同一台机器上跑的的其他应用和服务。
下面的脚本几乎跟之前的一样,除了我们现在有个新的类,DownloadWorker,一个Thread类的子类。运行无限循环的run方法已经被重写。在每次迭代时,它调用“self.queue.get()”试图从一个线程安全的队列里获取一个URL。它将会一直堵塞,直到队列中出现一个要处理元素。一旦工作线程从队列中得到一个元素,它将会调用之前脚本中用来下载图片到目录中所用到的“download_link”方法。下载完成之后,工作线程向队列发送任务完成的信号。这非常重要,因为队列一直在跟踪队列中的任务数。如果工作线程没有发出任务完成的信号,“queue.join()”的调用将会令整个主线程都在阻塞状态。
from queue import Queuefrom threading import Thread
class DownloadWorker(Thread): def __init__(self, queue):
Thread.__init__(self)
self.queue = queue
def run(self):
while True:
# Get the work from the queue and expand the tuple
# 从队列中获取任务并扩展tuple
directory, link = self.queue.get()
download_link(directory, link)
self.queue.task_done()
def main(): ts = time() client_id = os.getenv('IMGUR_CLIENT_ID') if not client_id:
raise Exception("Couldn't find IMGUR_CLIENT_ID environment variable!")
download_dir = setup_download_dir() links = [l for l in get_links(client_id) if l.endswith('.jpg')] # Create a queue to communicate with the worker threads queue = Queue() # Create 8 worker threads # 创建八个工作线程 for x in range(8):
worker = DownloadWorker(queue)
# Setting daemon to True will let the main thread exit even though the workers are blocking
# 将daemon设置为True将会使主线程退出,即使worker都阻塞了
worker.daemon = True
worker.start()
# Put the tasks into the queue as a tuple # 将任务以tuple的形式放入队列中 for link in links:
logger.info('Queueing {}'.format(link))
queue.put((download_dir, link))
# Causes the main thread to wait for the queue to finish processing all the tasks # 让主线程等待队列完成所有的任务 queue.join() print('Took {}'.format(time() - ts))注:为了测试方便,上面的代码可以用如下代码替代演示:
coding=utf-8
测试utf-8编码
from Queue import Queuefrom threading import Threadfrom single import *import sys
reload(sys)sys.setdefaultencoding('utf-8')
class ProcessWorker(Thread):
def __init__(self, queue):
Thread.__init__(self)
self.queue = queue
def run(self):
while True:
# Get the work from the queue
num = self.queue.get()
processNum(num)
self.queue.task_done()
def main():
ts = time()
nums = getNums(4)
# Create a queue to communicate with the worker threads
queue = Queue()
# Create 4 worker threads
# 创建四个工作线程
for x in range(4):
worker = ProcessWorker(queue)
# Setting daemon to True will let the main thread exit even though the workers are blocking
# 将daemon设置为True将会使主线程退出,即使worker都阻塞了
worker.daemon = True
worker.start()
# Put the tasks into the queue
for num in nums:
queue.put(num)
# Causes the main thread to wait for the queue to finish processing all the tasks
# 让主线程等待队列完成所有的任务
queue.join()
print("cost time is: {:.2f}s".format(time() - ts))
if name == "__main__":
main()
结果:
: 3 → 4: 2 → 3: 1 → 2
: 0 → 1cost time is: 1.01s在同一个机器上运行这个脚本,下载时间变成了4.1秒!即比之前的例子快4.7倍。虽然这快了很多,但还是要提一下,由于GIL的缘故,在这个进程中同一时间只有一个线程在运行。因此,这段代码是并发的但不是并行的。而它仍然变快的原因是这是一个IO密集型的任务。进程下载图片时根本毫不费力,而主要的时间都花在了等待网络上。这就是为什么线程可以提供很大的速度提升。每当线程中的一个准备工作时,进程可以不断转换线程。使用Python或其他有GIL的解释型语言中的线程模块实际上会降低性能。如果你的代码执行的是CPU密集型的任务,例如解压gzip文件,使用线程模块将会导致执行时间变长。对于CPU密集型任务和真正的并行执行,我们可以使用多进程(multiprocessing)模块。
官方的Python实现——CPython——带有GIL,但不是所有的Python实现都是这样的。比如,IronPython,使用.NET框架实现的Python就没有GIL,基于Java实现的Jython也同样没有。你可以点这查看现有的Python实现。
3、生成多进程多进程模块比线程模块更易使用,因为我们不需要像线程示例那样新增一个类。我们唯一需要做的改变在主函数中。
为了使用多进程,我们得建立一个多进程池。通过它提供的map方法,我们把URL列表传给池,然后8个新进程就会生成,它们将并行地去下载图片。这就是真正的并行,不过这是有代价的。整个脚本的内存将会被拷贝到各个子进程中。在我们的例子中这不算什么,但是在大型程序中它很容易导致严重的问题。
from functools import partialfrom multiprocessing.pool import Pool
def main(): ts = time() client_id = os.getenv('IMGUR_CLIENT_ID') if not client_id:
raise Exception("Couldn't find IMGUR_CLIENT_ID environment variable!")
download_dir = setup_download_dir() links = [l for l in get_links(client_id) if l.endswith('.jpg')] download = partial(download_link, download_dir) with Pool(8) as p:
p.map(download, links)
print('Took {}s'.format(time() - ts))注:为了测试方便,上面的代码可以用如下代码替代演示:
coding=utf-8
测试utf-8编码
from functools import partialfrom multiprocessing.pool import Poolfrom single import *from time import time
import sys
reload(sys)sys.setdefaultencoding('utf-8')
def main():
ts = time()
nums = getNums(4)
p = Pool(4)
p.map(processNum, nums)
print("cost time is: {:.2f}s".format(time() - ts))
if name == "__main__":
main()
结果:
<_mainthread started>: 0 → 1<_mainthread started>: 1 → 2<_mainthread started>: 3 → 4<_mainthread started>: 2 → 3
cost time is: 1.21s4、分布式任务你已经知道了线程和多进程模块可以给你自己的电脑跑脚本时提供很大的帮助,那么在你想要在不同的机器上执行任务,或者在你需要扩大规模而超过一台机器的的能力范围时,你该怎么办呢?一个很好的使用案例是网络应用的长时间后台任务。如果你有一些很耗时的任务,你不会希望在同一台机器上占用一些其他的应用代码所需要的子进程或线程。这将会使你的应用的性能下降,影响到你的用户们。如果能在另外一台甚至很多台其他的机器上跑这些任务就好了。
Python库RQ非常适用于这类任务。它是一个简单却很强大的库。首先将一个函数和它的参数放入队列中。它将函数调用的表示序列化(pickle),然后将这些表示添加到一个Redis列表中。任务进入队列只是第一步,什么都还没有做。我们至少还需要一个能去监听任务队列的worker(工作线程)。
第一步是在你的电脑上安装和使用Redis服务器,或是拥有一台能正常的使用的Redis服务器的使用权。接着,对于现有的代码只需要一些小小的改动。先创建一个RQ队列的实例并通过redis-py 库传给一台Redis服务器。然后,我们执行“q.enqueue(download_link, download_dir, link)”,而不只是调用“download_link” 。enqueue方法的第一个参数是一个函数,当任务真正执行时,其他的参数或关键字参数将会传给该函数。
最后一步是启动一些worker。RQ提供了方便的脚本,可以在默认队列上运行起worker。只要在终端窗口中执行“rqworker”,就可以开始监听默认队列了。请确认你当前的工作目录与脚本所在的是同一个。如果你想监听别的队列,你可以执行“rqworker queue_name”,然后将会开始执行名为queue_name的队列。RQ的一个很好的点就是,只要你可以连接到Redis,你就可以在任意数量上的机器上跑起任意数量的worker;因此,它可以让你的应用扩展性得到提升。下面是RQ版本的代码:
from redis import Redisfrom rq import Queue
def main(): client_id = os.getenv('IMGUR_CLIENT_ID') if not client_id:
raise Exception("Couldn't find IMGUR_CLIENT_ID environment variable!")
download_dir = setup_download_dir() links = [l for l in get_links(client_id) if l.endswith('.jpg')] q = Queue(connection=Redis(host='localhost', port=6379)) for link in links:
q.enqueue(download_link, download_dir, link)
然而RQ并不是Python任务队列的唯一解决方案。RQ确实易用并且能在简单的案例中起到很大的作用,但是如果有更高级的需求,我们可以使用其他的解决方案(例如 Celery)。
5、总结如果你的代码是IO密集型的,线程和多进程可以帮到你。多进程比线程更易用,但是消耗更多的内存。如果你的代码是CPU密集型的,多进程就明显是更好的选择——特别是所使用的机器是多核或多CPU的。对于网络应用,在你需要扩展到多台机器上执行任务,RQ是更好的选择。
6、注:关于并发、并行区别与联系并发是指,程序在运行的过程中存在多于一个的执行上下文。这些执行上下文一般对应着不同的调用栈。
在单处理器上,并发程序虽然有多个上下文运行环境,但某一个时刻只有一个任务在运行。
但在多处理器上,因为有了多个执行单元,就可以同时有数个任务在跑。
这种物理上同一时刻有多个任务同时运行的方式就是并行。
和并发相比,并行更加强调多个任务同时在运行。
而且并行还有一个层次问题,比如是指令间的并行还是任务间的并行。
xuning715
2019-12-02 01:10:11
0 浏览量
回答数 0
回答
在批评Python的讨论中,常常说起Python多线程是多么的难用。还有人对 global interpreter lock(也被亲切的称为“GIL”)指指点点,说它阻碍了Python的多线程程序同时运行。因此,如果你是从其他语言(比如C++或Java)转过来的话,Python线程模块并不会像你想象的那样去运行。必须要说明的是,我们还是可以用Python写出能并发或并行的代码,并且能带来性能的显著提升,只要你能顾及到一些事情。如果你还没看过的话,我建议你看看Eqbal Quran的文章《Ruby中的并发和并行》。
在本文中,我们将会写一个小的Python脚本,用于下载Imgur上最热门的图片。我们将会从一个按顺序下载图片的版本开始做起,即一个一个地下载。在那之前,你得注册一个Imgur上的应用。如果你还没有Imgur账户,请先注册一个。
本文中的脚本在Python3.4.2中测试通过。稍微改一下,应该也能在Python2中运行——urllib是两个版本中区别最大的部分。
1、开始动手让我们从创建一个叫“download.py”的Python模块开始。这个文件包含了获取图片列表以及下载这些图片所需的所有函数。我们将这些功能分成三个单独的函数:
get_links
download_link
setup_download_dir
第三个函数,“setup_download_dir”,用于创建下载的目标目录(如果不存在的话)。
Imgur的API要求HTTP请求能支持带有client ID的“Authorization”头部。你可以从你注册的Imgur应用的面板上找到这个client ID,而响应会以JSON进行编码。我们可以使用Python的标准JSON库去解码。下载图片更简单,你只需要根据它们的URL获取图片,然后写入到一个文件即可。
代码如下:
import jsonimport loggingimport osfrom pathlib import Pathfrom urllib.request import urlopen, Request
logger = logging.getLogger(__name__)
def get_links(client_id): headers = {'Authorization': 'Client-ID {}'.format(client_id)} req = Request('https://api.imgur.com/3/gallery/', headers=headers, method='GET') with urlopen(req) as resp:
data = json.loads(resp.readall().decode('utf-8'))
return map(lambda item: item['link'], data['data'])
def download_link(directory, link): logger.info('Downloading %s', link) download_path = directory / os.path.basename(link) with urlopen(link) as image, download_path.open('wb') as f:
f.write(image.readall())
def setup_download_dir(): download_dir = Path('images') if not download_dir.exists():
download_dir.mkdir()
return download_dir接下来,你需要写一个模块,利用这些函数去逐个下载图片。我们给它命名为“single.py”。它包含了我们最原始版本的Imgur图片下载器的主要函数。这个模块将会通过环境变量“IMGUR_CLIENT_ID”去获取Imgur的client ID。它将会调用“setup_download_dir”去创建下载目录。最后,使用get_links函数去获取图片的列表,过滤掉所有的GIF和专辑URL,然后用“download_link”去将图片下载并保存在磁盘中。下面是“single.py”的代码:
import loggingimport osfrom time import time
from download import setup_download_dir, get_links, download_link
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')logging.getLogger('requests').setLevel(logging.CRITICAL)logger = logging.getLogger(__name__)
def main(): ts = time() client_id = os.getenv('IMGUR_CLIENT_ID') if not client_id:
raise Exception("Couldn't find IMGUR_CLIENT_ID environment variable!")
download_dir = setup_download_dir() links = [l for l in get_links(client_id) if l.endswith('.jpg')] for link in links:
download_link(download_dir, link)
print('Took {}s'.format(time() - ts))
if name == '__main__': main()注:为了测试方便,上面两段代码可以用如下代码替代演示:
coding=utf-8
测试utf-8编码
from time import sleep, timeimport sys, threading
reload(sys)sys.setdefaultencoding('utf-8')
def getNums(N):
return xrange(N)
def processNum(num):
num_add = num + 1
sleep(1)
print str(threading.current_thread()) + ": " + str(num) + " → " + str(num_add)
if name == "__main__":
t1 = time()
for i in getNums(3):
processNum(i)
print "cost time is: {:.2f}s".format(time() - t1)
结果:
<_mainthread started>: 0 → 1<_mainthread started>: 1 → 2<_mainthread started>: 2 → 3cost time is: 3.00s在我的笔记本上,这个脚本花了19.4秒去下载91张图片。请注意这些数字在不同的网络上也会有所不同。19.4秒并不是非常的长,但是如果我们要下载更多的图片怎么办呢?或许是900张而不是90张。平均下载一张图片要0.2秒,900张的话大概需要3分钟。那么9000张图片将会花掉30分钟。好消息是使用了并发或者并行后,我们可以将这个速度显著地提高。
接下来的代码示例将只会显示导入特有模块和新模块的import语句。所有相关的Python脚本都可以在这方便地找到this GitHub repository。
2、使用线程线程是最出名的实现并发和并行的方式之一。操作系统一般提供了线程的特性。线程比进程要小,而且共享同一块内存空间。
在这里,我们将写一个替代“single.py”的新模块。它将创建一个有八个线程的池,加上主线程的话总共就是九个线程。之所以是八个线程,是因为我的电脑有8个CPU内核,而一个工作线程对应一个内核看起来还不错。在实践中,线程的数量是仔细考究的,需要考虑到其他的因素,比如在同一台机器上跑的的其他应用和服务。
下面的脚本几乎跟之前的一样,除了我们现在有个新的类,DownloadWorker,一个Thread类的子类。运行无限循环的run方法已经被重写。在每次迭代时,它调用“self.queue.get()”试图从一个线程安全的队列里获取一个URL。它将会一直堵塞,直到队列中出现一个要处理元素。一旦工作线程从队列中得到一个元素,它将会调用之前脚本中用来下载图片到目录中所用到的“download_link”方法。下载完成之后,工作线程向队列发送任务完成的信号。这非常重要,因为队列一直在跟踪队列中的任务数。如果工作线程没有发出任务完成的信号,“queue.join()”的调用将会令整个主线程都在阻塞状态。
from queue import Queuefrom threading import Thread
class DownloadWorker(Thread): def __init__(self, queue):
Thread.__init__(self)
self.queue = queue
def run(self):
while True:
# Get the work from the queue and expand the tuple
# 从队列中获取任务并扩展tuple
directory, link = self.queue.get()
download_link(directory, link)
self.queue.task_done()
def main(): ts = time() client_id = os.getenv('IMGUR_CLIENT_ID') if not client_id:
raise Exception("Couldn't find IMGUR_CLIENT_ID environment variable!")
download_dir = setup_download_dir() links = [l for l in get_links(client_id) if l.endswith('.jpg')] # Create a queue to communicate with the worker threads queue = Queue() # Create 8 worker threads # 创建八个工作线程 for x in range(8):
worker = DownloadWorker(queue)
# Setting daemon to True will let the main thread exit even though the workers are blocking
# 将daemon设置为True将会使主线程退出,即使worker都阻塞了
worker.daemon = True
worker.start()
# Put the tasks into the queue as a tuple # 将任务以tuple的形式放入队列中 for link in links:
logger.info('Queueing {}'.format(link))
queue.put((download_dir, link))
# Causes the main thread to wait for the queue to finish processing all the tasks # 让主线程等待队列完成所有的任务 queue.join() print('Took {}'.format(time() - ts))注:为了测试方便,上面的代码可以用如下代码替代演示:
coding=utf-8
测试utf-8编码
from Queue import Queuefrom threading import Threadfrom single import *import sys
reload(sys)sys.setdefaultencoding('utf-8')
class ProcessWorker(Thread):
def __init__(self, queue):
Thread.__init__(self)
self.queue = queue
def run(self):
while True:
# Get the work from the queue
num = self.queue.get()
processNum(num)
self.queue.task_done()
def main():
ts = time()
nums = getNums(4)
# Create a queue to communicate with the worker threads
queue = Queue()
# Create 4 worker threads
# 创建四个工作线程
for x in range(4):
worker = ProcessWorker(queue)
# Setting daemon to True will let the main thread exit even though the workers are blocking
# 将daemon设置为True将会使主线程退出,即使worker都阻塞了
worker.daemon = True
worker.start()
# Put the tasks into the queue
for num in nums:
queue.put(num)
# Causes the main thread to wait for the queue to finish processing all the tasks
# 让主线程等待队列完成所有的任务
queue.join()
print("cost time is: {:.2f}s".format(time() - ts))
if name == "__main__":
main()
结果:
: 3 → 4: 2 → 3: 1 → 2
: 0 → 1cost time is: 1.01s在同一个机器上运行这个脚本,下载时间变成了4.1秒!即比之前的例子快4.7倍。虽然这快了很多,但还是要提一下,由于GIL的缘故,在这个进程中同一时间只有一个线程在运行。因此,这段代码是并发的但不是并行的。而它仍然变快的原因是这是一个IO密集型的任务。进程下载图片时根本毫不费力,而主要的时间都花在了等待网络上。这就是为什么线程可以提供很大的速度提升。每当线程中的一个准备工作时,进程可以不断转换线程。使用Python或其他有GIL的解释型语言中的线程模块实际上会降低性能。如果你的代码执行的是CPU密集型的任务,例如解压gzip文件,使用线程模块将会导致执行时间变长。对于CPU密集型任务和真正的并行执行,我们可以使用多进程(multiprocessing)模块。
官方的Python实现——CPython——带有GIL,但不是所有的Python实现都是这样的。比如,IronPython,使用.NET框架实现的Python就没有GIL,基于Java实现的Jython也同样没有。你可以点这查看现有的Python实现。
3、生成多进程多进程模块比线程模块更易使用,因为我们不需要像线程示例那样新增一个类。我们唯一需要做的改变在主函数中。
为了使用多进程,我们得建立一个多进程池。通过它提供的map方法,我们把URL列表传给池,然后8个新进程就会生成,它们将并行地去下载图片。这就是真正的并行,不过这是有代价的。整个脚本的内存将会被拷贝到各个子进程中。在我们的例子中这不算什么,但是在大型程序中它很容易导致严重的问题。
from functools import partialfrom multiprocessing.pool import Pool
def main(): ts = time() client_id = os.getenv('IMGUR_CLIENT_ID') if not client_id:
raise Exception("Couldn't find IMGUR_CLIENT_ID environment variable!")
download_dir = setup_download_dir() links = [l for l in get_links(client_id) if l.endswith('.jpg')] download = partial(download_link, download_dir) with Pool(8) as p:
p.map(download, links)
print('Took {}s'.format(time() - ts))注:为了测试方便,上面的代码可以用如下代码替代演示:
coding=utf-8
测试utf-8编码
from functools import partialfrom multiprocessing.pool import Poolfrom single import *from time import time
import sys
reload(sys)sys.setdefaultencoding('utf-8')
def main():
ts = time()
nums = getNums(4)
p = Pool(4)
p.map(processNum, nums)
print("cost time is: {:.2f}s".format(time() - ts))
if name == "__main__":
main()
结果:
<_mainthread started>: 0 → 1<_mainthread started>: 1 → 2<_mainthread started>: 3 → 4<_mainthread started>: 2 → 3
cost time is: 1.21s4、分布式任务你已经知道了线程和多进程模块可以给你自己的电脑跑脚本时提供很大的帮助,那么在你想要在不同的机器上执行任务,或者在你需要扩大规模而超过一台机器的的能力范围时,你该怎么办呢?一个很好的使用案例是网络应用的长时间后台任务。如果你有一些很耗时的任务,你不会希望在同一台机器上占用一些其他的应用代码所需要的子进程或线程。这将会使你的应用的性能下降,影响到你的用户们。如果能在另外一台甚至很多台其他的机器上跑这些任务就好了。
Python库RQ非常适用于这类任务。它是一个简单却很强大的库。首先将一个函数和它的参数放入队列中。它将函数调用的表示序列化(pickle),然后将这些表示添加到一个Redis列表中。任务进入队列只是第一步,什么都还没有做。我们至少还需要一个能去监听任务队列的worker(工作线程)。
第一步是在你的电脑上安装和使用Redis服务器,或是拥有一台能正常的使用的Redis服务器的使用权。接着,对于现有的代码只需要一些小小的改动。先创建一个RQ队列的实例并通过redis-py 库传给一台Redis服务器。然后,我们执行“q.enqueue(download_link, download_dir, link)”,而不只是调用“download_link” 。enqueue方法的第一个参数是一个函数,当任务真正执行时,其他的参数或关键字参数将会传给该函数。
最后一步是启动一些worker。RQ提供了方便的脚本,可以在默认队列上运行起worker。只要在终端窗口中执行“rqworker”,就可以开始监听默认队列了。请确认你当前的工作目录与脚本所在的是同一个。如果你想监听别的队列,你可以执行“rqworker queue_name”,然后将会开始执行名为queue_name的队列。RQ的一个很好的点就是,只要你可以连接到Redis,你就可以在任意数量上的机器上跑起任意数量的worker;因此,它可以让你的应用扩展性得到提升。下面是RQ版本的代码:
from redis import Redisfrom rq import Queue
def main(): client_id = os.getenv('IMGUR_CLIENT_ID') if not client_id:
raise Exception("Couldn't find IMGUR_CLIENT_ID environment variable!")
download_dir = setup_download_dir() links = [l for l in get_links(client_id) if l.endswith('.jpg')] q = Queue(connection=Redis(host='localhost', port=6379)) for link in links:
q.enqueue(download_link, download_dir, link)
然而RQ并不是Python任务队列的唯一解决方案。RQ确实易用并且能在简单的案例中起到很大的作用,但是如果有更高级的需求,我们可以使用其他的解决方案(例如 Celery)。
5、总结如果你的代码是IO密集型的,线程和多进程可以帮到你。多进程比线程更易用,但是消耗更多的内存。如果你的代码是CPU密集型的,多进程就明显是更好的选择——特别是所使用的机器是多核或多CPU的。对于网络应用,在你需要扩展到多台机器上执行任务,RQ是更好的选择。
6、注:关于并发、并行区别与联系并发是指,程序在运行的过程中存在多于一个的执行上下文。这些执行上下文一般对应着不同的调用栈。
在单处理器上,并发程序虽然有多个上下文运行环境,但某一个时刻只有一个任务在运行。
但在多处理器上,因为有了多个执行单元,就可以同时有数个任务在跑。
这种物理上同一时刻有多个任务同时运行的方式就是并行。
和并发相比,并行更加强调多个任务同时在运行。
而且并行还有一个层次问题,比如是指令间的并行还是任务间的并行。
xuning715
2019-12-02 01:10:10
0 浏览量
回答数 0
回答
本节主要问题是如何处理15.4小节中提到的Point对象。仔细回一下,在C代码中包含了如下这些工具函数:
/* Destructor function for points */
static void del_Point(PyObject *obj) {
free(PyCapsule_GetPointer(obj,"Point"));
}
/* Utility functions */
static Point *PyPoint_AsPoint(PyObject *obj) {
return (Point *) PyCapsule_GetPointer(obj, "Point");
}
static PyObject *PyPoint_FromPoint(Point *p, int must_free) {
return PyCapsule_New(p, "Point", must_free ? del_Point : NULL);
}
现在的问题是怎样将 PyPoint_AsPoint() 和 Point_FromPoint() 函数作为API导出, 这样其他扩展模块能使用并链接它们,比如如果你有其他扩展也想使用包装的Point对象。
要解决这个问题,首先要为 sample 扩展写个新的头文件名叫 pysample.h ,如下:
/* pysample.h */
#include "Python.h"
#include "sample.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Public API Table */
typedef struct {
Point *(*aspoint)(PyObject *);
PyObject *(*frompoint)(Point *, int);
} _PointAPIMethods;
#ifndef PYSAMPLE_MODULE
/* Method table in external module */
static _PointAPIMethods *_point_api = 0;
/* Import the API table from sample */
static int import_sample(void) {
_point_api = (_PointAPIMethods *) PyCapsule_Import("sample._point_api",0);
return (_point_api != NULL) ? 1 : 0;
}
/* Macros to implement the programming interface */
#define PyPoint_AsPoint(obj) (_point_api->aspoint)(obj)
#define PyPoint_FromPoint(obj) (_point_api->frompoint)(obj)
#endif
#ifdef __cplusplus
}
#endif
这里最重要的部分是函数指针表 _PointAPIMethods . 它会在导出模块时被初始化,然后导入模块时被查找到。 修改原始的扩展模块来填充表格并将它像下面这样导出:
/* pysample.c */
#include "Python.h"
#define PYSAMPLE_MODULE
#include "pysample.h"
...
/* Destructor function for points */
static void del_Point(PyObject *obj) {
printf("Deleting point\n");
free(PyCapsule_GetPointer(obj,"Point"));
}
/* Utility functions */
static Point *PyPoint_AsPoint(PyObject *obj) {
return (Point *) PyCapsule_GetPointer(obj, "Point");
}
static PyObject *PyPoint_FromPoint(Point *p, int free) {
return PyCapsule_New(p, "Point", free ? del_Point : NULL);
}
static _PointAPIMethods _point_api = {
PyPoint_AsPoint,
PyPoint_FromPoint
};
...
/* Module initialization function */
PyMODINIT_FUNC
PyInit_sample(void) {
PyObject *m;
PyObject *py_point_api;
m = PyModule_Create(&samplemodule);
if (m == NULL)
return NULL;
/* Add the Point C API functions */
py_point_api = PyCapsule_New((void *) &_point_api, "sample._point_api", NULL);
if (py_point_api) {
PyModule_AddObject(m, "_point_api", py_point_api);
}
return m;
}
最后,下面是一个新的扩展模块例子,用来加载并使用这些API函数:
/* ptexample.c */
/* Include the header associated with the other module */
#include "pysample.h"
/* An extension function that uses the exported API */
static PyObject *print_point(PyObject *self, PyObject *args) {
PyObject *obj;
Point *p;
if (!PyArg_ParseTuple(args,"O", &obj)) {
return NULL;
}
/* Note: This is defined in a different module */
p = PyPoint_AsPoint(obj);
if (!p) {
return NULL;
}
printf("%f %f\n", p->x, p->y);
return Py_BuildValue("");
}
static PyMethodDef PtExampleMethods[] = {
{"print_point", print_point, METH_VARARGS, "output a point"},
{ NULL, NULL, 0, NULL}
};
static struct PyModuleDef ptexamplemodule = {
PyModuleDef_HEAD_INIT,
"ptexample", /* name of module */
"A module that imports an API", /* Doc string (may be NULL) */
-1, /* Size of per-interpreter state or -1 */
PtExampleMethods /* Method table */
};
/* Module initialization function */
PyMODINIT_FUNC
PyInit_ptexample(void) {
PyObject *m;
m = PyModule_Create(&ptexamplemodule);
if (m == NULL)
return NULL;
/* Import sample, loading its API functions */
if (!import_sample()) {
return NULL;
}
return m;
}
编译这个新模块时,你甚至不需要去考虑怎样将函数库或代码跟其他模块链接起来。 例如,你可以像下面这样创建一个简单的 setup.py 文件:
# setup.py
from distutils.core import setup, Extension
setup(name='ptexample',
ext_modules=[
Extension('ptexample',
['ptexample.c'],
include_dirs = [], # May need pysample.h directory
)
]
)
如果一切正常,你会发现你的新扩展函数能和定义在其他模块中的C API函数一起运行的很好。
>>> import sample
>>> p1 = sample.Point(2,3)
>>> p1
>>> import ptexample
>>> ptexample.print_point(p1)
2.000000 3.000000
>>>
哦哦喔
2020-04-17 18:20:13
0 浏览量
回答数 0
问题
Android分类应用程序与Tensorflow Lite模型崩溃
我与黑色素瘤细调CNN Mobilenet癌症数据集产生黑色素瘤和Non-Melanoma类分类模型,然后我转换Tensorflow Lite模式,但是当我开发它在移动,然后单击“分类”按钮程序崩溃告诉我不幸InceptionTutoria...
kun坤
2019-12-25 15:55:09
6 浏览量
回答数 1
回答
For small problems involving C code, it is often easy enough to use the ctypes modulethat is part of Python’s standard library. In order to use ctypes, you must first makesure the C code you want to access has been compiled into a shared library that iscompatible with the Python interpreter (e.g., same architecture, word size, compiler,etc.). For the purposes of this recipe, assume that a shared library, libsample.so, hasbeen created and that it contains nothing more than the code shown in the chapterintroduction. Further assume that the libsample.so file has been placed in the samedirectory as the sample.py file shown next.To access the resulting library, you make a Python module that wraps around it, suchas the following:# sample.pyimport ctypesimport os
Try to locate the .so file in the same directory as this file_file = ‘libsample.so'_path = os.path.join(*(os.path.split(file)[:-1] + (_file,)))_mod = ctypes.cdll.LoadLibrary(_path)
int gcd(int, int)gcd = _mod.gcdgcd.argtypes = (ctypes.c_int, ctypes.c_int)gcd.restype = ctypes.c_int
int in_mandel(double, double, int)in_mandel = _mod.in_mandelin_mandel.argtypes = (ctypes.c_double, ctypes.c_double, ctypes.c_int)in_mandel.restype = ctypes.c_int
int divide(int, int, int *)_divide = _mod.divide_divide.argtypes = (ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int))_divide.restype = ctypes.c_int
def divide(x, y):
rem = ctypes.c_int()quot = _divide(x, y, rem)
return quot,rem.value
void avg(double *, int n)# Define a special type for the ‘double *‘ argumentclass DoubleArrayType:
def fromparam(self, param):> typename = type(param).nameif hasattr(self, ‘[from](#)‘ + typename):
return getattr(self, ‘from_‘ + typename)(param)
elif isinstance(param, ctypes.Array):return paramelse:raise TypeError(“Can't convert %s” % typename)> # Cast from array.array objectsdef from_array(self, param):
if param.typecode != ‘d':raise TypeError(‘must be an array of doubles')> > ptr, _ = param.buffer_info()return ctypes.cast(ptr, ctypes.POINTER(ctypes.c_double))
Cast from lists/tuplesdef from_list(self, param):
val = ((ctypes.c_double)len(param))([](#)param)return val
from_tuple = from_list
Cast from a numpy arraydef from_ndarray(self, param):
return param.ctypes.data_as(ctypes.POINTER(ctypes.c_double))
DoubleArray = DoubleArrayType()_avg = _mod.avg_avg.argtypes = (DoubleArray, ctypes.c_int)_avg.restype = ctypes.c_double
def avg(values):return _avg(values, len(values))
struct Point { }class Point(ctypes.Structure):
fields = [(‘x', ctypes.c_double),(‘y', ctypes.c_double)]
double distance(Point *, Point *)distance = _mod.distancedistance.argtypes = (ctypes.POINTER(Point), ctypes.POINTER(Point))distance.restype = ctypes.c_double
If all goes well, you should be able to load the module and use the resulting C functions.For example:
>>> import sample
>>> sample.gcd(35,42)
7
>>> sample.in_mandel(0,0,500)
1
>>> sample.in_mandel(2.0,1.0,500)
0
>>> sample.divide(42,8)
(5, 2)
>>> sample.avg([1,2,3])
2.0
>>> p1 = sample.Point(1,2)
>>> p2 = sample.Point(4,5)
>>> sample.distance(p1,p2)
4.242640687119285
>>>
讨论
There are several aspects of this recipe that warrant some discussion. The first issueconcerns the overall packaging of C and Python code together. If you are using ctypesto access C code that you have compiled yourself, you will need to make sure that theshared library gets placed in a location where the sample.py module can find it. Onepossibility is to put the resulting .so file in the same directory as the supporting Pythoncode. This is what’s shown at the first part of this recipe—sample.py looks at the filevariable to see where it has been installed, and then constructs a path that points to alibsample.so file in the same directory.If the C library is going to be installed elsewhere, then you’ll have to adjust the pathaccordingly. If the C library is installed as a standard library on your machine, you mightbe able to use the ctypes.util.find_library() function. For example:
>>> from ctypes.util import find_library
>>> find_library('m')
'/usr/lib/libm.dylib'
>>> find_library('pthread')
'/usr/lib/libpthread.dylib'
>>> find_library('sample')
'/usr/local/lib/libsample.so'
>>>
Again, ctypes won’t work at all if it can’t locate the library with the C code. Thus, you’llneed to spend a few minutes thinking about how you want to install things.Once you know where the C library is located, you use ctypes.cdll.LoadLibrary()to load it. The following statement in the solution does this where _path is the fullpathname to the shared library:
_mod = ctypes.cdll.LoadLibrary(_path)
Once a library has been loaded, you need to write statements that extract specific sym‐bols and put type signatures on them. This is what’s happening in code fragments suchas this:
int in_mandel(double, double, int)in_mandel = _mod.in_mandelin_mandel.argtypes = (ctypes.c_double, ctypes.c_double, ctypes.c_int)in_mandel.restype = ctypes.c_int
In this code, the .argtypes attribute is a tuple containing the input arguments to afunction, and .restype is the return type. ctypes defines a variety of type objects (e.g.,c_double, c_int, c_short, c_float, etc.) that represent common C data types. Attach‐ing the type signatures is critical if you want to make Python pass the right kinds ofarguments and convert data correctly (if you don’t do this, not only will the code notwork, but you might cause the entire interpreter process to crash).A somewhat tricky part of using ctypes is that the original C code may use idioms thatdon’t map cleanly to Python. The divide() function is a good example because it returnsa value through one of its arguments. Although that’s a common C technique, it’s oftennot clear how it’s supposed to work in Python. For example, you can’t do anythingstraightforward like this:
>>> divide = _mod.divide
>>> divide.argtypes = (ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int))
>>> x = 0
>>> divide(10, 3, x)
Traceback (most recent call last):
File "", line 1, in
ctypes.ArgumentError: argument 3: : expected LP_c_int
instance instead of int
>>>
Even if this did work, it would violate Python’s immutability of integers and probablycause the entire interpreter to be sucked into a black hole. For arguments involvingpointers, you usually have to construct a compatible ctypes object and pass it in likethis:
>>> x = ctypes.c_int()
>>> divide(10, 3, x)
3
>>> x.value
1
>>>
Here an instance of a ctypes.c_int is created and passed in as the pointer object. Unlikea normal Python integer, a c_int object can be mutated. The .value attribute can beused to either retrieve or change the value as desired.
For cases where the C calling convention is “un-Pythonic,” it is common to write a smallwrapper function. In the solution, this code makes the divide() function return thetwo results using a tuple instead:# int divide(int, int, int *)_divide = _mod.divide_divide.argtypes = (ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int))_divide.restype = ctypes.c_int
def divide(x, y):rem = ctypes.c_int()quot = _divide(x,y,rem)return quot, rem.value
The avg() function presents a new kind of challenge. The underlying C code expectsto receive a pointer and a length representing an array. However, from the Python side,we must consider the following questions: What is an array? Is it a list? A tuple? Anarray from the array module? A numpy array? Is it all of these? In practice, a Python“array” could take many different forms, and maybe you would like to support multiplepossibilities.The DoubleArrayType class shows how to handle this situation. In this class, a singlemethod from_param() is defined. The role of this method is to take a single parameterand narrow it down to a compatible ctypes object (a pointer to a ctypes.c_double, inthe example). Within from_param(), you are free to do anything that you wish. In thesolution, the typename of the parameter is extracted and used to dispatch to a morespecialized method. For example, if a list is passed, the typename is list and a methodfrom_list() is invoked.For lists and tuples, the from_list() method performs a conversion to a ctypes arrayobject. This looks a little weird, but here is an interactive example of converting a list toa ctypes array:
>>> nums = [1, 2, 3]
>>> a = (ctypes.c_double * len(nums))(*nums)
>>> a
>>> a[0]
1.0
>>> a[1]
2.0
>>> a[2]
3.0
>>>
For array objects, the from_array() method extracts the underlying memory pointerand casts it to a ctypes pointer object. For example:
>>> import array
>>> a = array.array('d',[1,2,3])
>>> a
array('d', [1.0, 2.0, 3.0])
>>> ptr_ = a.buffer_info()
>>> ptr
4298687200
>>> ctypes.cast(ptr, ctypes.POINTER(ctypes.c_double))
>>>
The from_ndarray() shows comparable conversion code for numpy arrays.By defining the DoubleArrayType class and using it in the type signature of avg(), asshown, the function can accept a variety of different array-like inputs:
>>> import sample
>>> sample.avg([1,2,3])
2.0
>>> sample.avg((1,2,3))
2.0
>>> import array
>>> sample.avg(array.array('d',[1,2,3]))
2.0
>>> import numpy
>>> sample.avg(numpy.array([1.0,2.0,3.0]))
2.0
>>>
The last part of this recipe shows how to work with a simple C structure. For structures,you simply define a class that contains the appropriate fields and types like this:
class Point(ctypes.Structure):fields = [(‘x', ctypes.c_double),(‘y', ctypes.c_double)]
Once defined, you can use the class in type signatures as well as in code that needs toinstantiate and work with the structures. For example:
>>> p1 = sample.Point(1,2)
>>> p2 = sample.Point(4,5)
>>> p1.x
1.0
>>> p1.y
2.0
>>> sample.distance(p1,p2)
4.242640687119285
>>>
A few final comments: ctypes is a useful library to know about if all you’re doing isaccessing a few C functions from Python. However, if you’re trying to access a largelibrary, you might want to look at alternative approaches, such as Swig (described inRecipe 15.9) or Cython (described in Recipe 15.10).
The main problem with a large library is that since ctypes isn’t entirely automatic, you’llhave to spend a fair bit of time writing out all of the type signatures, as shown in theexample. Depending on the complexity of the library, you might also have to write alarge number of small wrapper functions and supporting classes. Also, unless you fullyunderstand all of the low-level details of the C interface, including memory managementand error handling, it is often quite easy to make Python catastrophically crash with asegmentation fault, access violation, or some similar error.As an alternative to ctypes, you might also look at CFFI. CFFI provides much of thesame functionality, but uses C syntax and supports more advanced kinds of C code. Asof this writing, CFFI is still a relatively new project, but its use has been growing rapidly.There has even been some discussion of including it in the Python standard library insome future release. Thus, it’s definitely something to keep an eye on.
景凌凯
2020-04-19 21:01:17
0 浏览量
回答数 0