库和扩展常见问题解答

目录

一般类库问题

如何找到执行任务X的模块或应用程序?

检查 the Library Reference 看看是否有相关的标准库模块。(最终,您将了解标准库中的内容,并可以跳过此步骤。)

对于第三方软件包,请搜索 Python Package Index 或尝试 Google 或者另一个网络搜索引擎。搜索“python”加上一个或两个关键字来搜索您感兴趣的主题通常会发现一些有用的东西。

math.py(socket.py、regex.py等)源文件在哪里?

如果找不到模块的源文件,它可能是一个内置的或动态加载的模块,用C、C++或其他编译语言实现。在这种情况下,您可能没有源文件,或者它可能类似于 mathmodule.c ,位于C源目录中的某个位置(不在python路径上)。

python中至少有三种模块:

  1. 用python(.py)编写的模块;

  2. 用C编写并动态加载的模块(.dll,.pyd,.so,.sl等);

  3. 用C编写并与解释器链接的模块;要获取这些模块的列表,请键入:

    import sys
    print(sys.builtin_module_names)
    

如何使python脚本在UNIX上可执行?

您需要做两件事:脚本文件的模式必须是可执行的,第一行必须以 #! 后面是python解释器的路径。

第一个是通过执行 chmod +x scriptfile 或者也许 chmod 755 scriptfile .

第二种方法有很多种。最直接的方法是写:

#!/usr/local/bin/python

作为文件的第一行,将路径名用于平台上安装Python解释器的位置。

如果希望脚本独立于Python解释器所在的位置,可以使用 env 程序。假设python解释器位于用户的 PATH ::

#!/usr/bin/env python

不要 对CGI脚本执行此操作。这个 PATH CGI脚本的变量通常非常小,因此需要使用解释器的实际绝对路径名。

有时,用户的环境非常满,以至于 /usr/bin/env 程序失败;或者根本没有env程序。在这种情况下,您可以尝试以下黑客(由于Alex Rezinsky):

#! /bin/sh
""":"
exec python $0 ${1+"$@"}
"""

次要的缺点是它定义了脚本的 __doc__ 字符串。但是,您可以通过添加:

__doc__ = """...Whatever..."""

是否有针对python的curses/termcap包?

对于UNIX变体:标准的python源代码发行版在 Modules 子目录,尽管它不是默认编译的。(请注意,这在Windows分发版中不可用——Windows没有Curses模块。)

这个 curses 模块支持基本CURSES功能,以及来自NCURES和SYSV CURSES的许多附加功能,例如颜色、可选字符集支持、PADS和鼠标支持。这意味着该模块与只有BSDcurses的操作系统不兼容,但似乎没有任何当前维护的OSE属于此类。

对于Windows:使用 the consolelib module .

在python中是否有与c的onexit()等效的函数?

这个 atexit 模块提供类似于C的寄存器功能 onexit() .

为什么我的信号处理器不工作?

最常见的问题是,用错误的参数列表声明信号处理程序。它被称为:

handler(signum, frame)

所以它应该用两个参数声明:

def handler(signum, frame):
    ...

共同任务

如何测试Python程序或组件?

Python附带了两个测试框架。这个 doctest 模块在一个模块的docstrings中查找示例并运行它们,将输出与docstring中给出的预期输出进行比较。

这个 unittest 模块是一个基于Java和SimultTalk测试框架的比较理想的测试框架。

为了使测试更容易,您应该在程序中使用好的模块化设计。您的程序应该将几乎所有的功能封装在函数或类方法中——这有时会使程序运行得更快(因为局部变量访问比全局访问更快),从而产生令人惊讶和愉快的效果。此外,程序应该避免依赖于变化的全局变量,因为这使得测试更加困难。

程序的“全局主逻辑”可能简单如下:

if __name__ == "__main__":
    main_logic()

在程序主模块的底部。

