Library and Extension FAQ

General Library Questions

How do I find a module or application to perform task X?

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

对于第三方包,请搜索Python包索引或尝试Google或其他网络搜索引擎。搜索“Python”加上一个关键字或两个您感兴趣的主题通常会找到有用的东西。

Where is the math.py (socket.py, regex.py, etc.) source file?

如果您找不到模块的源文件,它可能是用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)
    

How do I make a Python script executable on Unix?

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

第一个是通过执行chmod + x scriptfile或者chmod t5 > 755 scriptfile

第二个可以以多种方式完成。最直接的方式是写

#!/usr/local/bin/python

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

如果您希望脚本独立于Python解释器所在的位置,可以使用env程序。几乎所有Unix变体都支持以下假设Python解释器在用户的 PATH上的目录中:

#!/usr/bin/env python

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

偶尔,用户的环境已满,使得/ usr / bin / env程序失败;或者没有env程序。在这种情况下,您可以尝试以下黑客(由于Alex Rezinsky):

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

最小的缺点是,这定义了脚本的__doc__字符串。但是,您可以通过添加来解决

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

Is there a curses/termcap package for Python?

对于Unix变体:标准的Python源代码分发在Modules子目录中带有一个curses模块,但默认情况下不会编译。(请注意,这在Windows分发版中不可用 - 没有用于Windows的curses模块。)

curses模块支持基本curses特性以及ncurses和SYSV curses中的许多附加函数,如颜色,替代字符集支持,焊盘和鼠标支持。这意味着该模块与仅具有BSD curses的操作系统不兼容,但似乎没有属于此类别的任何当前维护的操作系统。

对于Windows:使用consolelib模块

Is there an equivalent to C’s onexit() in Python?

atexit模块提供了类似于C的onexit()的寄存器功能。

Why don’t my signal handlers work?

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

handler(signum, frame)

因此应该使用两个参数声明:

def handler(signum, frame):
    ...

Common tasks

How do I test a Python program or component?

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

unittest模块是一个基于Java和Smalltalk测试框架建模的fancier测试框架。

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

程序的“全局主逻辑”可能很简单

if __name__ == "__main__":
    main_logic()

在程序的主模块的底部。

一旦你的程序被组织为一个易处理的函数和类行为的容器,你应该编写测试函数来执行行为。自动化一系列测试的测试套件可以与每个模块相关联。这听起来像很多工作,但由于Python是如此简洁和灵活,这是令人惊讶的容易。通过与“生产代码”并行编写测试函数,可以使编码更加愉快和有趣,因为这使得很容易找到错误,甚至更早地设计缺陷。

不旨在作为程序的主要模块的“支持模块”可以包括模块的自测试。

if __name__ == "__main__":
    self_test()

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

How do I create documentation from doc strings?

pydoc模块可以从Python源代码中的文档字符串创建HTML。纯粹从docstrings创建API文档的替代方法是epydocSphinx也可以包含文档字符串内容。

How do I get a single keypress at a time?

对于Unix变体,有几种解决方案。使用curses很容易做到这一点,但是curses是一个相当大的模块。

Threads

How do I program using threads?

请务必使用threading模块,而不是_thread模块。threading模块在_thread模块提供的低级原语之上构建方便的抽象。

Aahz从他的线程教程中有一组有用的幻灯片;请参阅http://www.pythoncraft.com/OSCON2001/

None of my threads seem to run: why?

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

一个简单的解决方法是在程序结束时添加一个睡眠,该睡眠时间足以使所有线程完成:

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线程调度程序不启动新线程。

一个简单的修复是在运行函数的开始添加一个微小的睡眠:

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模块创建队列对象,让每个线程在完成后向队列附加一个令牌,并让主线程从队列中读取多个令牌,因为有线程。

How do I parcel out work among a bunch of worker threads?

最简单的方法是使用新的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类提供了一个强大的接口。

What kinds of global value mutation are thread-safe?

内部使用global interpreter lock(GIL)来确保每次只有一个线程在Python VM中运行。一般来说,Python只在字节码指令之间切换线程;可以通过sys.setswitchinterval()设置它切换的频率。因此,从Python程序的角度来看,每个字节码指令以及从每个指令到达的所有C实现代码都是原子的。

在理论上,这意味着精确的计算需要对PVM字节码实现的精确理解。在实践中,它意味着对内建数据类型(int,列表,dicts等)的共享变量的操作真正“看起来原子”。

