Python操作系统函数

Published on 2017 - 02 - 04

文件

和其他语言一样,Python 的文件操作很像 Unix。有些函数的名字相同,比如 chown() 和 chmod(),不过也有很多新函数。

用open()创建文件

使用 open() 函数来打开文件或者创建文件。下面来创建一个名为 oops.txt 的文本文件:

>>> fout = open('oops.txt', 'wt')
>>> print('Oops, I created a file.', file=fout)
>>> fout.close()

我们用这个文件来进行一些测试。

用exists()检查文件是否存在

要判断文件或者目录是否存在,可以使用 exists(),传入相对或者绝对路径名,如下所示:

>>> import os
>>> os.path.exists('oops.txt')
True
>>> os.path.exists('./oops.txt')
True
>>> os.path.exists('waffles')
False
>>> os.path.exists('.')
True
>>> os.path.exists('..')
True

用isfile()检查是否为文件

本节中的函数可以检查一个名称是文件、目录还是符号链接(详情见下方的例子)。

我们要学习的第一个函数是 isfile,它只回答一个问题:这个是不是文件?

>>> name = 'oops.txt'
>>> os.path.isfile(name)
True

下面是判断目录的方法:

>>> os.path.isdir(name)
False

一个点号(.)表示当前目录,两个点号(..)表示上层目录。它们一直存在,所以下面的语句总会返回 True:

>>> os.path.isdir('.')
True

os 模块中有许多处理路径名(完整的文件名,由 / 开始并包含所有上级目录)的函数。其中之一是 isabs(),可以判断参数是否是一个绝对路径名。参数不需要是一个真正的文件:

>>> os.path.isabs(name)
False
>>> os.path.isabs('/big/fake/name')
True
>>> os.path.isabs('big/fake/name/without/a/leading/slash')
False

用copy()复制文件

copy() 函数来自于另一个模块 shutil。下面的例子会把文件 oops.txt 复制到文件 ohno.txt:

>>> import shutil
>>> shutil.copy('oops.txt', 'ohno.txt')

shutil.move() 函数会复制一个文件并删除原始文件。

用rename()重命名文件

这个函数的作用看名字就知道了。下面的例子会把 ohno.txt 文件重命名为 ohwell.txt 文件:

>>> import os
>>> os.rename('ohno.txt', 'ohwell.txt')

用link()或者symlink()创建链接

在 Unix 中,文件只存在于一个位置,但是可以有多个名称,这种机制叫作链接。在更底层的硬链接中,找到一个文件的所有名称并不容易。可以考虑使用符号链接,它会把新名称当作文件存储,这样一次就可以获取到原始名称和新名称。link() 会创建一个硬链接。symlink() 会创建一个符号链接。islink() 函数会检查参数是文件还是符号链接。

下面这个例子展示了如何把已有文件 oops.txt 硬链接到一个新文件 yikes.txt:

>>> os.link('oops.txt', 'yikes.txt')
>>> os.path.isfile('yikes.txt')
True

下面的例子展示了如何把已有文件 oops.txt 符号链接到一个新文件 jeepers.txt:

>>> os.path.islink('yikes.txt')
False
>>> os.symlink('oops.txt', 'jeepers.txt')
>>> os.path.islink('jeepers.txt')
True

用chmod()修改权限

在 Unix 系统中,chmod() 可以修改文件权限。可以设置用户(如果是你创建了文件,那用户通常就是你)、当前用户所在用户组以及其他所有用户组的读、写和执行权限。这个命令接收一个压缩过的八进制(基数为 8)值,这个值包含用户、用户组和权限。举例来说,下面的命令可以让 oops.txt 只能被拥有者读:

>>> os.chmod('oops.txt', 0o400)

如果你不想用压缩过的八进制值并且更喜欢那些(有点)难懂的符号,可以从 stat 模块中导入一些常量并用在语句中:

>>> import stat
>>> os.chmod('oops.txt', stat.S_IRUSR)

用chown()修改所有者