一旦你的程序被组织成一个简单的函数和类行为集合,你就应该编写测试函数来练习这些行为。自动化一系列测试的测试套件可以与每个模块相关联。这听起来像是很多工作,但是由于Python是如此简洁和灵活,所以它非常容易。通过与“生产代码”并行编写测试函数,您可以使编写代码变得更加愉快和有趣,因为这样可以很容易地更早地发现错误甚至设计缺陷。

不作为程序主模块的“支持模块”可包括模块的自检。::

if __name__ == "__main__":
    self_test()

即使是与复杂外部接口交互的程序,也可以在外部接口不可用时使用在Python中实现的“假”接口进行测试。

如何从文档字符串创建文档?

这个 pydoc 模块可以从Python源代码中的文档字符串创建HTML。纯粹从docstrings创建API文档的另一种方法是 epydoc . Sphinx 还可以包括docstring内容。

我怎样才能一次按一个键?

对于UNIX变体,有几种解决方案。使用curses很简单,但是curses是一个相当大的学习模块。

线程

如何使用线程编程?

一定要使用 threading 模块而不是 _thread 模块。这个 threading 模块在由 _thread 模块。

aahz的线程教程中有一组幻灯片很有用;请参阅http://www.pythoncraft.com/oscon2001/。

我的线程似乎都没有运行:为什么?

一旦主线程退出,所有线程都将被终止。您的主线程运行得太快,使线程没有时间做任何工作。

一个简单的修复方法是在程序末尾添加一个足够长的睡眠,使所有线程都可以完成:

import threading, time

def thread_task(name, n):
    for i in range(n):
        print(name, i)

for i in range(10):
    T = threading.Thread(target=thread_task, args=(str(i), i))
    T.start()

time.sleep(10)  # <---------------------------!

但现在(在许多平台上)线程不是并行运行的,而是按顺序运行的,一次运行一个线程!原因是,在前一个线程被阻塞之前,OS线程调度程序不会启动新线程。

一个简单的修复方法是在run函数的开头添加一个很小的睡眠:

def thread_task(name, n):
    time.sleep(0.001)  # <--------------------!
    for i in range(n):
        print(name, i)

for i in range(10):
    T = threading.Thread(target=thread_task, args=(str(i), i))
    T.start()

time.sleep(10)

而不是试图猜测 time.sleep() 最好使用某种信号量机制。一种方法是使用 queue 模块创建一个队列对象,让每个线程在队列结束时向队列附加一个令牌,并让主线程从队列中读取尽可能多的令牌。

如何在一堆工作线程中包出工作?

最简单的方法是使用 concurrent.futures 模块,尤其是 ThreadPoolExecutor 班级。

或者,如果您想要对调度算法进行精细控制,您可以手动编写自己的逻辑。使用 queue 模块创建包含任务列表的队列。这个 Queue 类维护对象列表并具有 .put(obj) 将项添加到队列和 .get() 方法返回它们。该类将负责必要的锁定,以确保每项任务只分发一次。

下面是一个小例子:

import threading, queue, time

# The worker thread gets jobs off the queue.  When the queue is empty, it
# assumes there will be no more work and exits.
# (Realistically workers will run until terminated.)
def worker():
    print('Running worker')
    time.sleep(0.1)
    while True:
        try:
            arg = q.get(block=False)
        except queue.Empty:
            print('Worker', threading.currentThread(), end=' ')
            print('queue empty')
            break
        else:
            print('Worker', threading.currentThread(), end=' ')
            print('running with argument', arg)
            time.sleep(0.5)

# Create queue
q = queue.Queue()

# Start a pool of 5 workers
for i in range(5):
    t = threading.Thread(target=worker, name='worker %i' % (i+1))
    t.start()

# Begin adding work to the queue
for i in range(50):
    q.put(i)

# Give threads time to run
print('Main thread sleeping')
time.sleep(5)

运行时,将产生以下输出:

Running worker
Running worker
Running worker
Running worker
Running worker
Main thread sleeping
Worker <Thread(worker 1, started 130283832797456)> running with argument 0
Worker <Thread(worker 2, started 130283824404752)> running with argument 1
Worker <Thread(worker 3, started 130283816012048)> running with argument 2
Worker <Thread(worker 4, started 130283807619344)> running with argument 3
Worker <Thread(worker 5, started 130283799226640)> running with argument 4
Worker <Thread(worker 1, started 130283832797456)> running with argument 5
...

有关更多详细信息,请参阅模块的文档; Queue 类提供了一个功能性接口。

什么样的全局值突变是线程安全的?

A global interpreter lock (gil)在内部使用,以确保每次在python vm中只运行一个线程。通常,python只提供在字节码指令之间在线程之间切换;通过 sys.setswitchinterval() . 因此,从Python程序的角度来看,每个字节码指令以及从每个指令获得的所有C实现代码都是原子的。

理论上,这意味着精确的会计处理需要对pvm字节码实现有一个精确的理解。在实践中,它意味着对内置数据类型(ints、list、dict等)的共享变量的操作实际上是“看起来像原子的”。

例如,以下操作都是原子操作(l、l1、l2是列表,d、d1、d2是字典,x、y是对象,i、j是整数)::

L.append(x)
L1.extend(L2)
x = L[i]
x = L.pop()
L1[i:j] = L2
L.sort()
x = y
x.field = y
D[x] = y
D1.update(D2)
D.keys()

这些不是:

i = i+1
L.append(L[-1])
L[i] = L[j]
D[x] = D[x] + 1

替换其他对象的操作可能会调用这些其他对象的 __del__() 方法的引用计数为零时,这可能会影响到某些内容。这对于字典和列表的批量更新尤其适用。如果有疑问,请使用互斥体!

我们不能把全局解释器锁去掉吗?

这个 global interpreter lock (gil)通常被视为妨碍Python在高端多处理器服务器机器上的部署,因为多线程Python程序有效地只使用一个CPU,这是因为(几乎)所有Python代码只能在gil被保存时运行。

早在Python1.5时代,GregStein实际上实现了一个全面的补丁集(“自由线程”补丁),它删除了gil,并用细粒度的锁替换了它。亚当·奥尔森最近做了一个类似的实验 python-safethread 项目。不幸的是,两个实验都显示了单线程性能的急剧下降(至少慢了30%),这是因为需要大量的细粒度锁定来补偿gil的删除。

这并不意味着你不能在多CPU机器上很好地利用python!你只需要创造性地将工作分成多个部分 过程 而不是多重的 线程 . 这个 ProcessPoolExecutor 新课程 concurrent.futures 模块提供了一种简单的方法;即 multiprocessing 模块提供了一个较低级别的API,以防您需要对任务调度进行更多控制。

明智地使用C扩展也会有所帮助;如果您使用C扩展来执行一个耗时的任务,那么该扩展可以在执行线程位于C代码中时释放gil,并允许其他线程完成一些工作。一些标准库模块,如 zlibhashlib 已经这样做了。

有人建议,gil应该是每个解释器的状态锁,而不是真正的全局锁;这样,解释器就不能共享对象。不幸的是,这也不太可能发生。这将是一项巨大的工作,因为许多对象实现当前都具有全局状态。例如,缓存小整数和短字符串;这些缓存必须移动到解释器状态。其他对象类型有自己的空闲列表;这些空闲列表必须移动到解释器状态。等等。

我怀疑它甚至可以在有限的时间内完成,因为第三方扩展也存在同样的问题。第三方扩展的写入速度可能快于您将其转换为将其所有全局状态存储在解释器状态的速度。

最后,当您有多个不共享任何状态的解释程序时,在一个单独的过程中运行每个解释程序有什么好处?

输入和输出

如何删除文件?(以及其他文件问题…)

使用 os.remove(filename)os.unlink(filename) ;有关文档,请参见 os 模块。两种功能相同; unlink() 只是该函数的Unix系统调用的名称。

要删除目录,请使用 os.rmdir() 使用 os.mkdir() 创建一个。 os.makedirs(path) 将在中创建任何中间目录 path 那是不存在的。 os.removedirs(path) 将删除中间目录,只要它们为空;如果要删除整个目录树及其内容,请使用 shutil.rmtree() .