例如,以下操作都是原子的(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__()方法,这可能会影响事情。这对于字典和列表的大量更新尤其如此。当有疑问时,使用互斥体!

Can’t we get rid of the Global Interpreter Lock?

global interpreter lock(GIL)通常被视为阻碍Python在高端多处理器服务器机器上部署,因为多线程Python程序仅使用一个CPU,因为坚持(几乎)所有的Python代码只能在GIL持有时运行。

回到Python 1.5的日子里,Greg Stein实际上实现了一个全面的补丁集(“自由线程”补丁),它删除了GIL并用细粒度锁定代替它。Adam Olsen最近在他的python-safethread项目中做了类似的实验。不幸的是,由于补偿GIL的移除所需的细粒度锁定的量,两个实验都表现出单螺纹性能的急剧下降(至少慢30%)。

这并不意味着你不能在多CPU机器上充分利用Python!您只需要创造性地将工作划分在多个进程之间,而不是多个线程concurrent.futures模块中的ProcessPoolExecutor类提供了一种简单的方法; multiprocessing模块提供了一个较低级别的API,以防您更多地控制任务分派。

合理使用C扩展也将有所帮助;如果使用C扩展来执行一个耗时的任务,扩展可以释放GIL,而执行线程是在C代码中,并允许其他线程完成一些工作。一些标准库模块,例如zlibhashlib已经做到了。

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

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

最后,一旦你有多个解释器不共享任何状态,你在一个单独的过程中运行每个解释器获得了什么?

Input and Output

How do I delete a file? (And other file questions...)

使用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 +”) / t0>,并使用f.truncate(offset); offset默认为当前搜索位置。There’s also os.ftruncate(fd, offset) for files opened with os.open(), where fd is the file descriptor (a small integer).

shutil模块还包含一些用于处理包括copyfile()copytree()rmtree()

How do I copy a file?

shutil模块包含一个copyfile()函数。注意在MacOS 9它不复制资源fork和Finder信息。

How do I read (or write) binary data?

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

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

import struct

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

格式字符串中的'>'强制大端数据;字母'h'读取一个“短整数”(2字节),并且'l'从字符串读取一个“长整数”(4字节)。

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

注意

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

I can’t seem to use os.read() on a pipe created with os.popen(); why?

os.read()是一个低级函数,它接受一个文件描述器,一个小整数表示打开的文件。os.popen()创建一个高级文件对象,由内建open()函数返回的类型相同。因此,要从使用os.popen()创建的管道p读取n字节,您需要使用p.read(n)

How do I access the serial (RS232) port?

对于Win32,POSIX(Linux,BSD等)),Jython:

对于Unix,请参阅Mitch Chapman的Usenet帖子:

Why doesn’t closing sys.stdout (stdin, stderr) really close it?

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

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

但stdin,stdout和stderr是由Python特别处理的,因为它们也给予它们特殊的状态。运行sys.stdout.close()标记Python级别的文件对象被关闭,但关闭相关的C文件描述器。

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

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

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

Network/Internet Programming

What WWW tools are there for Python?

请参见“库参考手册”中的Internet Protocols and SupportInternet Data Handling章节。Python有许多模块,将帮助您构建服务器端和客户端Web系统。

可用框架的摘要由Paul Boddie在https://wiki.python.org/moin/WebProgramming中维护。

Cameron Laird在http://phaseit.net/claird/comp.lang.python/web_python上维护了一组有关Python Web技术的有用网页。

How can I mimic CGI form submission (METHOD=POST)?

我想检索的网页是POST表单的结果。有没有现成的代码,让我很容易这样做?

是。这里有一个使用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.'

What module should I use to help with generating HTML?

您可以在Web编程wiki页面上找到一个有用链接的容器。

How do I send mail from a Python script?

使用标准库模块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/sendmailsendmail手册页将帮助你。以下是一些示例代码:

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)

How do I avoid blocking in the connect() method of a socket?

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

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

您可以使用connect_ex()方法避免创建异常。它将只返回errno值。要轮询,您可以稍后再次调用connect_ex() - 0errno.EISCONN表示您已连接 - 或者您可以通过套接字选择检查它是否可写。

注意

asyncore模块提供了一种框架式方法来解决编写无阻塞网络代码的问题。第三方Twisted库是一种受欢迎和功能丰富的替代品。

Databases

Are there any interfaces to database packages in Python?

是。

与基于磁盘的散列(例如DBMGDBM)的接口也包含在标准Python中。还有sqlite3模块,它提供了一个轻量级的基于磁盘的关系数据库。

支持大多数关系数据库。有关详细信息,请参阅DatabaseProgramming wiki页面

How do you implement persistent objects in Python?

pickle库模块以非常通用的方式解决这个问题(虽然你仍然不能存储像打开的文件,套接字或窗口),而shelve库模块使用pickle和(g)dbm创建包含任意Python对象的持久性映射。

Mathematics and Numerics

How do I generate random numbers in Python?

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

import random
random.random()

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

在这个模块中还有许多其他专门的生成器,如:

  • randrange(a, b)在范围[a,b]中选择一个整数。
  • uniform(a, b)在范围[a,b]中选择浮点数。
  • 正态变量(mean, sdev)对正态(高斯)分布进行采样。

一些更高级的函数直接对序列进行操作,例如:

  • choice(S)从给定序列中选择随机元素
  • shuffle(L)对列表进行就地清洗,即随机排列

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