这个函数也是 Unix/Linux/Mac 特有的。你可以指定用户的 ID(uid)和用户组 ID(gid)来修改文件的所有者和 / 或所有用户组:

>>> uid = 5
>>> gid = 22
>>> os.chown('oops', uid, gid)

用abspath()获取路径名

这个函数会把一个相对路径名扩展成绝对路径名。如果你的当前目录是 /usr/gabenlunzie,其中有文件 oops.txt,那你可以用这个函数得到下面的输出:

>>> os.path.abspath('oops.txt')
'/usr/gaberlunzie/oops.txt'

用realpath()获取符号的路径名

在之前的几节中,我们创建了一个 oops.txt 到 jeepers.txt 的符号链接。在这种情况下,你可以使用 realpath() 函数从 jeepers.txt 获取名称 oops.txt,如下所示:

>>> os.path.realpath('jeepers.txt')
'/usr/gaberlunzie/oops.txt'

用remove()删除文件

下面的例子使用 remove() 函数来删除 oops.txt 文件:

>>> os.remove('oops.txt')
>>> os.path.exists('oops.txt')
False

目录

在大多数操作系统中,文件被存储在多级目录(现在经常被称为文件夹)中。包含所有这些文件和目录的容器是文件系统(有时候被称为卷)。标准模块 os 可以处理这些东西,下面是一些可以使用的函数。

使用mkdir()创建目录

下面的例子展示了如何创建目录 poems:

>>> os.mkdir('poems')
>>> os.path.exists('poems')
True

使用rmdir()删除目录

假如你现在发现不需要这个目录,可以用这个函数来删除目录:

>>> os.rmdir('poems')
>>> os.path.exists('poems')
False

使用listdir()列出目录内容

你又后悔了,来重新创建 poems 并加入一些内容:

>>> os.mkdir('poems')

现在列出它的内容(目前还是空):

>>> os.listdir('poems')
[]

接着创建一个子目录:

>>> os.mkdir('poems/mcintyre')
>>> os.listdir('poems')
['mcintyre']

在这个子目录中,创建一个文件(不要手动输入这些文字,除非你真的很喜欢这首诗;一定要确保文中的单引号和三引号可以正确匹配):

>>> fout = open('poems/mcintyre/the_good_man', 'wt')
>>> fout.write('''Cheerful and happy was his mood,
... He to the poor was kind and good,
... And he oft' times did find them food,
... Also supplies of coal and wood,
... He never spake a word was rude,
... And cheer'd those did o'er sorrows brood,
... He passed away not understood,
... Because no poet in his lays
... Had penned a sonnet in his praise,
... 'Tis sad, but such is world's ways.
... ''')
344
>>> fout.close()

最后来看看目录中有什么:

>>> os.listdir('poems/mcintyre')
['the_good_man']

使用chdir()修改当前目录

你可以使用这个函数从一个目录跳转到另一个目录。我们从当前目录跳转到 poems 目录中:

>>> import os
>>> os.chdir('poems')
>>> os.listdir('.')
['mcintyre']

使用glob()列出匹配文件

glob() 函数会使用 Unix shell 的规则来匹配文件或者目录,而不是更复杂的正则表达式。具体规则如下所示:

  • * 会匹配任意名称(re 中是 .*)
  • ? 会匹配一个字符
  • [abc] 会匹配字符 a、b 和 c
  • [!abc] 会匹配除了 a、b 和 c 之外的所有字符

试着获取所有以 m 开头的文件和目录:

>>> import glob
>>> glob.glob('m*')
['mcintyre']

获取所有名称为两个字符的文件和目录:

>>> glob.glob('??')
[]

获取名称为 8 个字符并且以 m 开头和以 e 结尾的文件和目录:

>>> glob.glob('m??????e')
['mcintyre']

获取所有以 k、l 或者 m 开头并且以 e 结尾的文件和目录:

>>> glob.glob('[klm]*e')
['mcintyre']

程序和进程