要重命名文件,请使用 os.rename(old_path, new_path) .

要截断文件,请使用 f = open(filename, "rb+") 及使用 f.truncate(offset) ;偏移默认为当前搜索位置。还有 os.ftruncate(fd, offset) 用于打开的文件 os.open() 在哪里 fd 是文件描述符(小整数)。

这个 shutil 模块还包含许多处理文件的函数,包括 copyfile()copytree()rmtree() .

如何复制文件?

这个 shutil 模块包含 copyfile() 功能。请注意,在MacOS9上,它不会复制资源叉和查找器信息。

如何读取(或写入)二进制数据?

要读取或写入复杂的二进制数据格式,最好使用 struct 模块。它允许您获取包含二进制数据(通常是数字)的字符串,并将其转换为Python对象;反之亦然。

例如,以下代码从文件中读取两个2字节整数和一个4字节的big-endian格式整数:

import struct

with open(filename, "rb") as f:
    s = f.read(8)
    x, y, z = struct.unpack(">hhl", s)

格式字符串中的“>”强制使用big endian数据;字母“h”读取一个“短整数”(2个字节),字母“l”读取字符串中的一个“长整数”(4个字节)。

对于更规则的数据(例如整数或浮点数的同类列表),也可以使用 array 模块。

注解

要读取和写入二进制数据,必须以二进制模式打开文件(此处,传递 "rb"open() )如果你使用 "r" 相反(默认),文件将以文本模式打开,并且 f.read() 将返回 str 对象而不是 bytes 物体。

我似乎不能在用os.popen()创建的管道上使用os.read();为什么?

os.read() 是一个低级函数,它接受一个文件描述符,一个表示打开文件的小整数。 os.popen() 创建一个高级文件对象,该对象与内置程序返回的类型相同 open() 功能。因此,阅读 n 来自管道的字节 p 创建与 os.popen() ,您需要使用 p.read(n) .

如何访问串行(RS232)端口?

对于win32、posix(linux、bsd等),jython:

对于Unix,请参阅Mitch Chapman的一篇Usenet文章:

为什么关闭sys.stdout(stdin、stderr)不会真正关闭它?

Python file objects 是底层C文件描述符上的高级抽象层。

对于大多数在python中通过内置的 open() 函数, f.close() 从python的角度将python文件对象标记为关闭,并安排关闭底层的c文件描述符。这也会自动发生在 f 的析构函数,当 f 变成垃圾。

但是,stdin、stdout和stderr是由python专门处理的,因为C.running也为它们提供了特殊的状态。 sys.stdout.close() 将python级别的文件对象标记为已关闭,但确实如此 not 关闭关联的C文件描述符。

要关闭这三个文件中的一个的底层C文件描述符,首先应该确保这是您真正想要做的(例如,您可能会混淆尝试执行I/O的扩展模块)。如果是,使用 os.close() ::

os.close(stdin.fileno())
os.close(stdout.fileno())
os.close(stderr.fileno())

或者可以分别使用数字常量0、1和2。

网络/互联网编程

有什么针对python的WWW工具?

参见标题为 Internet协议和支持互联网数据处理 在类库参考手册中。python有许多模块可以帮助您构建服务器端和客户端Web系统。

可用框架的摘要由PaulBoddie在https://wiki.python.org/moin/webprogramming上维护。

Cameron Laird在http://phaseit.net/clard/comp.lang.python/web_python上维护了一组关于python web技术的有用页面。

如何模拟CGI表格提交(方法=邮寄)?

我想检索发布表单的结果网页。有没有现有的代码可以让我轻松地做到这一点?

对。下面是一个简单的例子 urllib.request ::

#!/usr/local/bin/python

import urllib.request

# build the query string
qs = "First=Josephine&MI=Q&Last=Public"

# connect and send the server a path
req = urllib.request.urlopen('http://www.some-server.out-there'
                             '/cgi-bin/some-cgi-script', data=qs)