当运行一个程序时,操作系统会创建一个进程。它会使用系统资源(CPU、内存和磁盘空间)和操作系统内核中的数据结构(文件、网络连接、用量统计等)。进程之间是互相隔离的,即一个进程既无法访问其他进程的内容,也无法操作其他进程。

操作系统会跟踪所有正在运行的进程,给每个进程一小段运行时间,然后切换到其他进程,这样既可以做到公平又可以响应用户操作。你可以在图形界面中查看进程状态,在 Mac OS X 上可以使用活动监视器,在 Windows 上可以使用任务管理器。

你也可以自己编写程序来获取进程信息。标准库模块 os 提供了一些常用的获取系统信息的函数。举例来说,下面的函数会获取正在运行的 Python 解释器的进程号和当前工作目录:

>>> import os
>>> os.getpid()
76051
>>> os.getcwd()
'/Users/williamlubanovic'

下面的函数会获取我的用户 ID 和用户组 ID:

>>> os.getuid()
501
>>> os.getgid()
20

使用subprocess创建进程

到目前为止,你看到的所有程序都是单进程程序。你可以使用 Python 标准库中的 subprocess 模块来启动和终止其他程序。如果只是想在 shell 中运行其他程序并获取它的输出(标准输出和标准错误输出),可以使用 getoutput() 函数。这里获取了 Unix date 程序的输出:

>>> import subprocess
>>> ret = subprocess.getoutput('date')
>>> ret
'Sun Mar 30 22:54:37 CDT 2014'

在进程执行完毕之前,你获取不到任何内容。因为 getoutput() 的参数是一个字符串,可以表示一个完整的 shell 命令,所以你可以在里面使用参数、管道、I/O 重定向 < 和 >,等等:

>>> ret = subprocess.getoutput('date -u')
>>> ret
'Mon Mar 31 03:55:01 UTC 2014'

把这个输出用管道传给 wc 命令,可以计算出一共有 1 行、6 个单词和 29 个字符:

>>> ret = subprocess.getoutput('date -u | wc')
>>> ret
'       1       6      29'

另一个类似的方法是 check_output(),可以接受一个命令和参数列表。默认情况下,它返回的不是字符串,而是字节类型的标准输出。此外,这个函数并没有使用 shell:

>>> ret = subprocess.check_output(['date', '-u'])
>>> ret
b'Mon Mar 31 04:01:50 UTC 2014\n'

如果要获取其他程序的退出状态,可以使用 getstatusoutput() 函数,它会返回一个包含状态码和输出的元组:

>>> ret = subprocess.getstatusoutput('date')
>>> ret
(0, 'Sat Jan 18 21:36:23 CST 2014')

如果只想要退出状态,可以使用 call():

>>> ret = subprocess.call('date')
Sat Jan 18 21:33:11 CST 2014
>>> ret
0

在 Unix 类操作系统中,退出状态 0 通常表示运行成功。

本例中,日期和时间被打印到输出中,但是并没有被我们的程序获取,所以 ret 中只有状态码。

你可以用两种方式来运行带参数的程序。第一种是在字符串中写明参数。我们的示例中使用的是 date -u,这会打印出 UTC 格式的当前日期和时间:

>>> ret = subprocess.call('date -u', shell=True)
Tue Jan 21 04:40:04 UTC 2014

需要加上参数 shell=True,这样函数就会用 shell 来执行命令,shell 会把 date -u 分割成单独的字符串并对通配符(比如 *,我们的例子中没有使用它)进行扩展。

第二种方式是传入一个参数列表,这样函数就不需要调用 shell:

>>> ret = subprocess.call(['date', '-u'])
Tue Jan 21 04:41:59 UTC 2014

使用multiprocessing创建进程

你可以在一个单独的进程中运行一个 Python 函数,也可以使用 multiprocessing 模块在一个程序中运行多个进程。下面的例子做了一些很无聊的事,把它存储为 mp.py,然后用 python mp.py 运行它:

import multiprocessing
import os

def do_this(what):
    whoami(what)

def whoami(what):
    print("Process %s says: %s" % (os.getpid(), what))

if __name__ == "__main__":
    whoami("I'm the main program")
    for n in range(4):
        p = multiprocessing.Process(target=do_this,
          args=("I'm function %s" % n,))
        p.start()

运行时得到如下输出:

Process 6224 says: I'm the main program
Process 6225 says: I'm function 0
Process 6226 says: I'm function 1
Process 6227 says: I'm function 2
Process 6228 says: I'm function 3

Process() 函数会创建一个新进程来运行 do_this() 函数。由于我们在一个循环中执行它,所以生成了 4 个执行 do_this() 的进程并在执行完毕后退出。

multiprocessing 模块真正的功能比这个例子中所展示的要强大得多。当你需要用多进程来减少运行时间时,它非常有用,比如下载需要抓取的网页、调整图片尺寸等。它支持任务队列和进程间通信,而且可以等待所有进程执行完毕。

使用terminate()终止进程

如果创建了一个或者多个进程并且想终止它们,可以使用 terminate()。下面的例子中,我们的进程会一直计数到百万,每次计数之后都会等待 1 秒并打印出相关信息。然而,主程序只会保持 5 秒耐心,之后就会终止进程:

import multiprocessing
import time
import os

def whoami(name):
    print("I'm %s, in process %s" % (name, os.getpid()))

def loopy(name):
    whoami(name)
    start = 1
    stop = 1000000
    for num in range(start, stop):
        print("\tNumber %s of %s. Honk!" % (num, stop))
        time.sleep(1)

if __name__ == "__main__":
    whoami("main")
    p = multiprocessing.Process(target=loopy, args=("loopy",))
    p.start()
    time.sleep(5)
    p.terminate()

运行程序时得到如下输出:

I'm main, in process 97080
I'm loopy, in process 97081
    Number 1 of 1000000. Honk!
    Number 2 of 1000000. Honk!
    Number 3 of 1000000. Honk!
    Number 4 of 1000000. Honk!
    Number 5 of 1000000. Honk!

日期和时间

程序员们需要花费大量时间来处理日期和时间。我们会讨论一些常见的问题,之后会介绍一些对应的最佳实践和能够帮助缓解问题的小技巧。

可以用多种方式来表示日期,甚至多到让人厌烦。即使是使用罗马历的英语国家也有很多表示日期的方法:

  • July 29 1984
  • 29 Jul 1984
  • 29/7/1984
  • 7/29/1984

表示日期的第一个问题就是二义性。从上面的例子可以很容易看出,7 表示月份,29 表示天数,因为月份不可能是 29。但是 1/6/2012 呢?它到底是 1 月 6 日还是 6 月 1 日呢?

罗马历中,月份的名字在不同语言中差别很大。在其他文化中,年份和月份本身的定义都有可能不一样。

闰年是另一个问题。你可能知道,每隔 4 年会出现一个闰年。不过你知道吗,年份是整百数时,必须是 400 的倍数才是闰年。下面的代码可以检测是否是闰年:

>>> import calendar
>>> calendar.isleap(1900)
False
>>> calendar.isleap(1996)
True
>>> calendar.isleap(1999)
False
>>> calendar.isleap(2000)
True
>>> calendar.isleap(2002)
False
>>> calendar.isleap(2004)
True

时间则有另外的问题,主要是因为时区以及夏时制。观察时区图会发现,时区是按照政治和历史因素分割的,并不是按照经度 15 度(360 度 /24)分割。不同国家每年夏时制的开始和结束时间也不一样。

Python 的标准库中有很多日期和时间模块:datetime、time、calendar、dateutil,等等。有些在功能上有重复,还有点不好理解。

datetime模块

首先介绍一下标准 datetime 模块。它定义了 4 个主要的对象,每个对象都有很多方法:

  • date 处理年、月、日
  • time 处理时、分、秒和分数
  • datetime 处理日期和时间同时出现的情况
  • timedelta 处理日期和 / 或时间间隔

你可以指定年、月、日,来创建一个 date 对象。这些值之后会变成对象的属性:

>>> from datetime import date
>>> halloween = date(2014, 10, 31)
>>> halloween
datetime.date(2014, 10, 31)
>>> halloween.day
31
>>> halloween.month
10
>>> halloween.year
2014

可以使用 isoformat() 方法打印一个 date 对象:

>>> halloween.isoformat()
'2014-10-31'

iso 是指 ISO 8601,一种表示日期和时间的国际标准。这个标准的显示顺序是从一般(年)到特殊(日)。它也可以对日期进行正确的排序:先按照年,然后是月,最后是日。我经常在程序中使用这种格式表示日期,还会在存储和日期相关的数据时当作文件名。下一节会介绍更复杂的 strptime() 和 strftime() 方法,它们分别用来解析和格式化日期。

下面的例子使用 today() 方法生成今天的日期:

>>> from datetime import date
>>> now = date.today()
>>> now
datetime.date(2014, 2, 2)

下面的例子使用 timedelta 对象来实现 date 的加法:

>>> from datetime import timedelta
>>> one_day = timedelta(days=1)
>>> tomorrow = now + one_day
>>> tomorrow
datetime.date(2014, 2, 3)
>>> now + 17*one_day
datetime.date(2014, 2, 19)
>>> yesterday = now - one_day
>>> yesterday
datetime.date(2014, 2, 1)

date 的范围 是 date.min( 年 = 1, 月 = 1, 日 = 1) 到 date.max( 年 = 9999, 月 = 12, 日 = 31)。因此,不能使用它来进行和历史或者天文相关的计算。

datetime 模块中的 time 对象用来表示一天中的时间:

>>> from datetime import time
>>> noon = time(12, 0, 0)
>>> noon
datetime.time(12, 0)
>>> noon.hour
12
>>> noon.minute
0
>>> noon.second
0
>>> noon.microsecond
0

参数的顺序是按照时间单位从大(时)到小(微秒)排列。如果没有参数,time 会默认全部使用 0。顺便说一句,能够存取微秒并不意味着你能从计算机中得到准确的微秒。每秒的准确度取决于硬件和操作系统中的很多因素。

datetime 对象既包含日期也包含时间。你可以直接创建一个 datetime 对象,如下所示,表示 2014 年 1 月 2 日上午 3:04,5 秒 6 微秒:

>>> from datetime import datetime
>>> some_day = datetime(2014, 1, 2, 3, 4, 5, 6)
>>> some_day
datetime.datetime(2014, 1, 2, 3, 4, 5, 6)

datetime 对象也有一个 isoformat() 方法:

>>> some_day.isoformat()
'2014-01-02T03:04:05.000006'

中间的 T 把日期和时间分割开。

datetime 有一个 now() 方法,可以用它获取当前日期和时间:

>>> from datetime import datetime
>>> now = datetime.now()
>>> now
datetime.datetime(2014, 2, 2, 23, 15, 34, 694988)
14
>>> now.month
2
>>> now.day
2
>>> now.hour
23
>>> now.minute
15
>>> now.second
34
>>> now.microsecond
694988

可以使用 combine() 方法把一个 date 对象和一个 time 对象合并成一个 datetime 对象:

>>> from datetime import datetime, time, date
>>> noon = time(12)
>>> this_day = date.today()
>>> noon_today = datetime.combine(this_day, noon)
>>> noon_today
datetime.datetime(2014, 2, 2, 12, 0)

也可以使用 date() 和 time() 方法从 datetime 中取出 date 和 time 部分:

>>> noon_today.date()
datetime.date(2014, 2, 2)
>>> noon_today.time()
datetime.time(12, 0)

使用time模块

Python 的 datetime 模块中有一个 time 对象,Python 还有一个单独的 time 模块,这有点令人困扰。此外,time 模块还有一个函数叫作 time()。

一种表示绝对时间的方法是计算从某个起始点开始的秒数。Unix 时间使用的是从 1970 年 1 月 1 日 0 点开始的秒数 。这个值通常被称为纪元,它是不同系统之间最简单的交换日期时间的方法。

time 模块的 time() 函数会返回当前时间的纪元值:

>>> import time
>>> now = time.time()
>>> now
1391488263.664645

数一下位数,从 1970 年新年到现在已经过去了超过十亿秒。时间都去哪儿了?

可以用 ctime() 把一个纪元值转换成一个字符串:

>>> time.ctime(now)
'Mon Feb  3 22:31:03 2014'

纪元值是不同系统(比如 JavaScript)之间交换日期和时间的一种非常有用的方法。但是,有时候想要真正的日期而不是一串数字,这时可以使用 struct_time 对象。localtime() 会返回当前系统时区下的时间,gmtime() 会返回 UTC 时间:

>>> time.localtime(now)
time.struct_time(tm_year=2014, tm_mon=2, tm_mday=3, tm_hour=22, tm_min=31,
tm_sec=3, tm_wday=0, tm_yday=34, tm_isdst=0)
>>> time.gmtime(now)
time.struct_time(tm_year=2014, tm_mon=2, tm_mday=4, tm_hour=4, tm_min=31,
tm_sec=3, tm_wday=1, tm_yday=35, tm_isdst=0)

在我所处的时区(中央时区),22:31 是 UTC(正式称呼是格林威治时间或者祖鲁时间)第二天的 04:31。如果省略 localtime() 或者 gmtime() 的参数,默认会返回当前时间。

对应上面两个函数的是 mktime(),它会把 struct_time 对象转换回纪元值:

>>> tm = time.localtime(now)
>>> time.mktime(tm)
1391488263.0

这个值和之前 now() 返回的纪元值并不完全相同,因为 struct_time 对象只能精确到秒。

一些建议:尽量多使用 UTC 来代替时区。UTC 是绝对时间,和时区无关。如果你有服务器,把它的时间设置为 UTC,不要使用本地时间。

读写日期和时间

isoformat() 并不是唯一一种可以打印日期和时间的方法。前面已经见过,time 模块中的 ctime() 函数可以把纪元转换成字符串:

>>> import time
>>> now = time.time()
>>> time.ctime(now)
'Mon Feb  3 21:14:36 2014'

也可以使用 strftime() 把日期和时间转换成字符串。这个方法在 datetime、date 和 time 对象中都有,在 time 模块中也有。strftime() 使用格式化字符串来指定输出,详见表 1。

[表1:strftime()的格式化字符串]

格式化字符串 日期/时间单元 范围
%Y 1900-...
%m 01-12
%B 月名 January,...
%b 月名缩写 Jan,...
%d 01-31
%A 星期 Sunday,...
%a 星期缩写 Sun,...
%H 时(24 小时制) 00-23
%I 时(12 小时制) 01-12
%p 上午 / 下午 AM, PM
%M 00-59
%S 00-59

数字都是左侧补零。

下面是 time 模块中的 strftime() 函数。它会把 struct_time 对象转换成字符串。我们首先定义格式化字符串 fmt,然后使用它:

>>> import time
>>> fmt = "It's %A, %B %d, %Y, local time %I:%M:%S%p"
>>> t = time.localtime()
>>> t
time.struct_time(tm_year=2014, tm_mon=2, tm_mday=4, tm_hour=19,
tm_min=28, tm_sec=38, tm_wday=1, tm_yday=35, tm_isdst=0)
>>> time.strftime(fmt, t)
"It's Tuesday, February 04, 2014, local time 07:28:38PM"

如果使用 date 对象的 strftime() 函数,只能获取日期部分,时间默认是午夜:

>>> from datetime import date
>>> some_day = date(2014, 7, 4)
>>> fmt = "It's %B %d, %Y, local time %I:%M:%S%p"
>>> some_day.strftime(fmt)
"It's Friday, July 04, 2014, local time 12:00:00AM"

对于 time 对象,只会转换时间部分:

>>> from datetime import time
>>> some_time = time(10, 35)
>>> some_time.strftime(fmt)
"It's Monday, January 01, 1900, local time 10:35:00AM"

显然,你肯定不想使用 time 对象的时间部分,因为它们毫无意义。