with req:
    msg, hdrs = req.read(), req.info()

注意,通常对于百分比编码的post操作,查询字符串必须使用 urllib.parse.urlencode() .例如,发送 name=Guy Steele, Jr. ::

>>> import urllib.parse
>>> urllib.parse.urlencode({'name': 'Guy Steele, Jr.'})
'name=Guy+Steele%2C+Jr.'

参见

如何使用urllib包获取Internet资源 对于广泛的例子。

我应该使用什么模块来帮助生成HTML?

您可以在 Web Programming wiki page .

如何从python脚本发送邮件?

使用标准库模块 smtplib .

这是一个非常简单的交互式邮件发送器。此方法将在任何支持SMTP侦听器的主机上工作。::

import sys, smtplib

fromaddr = input("From: ")
toaddrs  = input("To: ").split(',')
print("Enter message, end with ^D:")
msg = ''
while True:
    line = sys.stdin.readline()
    if not line:
        break
    msg += line

# The actual mail send
server = smtplib.SMTP('localhost')
server.sendmail(fromaddr, toaddrs, msg)
server.quit()

只有Unix才使用sendmail。sendmail程序的位置因系统而异;有时 /usr/lib/sendmail ,有时 /usr/sbin/sendmail . sendmail手册页将帮助您解决问题。以下是一些示例代码:

import os

SENDMAIL = "/usr/sbin/sendmail"  # sendmail location
p = os.popen("%s -t -i" % SENDMAIL, "w")
p.write("To: receiver@example.com\n")
p.write("Subject: test\n")
p.write("\n")  # blank line separating headers from body
p.write("Some text\n")
p.write("some more text\n")
sts = p.close()
if sts != 0:
    print("Sendmail exit status", sts)

如何避免在套接字的connect()方法中阻塞?

这个 select 模块通常用于帮助处理套接字上的异步I/O。

为了防止TCP连接阻塞,可以将套接字设置为非阻塞模式。当你做的时候 socket.connect() ,您将立即连接(不太可能)或获取包含错误号的异常 .errno . errno.EINPROGRESS 指示连接正在进行,但尚未完成。不同的OSE将返回不同的值,因此您必须检查系统上返回的值。

你可以使用 socket.connect_ex() 方法以避免创建异常。它只返回errno值。要投票,您可以拨打 socket.connect_ex() 以后再来一次-- 0errno.EISCONN 表示您已连接--或者您可以将此套接字传递给 select.select() 检查它是否可写。

注解

这个 asyncio 模块提供了一个通用的单线程并发异步库,可用于编写无阻塞的网络代码。第三方 Twisted 类库是一种流行的、功能丰富的选择。

数据库

在python中有数据库包的接口吗?

对。

与基于磁盘的hash的接口,如 DBMGDBM 也包括在标准python中。还有 sqlite3 模块,它提供了一个基于磁盘的轻量级关系数据库。

支持大多数关系数据库。见 DatabaseProgramming wiki page 有关详细信息。

如何在Python中实现持久对象?

这个 pickle 库模块以非常普通的方式解决了这个问题(尽管您仍然不能存储诸如打开的文件、套接字或窗口之类的内容),并且 shelve 库模块使用pickle和(g)dbm创建包含任意python对象的持久映射。

数学与数理

如何在Python中生成随机数?

标准模块 random 实现随机数生成器。用法很简单:

import random
random.random()

这将返回范围[0,1]内的随机浮点数。

此模块中还有许多其他专用的生成器,例如:

  • randrange(a, b) 选择范围[A,B]内的整数。

  • uniform(a, b) 选择范围[A,B]中的浮点数。

  • normalvariate(mean, sdev) 采样正态(高斯)分布。

一些更高级的函数直接作用于序列,例如:

  • choice(S) 从给定序列中选择随机元素。

  • shuffle(L) 将列表随机排列。

还有一个 Random 类可以实例化以创建独立的多个随机数生成器。