如果要把字符串转换成日期或者时间,可以对同样的格式化字符串使用 strptime() 函数。

这里不能使用正则表达式,字符串的非格式化部分(没有 % 的部分)必须完全匹配。假设可以匹配格式“年 - 月 - 日”,比如 2012-01-29,如果目标字符串中用空格代替破折号,解析时会发生什么呢?

>>> import time
>>> fmt = "%Y-%m-%d"
>>> time.strptime("2012 01 29", fmt)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Library/Frameworks/Python.framework/Versions/3.3/lib/
  python3.3/_strptime.py", line 494, in _strptime_time
    tt = _strptime(data_string, format)[0]
  File "/Library/Frameworks/Python.framework/Versions/3.3/lib/
  python3.3/_strptime.py", line 337, in _strptime
    (data_string, format))
ValueError: time data '2012 01 29' does not match format '%Y-%m-%d'

如果传入 strptime() 的字符串中有破折号会怎么样呢?

>>> time.strptime("2012-01-29", fmt)
time.struct_time(tm_year=2012, tm_mon=1, tm_mday=29, tm_hour=0, tm_min=0,
tm_sec=0, tm_wday=6, tm_yday=29, tm_isdst=-1)

现在可以正常解析了。

即使字符串看起来可以匹配格式,但如果超出取值范围也会报错:

>>> time.strptime("2012-13-29", fmt)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Library/Frameworks/Python.framework/Versions/3.3/lib/
  python3.3/_strptime.py", line 494, in _strptime_time
    tt = _strptime(data_string, format)[0]
  File "/Library/Frameworks/Python.framework/Versions/3.3/lib/
  python3.3/_strptime.py", line 337, in _strptime
    (data_string, format))
ValueError: time data '2012-13-29' does not match format '%Y-%m-%d'

名称通过你的 locale 设置,这是操作系统中的国际化设置。如果要打印出不同的月和日名称,可以通过 setlocale() 来修改。这个函数的第一个参数是 locale.LC_TIME,表示设置的是日期和时间,第二个参数是一个结合了语言和国家名称的缩写字符串。假设我们邀请了一些国际友人来参加万圣节派对,需要分别打印出美国英语、法语、德语、西班牙语和冰岛语中的月、日和具体的星期。

>>> import locale
>>> from datetime import date
>>> halloween = date(2014, 10, 31)
>>> for lang_country in['en_us', 'fr_fr', 'de_de', 'es_es', 'is_is',]:
...     locale.setlocale(locale.LC_TIME, lang_country)
...     halloween.strftime('%A, %B %d')
...
'en_us'
'Friday, October 31'
'fr_fr'
'Vendredi, octobre 31'
'de_de'
'Freitag, Oktober 31'
'es_es'
'viernes, octubre 31'
'is_is'
'föstudagur, október 31'
>>>

去哪儿找这些神奇的 lang_country 值呢?虽然不是特别靠谱,不过可以用下面的方式来获取所有值(有好几百种):

>>> import locale
>>> names = locale.locale_alias.keys()

我们从 names 中过滤出可以用在 setlocale() 中的名称,它们的格式和之前例子中的类似,两个字符构成的语言代码 加一个下划线和两个字符构成的国家代码

>>> good_names = [name for name in names if \
len(name) == 5 and name[2] == '_']

看看前 5 个是什么?

>>> good_names[:5]
['sr_cs', 'de_at', 'nl_nl', 'es_ni', 'sp_yu']

如果想获取所有的德国语言,试试这个:

>>> de = [name for name in good_names if name.startswith('de')]
>>> de
['de_at', 'de_de', 'de_ch', 'de_lu', 'de_be']

其他模块

如果你觉得标准库模块不好用或者没有你想要的功能,可以试试下面这些第三方模块。

这个模块有许多日期和时间函数,并提供了简单易用的 API。

这个模块可以解析绝大多数日期格式,并且可以很好地处理相对日期和时间。

这个模块会完善标准库中对于 ISO8601 格式的处理。

这个模块提供了许多时区函数。

参考文档