Merge remote-tracking branch 'origin/master'

This commit is contained in:
XiongNeng
2014-09-14 12:13:27 +08:00
17 changed files with 1117 additions and 29 deletions

View File

@@ -2,6 +2,10 @@
《Python Cookbook》 3rd Edition 翻译
=========================================================
在线预览地址: http://python3-cookbook.readthedocs.org/zh_CN/latest/
--------------------------------------------------------------
-----------------
译者的话
-----------------
@@ -28,6 +32,7 @@
#. 当前文档生成托管在 readthedocs_ 上
#. 生成的文档预览地址: python3-cookbook_
#. 使用了python官方文档主题 sphinx-rtd-theme_ 也是默认的主题default.
#. 书中所有代码均在python 3.4版本下面运行通过所有源码放在cookbook包下面
::

View File

@@ -0,0 +1,55 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
Topic: 利用numpy执行数组运算
Desc :
"""
import numpy as np
def array_numpy():
x = [1, 2, 3, 4]
y = [5, 6, 7, 8]
print(x * 2)
print(x + y)
# Numpy arrays
ax = np.array([1, 2, 3, 4])
ay = np.array([5, 6, 7, 8])
print(ax * 2)
print(ax + ay)
print(ax * ay)
print(f(ax))
print(np.sqrt(ax))
print(np.cos(ax))
# 大数组
grid = np.zeros(shape=(10000, 10000), dtype=float)
grid += 10
print(grid)
print(np.sin(grid))
# 二维数组的索引操作
a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
print(a)
print(a[1]) # Select row 1
print(a[:, 1]) # Select column 1
# Select a subregion and change it
print(a[1:3, 1:3])
a[1:3, 1:3] += 10
print(a)
# Broadcast a row vector across an operation on all rows
print(a + [100, 101, 102, 103])
# Conditional assignment on an array
print(np.where(a < 10, a, 10))
def f(x):
return 3 * x ** 2 - 2 * x + 7
if __name__ == '__main__':
array_numpy()

View File

@@ -0,0 +1,42 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
Topic: 矩阵和线性代数
Desc :
"""
import numpy as np
import numpy.linalg
def matrix_linear():
m = np.matrix([[1,-2,3],[0,4,5],[7,8,-9]])
print(m)
# Return transpose 转置矩阵
print(m.T)
# Return inverse # 逆矩阵
print(m.I)
# Create a vector and multiply
v = np.matrix([[2],[3],[4]])
print(v)
print(m * v)
# Determinant 行列式
print(numpy.linalg.det(m))
# Eigenvalues 特征值
print(numpy.linalg.eigvals(m))
# Solve for x in m*x = v
x = numpy.linalg.solve(m, v)
print(x)
print(m * x)
print(v)
if __name__ == '__main__':
matrix_linear()

View File

@@ -0,0 +1,43 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
Topic: 随机数
Desc :
"""
import random
def random_num():
values = [1, 2, 3, 4, 5, 6]
print(random.choice(values))
print(random.choice(values))
print(random.choice(values))
print(random.choice(values))
print(random.choice(values))
# 抽取样本
print(random.sample(values, 2))
print(random.sample(values, 2))
print(random.sample(values, 3))
# 打算顺序
random.shuffle(values)
print(values)
# 随机整数
print(random.randint(0,10))
print(random.randint(0,10))
print(random.randint(0,10))
print(random.randint(0,10))
# 随机二进制数的整数返回
print(random.getrandbits(200))
# 修改随机数生成的种子
random.seed() # Seed based on system time or os.urandom()
random.seed(12345) # Seed based on integer given
random.seed(b'bytedata') # Seed based on byte data
if __name__ == '__main__':
random_num()

View File

@@ -0,0 +1,51 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
Topic: 日期时间转换
Desc :
"""
from datetime import timedelta
from datetime import datetime
from dateutil.relativedelta import relativedelta
def date_time():
a = timedelta(days=2, hours=6)
b = timedelta(hours=4.5)
c = a + b
print(c.days)
print(c.seconds)
print(c.seconds / 3600)
print(c.total_seconds() / 3600)
# 具体的日期
a = datetime(2012, 9, 23)
print(a + timedelta(days=10))
b = datetime(2012, 12, 21)
d = b - a
print(d.days)
now = datetime.today()
print(now)
print(now + timedelta(minutes=10))
# 标准库中datetime模块
a = datetime(2012, 9, 23)
# a + timedelta(months=1) # 这个会报错
# 使用dateutil模块解决这个问题
print(a + relativedelta(months=+1))
print(a + relativedelta(months=+4))
# Time between two dates
b = datetime(2012, 12, 21)
d = b - a
print(d)
d = relativedelta(b, a)
print(d)
print(d.months, d.days)
if __name__ == '__main__':
date_time()

View File

@@ -0,0 +1,51 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
Topic: 最后的周五
Desc :
"""
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
from dateutil.rrule import *
weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday',
'Friday', 'Saturday', 'Sunday']
def get_previous_byday(dayname, start_date=None):
if start_date is None:
start_date = datetime.today()
day_num = start_date.weekday()
day_num_target = weekdays.index(dayname)
days_ago = (7 + day_num - day_num_target) % 7
if days_ago == 0:
days_ago = 7
target_date = start_date - timedelta(days=days_ago)
return target_date
def last_friday():
print(datetime.today())
print(get_previous_byday('Monday'))
print(get_previous_byday('Tuesday'))
print(get_previous_byday('Friday'))
print(get_previous_byday('Saturday'))
# 显式的传递开始日期
print(get_previous_byday('Sunday', datetime(2012, 12, 21)))
# 使用dateutil模块
d = datetime.now()
# 下一个周五
print(d + relativedelta(weekday=FR))
# 上一个周五
print(d + relativedelta(weekday=FR(-1)))
# 下一个周六, 为什么如果今天是周六,下一个/上一个都返回今天的日期??
print(d + relativedelta(weekday=SA))
# 上一个周六
print(d + relativedelta(weekday=SA(-1)))
if __name__ == '__main__':
last_friday()

View File

@@ -0,0 +1,40 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
Topic: 计算当前月份的日期范围
Desc :
"""
from datetime import datetime, date, timedelta
import calendar
def get_month_range(start_date=None):
if start_date is None:
start_date = date.today().replace(day=1)
_, days_in_month = calendar.monthrange(start_date.year, start_date.month)
end_date = start_date + timedelta(days=days_in_month)
return (start_date, end_date)
def date_range(start, stop, step):
while start < stop:
yield start
start += step
def month_range():
a_day = timedelta(days=1)
first_day, last_day = get_month_range()
while first_day < last_day:
print(first_day)
first_day += a_day
# 使用生成器
for d in date_range(datetime(2012, 9, 1), datetime(2012, 10, 1),
timedelta(hours=6)):
print(d)
if __name__ == '__main__':
month_range()

View File

@@ -0,0 +1,30 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
Topic: 字符串转换为datetime
Desc :
"""
from datetime import datetime
def str_datetime():
text = '2012-09-20'
y = datetime.strptime(text, '%Y-%m-%d')
z = datetime.now()
diff = z - y
print(diff)
print(z)
nice_z = datetime.strftime(z, '%A %B %d, %Y')
print(nice_z)
def parse_ymd(s):
'''自定义解析,要快很多'''
year_s, mon_s, day_s = s.split('-')
return datetime(int(year_s), int(mon_s), int(day_s))
if __name__ == '__main__':
str_datetime()

View File

@@ -0,0 +1,50 @@
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
Topic: 日期时区和本地化
Desc :
"""
from datetime import datetime, timedelta
from pytz import timezone
import pytz
def tz_local():
d = datetime(2012, 12, 21, 9, 30, 0)
print(d)
# Localize the date for Chicago
central = timezone('US/Central')
loc_d = central.localize(d)
print(loc_d)
# Convert to Bangalore time
bang_d = loc_d.astimezone(timezone('Asia/Kolkata'))
print(bang_d)
# 夏令时
d = datetime(2013, 3, 10, 1, 45)
loc_d = central.localize(d)
print(loc_d)
later = loc_d + timedelta(minutes=30)
print(later)
# 使用normalize修正这个问题
later = central.normalize(loc_d + timedelta(minutes=30))
print(later)
# 一个普遍策略是先转换为UTC时间使用UTC时间来进行计算
print(loc_d)
utc_d = loc_d.astimezone(pytz.utc)
print(utc_d)
later_utc = utc_d + timedelta(minutes=30)
# 转回到本地时间
print(later_utc.astimezone(central))
# 根据ISO 3166国家代码查找时区名称
print(pytz.country_timezones['IN'])
if __name__ == '__main__':
tz_local()

View File

@@ -1,18 +1,180 @@
============================
========================
3.9 大型数组运算
============================
========================
----------
问题
----------
todo...
你需要在大数据集(比如数组或网格)上面执行计算。
|
----------
解决方案
----------
todo...
涉及到数组的重量级运算操作可以使用NumPy库。
NumPy的一个主要特征是它会给Python提供一个数组对象相比标准的Python列表而已更适合用来做数学运算。
下面是一个简单的小例子向你展示标准列表对象和NumPy数组对象之间的差别
.. code-block:: python
>>> # Python lists
>>> x = [1, 2, 3, 4]
>>> y = [5, 6, 7, 8]
>>> x * 2
[1, 2, 3, 4, 1, 2, 3, 4]
>>> x + 10
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate list (not "int") to list
>>> x + y
[1, 2, 3, 4, 5, 6, 7, 8]
>>> # Numpy arrays
>>> import numpy as np
>>> ax = np.array([1, 2, 3, 4])
>>> ay = np.array([5, 6, 7, 8])
>>> ax * 2
array([2, 4, 6, 8])
>>> ax + 10
array([11, 12, 13, 14])
>>> ax + ay
array([ 6, 8, 10, 12])
>>> ax * ay
array([ 5, 12, 21, 32])
>>>
正如所见,两种方案中数组的基本数学运算结果并不相同。
特别的numpy中的标量运算(比如ax * 2或ax + 10)会作用在每一个元素上。
另外,当两个操作数都是数组的时候执行元素对等位置计算,并最终生成一个新的数组。
对整个数组中所有元素同时执行数学运算可以使得作用在整个数组上的函数运算简单而又快速。
比如,如果你想计算多项式的值,可以这样做:
.. code-block:: python
>>> def f(x):
... return 3*x**2 - 2*x + 7
...
>>> f(ax)
array([ 8, 15, 28, 47])
>>>
NumPy还为数组操作提供了大量的通用函数这些函数可以作为math模块中类似函数的替代。比如
.. code-block:: python
>>> np.sqrt(ax)
array([ 1. , 1.41421356, 1.73205081, 2. ])
>>> np.cos(ax)
array([ 0.54030231, -0.41614684, -0.9899925 , -0.65364362])
>>>
使用这些通用函数要比循环数组并使用math模块中的函数执行计算要快的多。
因此只要有可能的话尽量选择numpy的数组方案。
底层实现中NumPy数组使用了C或者Fortran语言的机制分配内存。
也就是说,它们是一个非常大的连续的并由同类型数据组成的内存区域。
所以你可以构造一个比普通Python列表大的多的数组。比如如果你想构造一个10,000*10,000的浮点数二维网格很轻松
.. code-block:: python
>>> grid = np.zeros(shape=(10000,10000), dtype=float)
>>> grid
array([[ 0., 0., 0., ..., 0., 0., 0.],
[ 0., 0., 0., ..., 0., 0., 0.],
[ 0., 0., 0., ..., 0., 0., 0.],
...,
[ 0., 0., 0., ..., 0., 0., 0.],
[ 0., 0., 0., ..., 0., 0., 0.],
[ 0., 0., 0., ..., 0., 0., 0.]])
>>>
所有的普通操作还是会同时作用在所有元素上:
.. code-block:: python
>>> grid += 10
>>> grid
array([[ 10., 10., 10., ..., 10., 10., 10.],
[ 10., 10., 10., ..., 10., 10., 10.],
[ 10., 10., 10., ..., 10., 10., 10.],
...,
[ 10., 10., 10., ..., 10., 10., 10.],
[ 10., 10., 10., ..., 10., 10., 10.],
[ 10., 10., 10., ..., 10., 10., 10.]])
>>> np.sin(grid)
array([[-0.54402111, -0.54402111, -0.54402111, ..., -0.54402111,
-0.54402111, -0.54402111],
[-0.54402111, -0.54402111, -0.54402111, ..., -0.54402111,
-0.54402111, -0.54402111],
[-0.54402111, -0.54402111, -0.54402111, ..., -0.54402111,
-0.54402111, -0.54402111],
...,
[-0.54402111, -0.54402111, -0.54402111, ..., -0.54402111,
-0.54402111, -0.54402111],
[-0.54402111, -0.54402111, -0.54402111, ..., -0.54402111,
-0.54402111, -0.54402111],
[-0.54402111, -0.54402111, -0.54402111, ..., -0.54402111,
-0.54402111, -0.54402111]])
>>>
关于NumPy有一点需要特别的主意那就是它扩展Python列表的索引功能 - 特别是对于多维数组。
为了说明清楚,先构造一个简单的二维数组并试着做些试验:
.. code-block:: python
>>> a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
>>> a
array([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
>>> # Select row 1
>>> a[1]
array([5, 6, 7, 8])
>>> # Select column 1
>>> a[:,1]
array([ 2, 6, 10])
>>> # Select a subregion and change it
>>> a[1:3, 1:3]
array([[ 6, 7],
[10, 11]])
>>> a[1:3, 1:3] += 10
>>> a
array([[ 1, 2, 3, 4],
[ 5, 16, 17, 8],
[ 9, 20, 21, 12]])
>>> # Broadcast a row vector across an operation on all rows
>>> a + [100, 101, 102, 103]
array([[101, 103, 105, 107],
[105, 117, 119, 111],
[109, 121, 123, 115]])
>>> a
array([[ 1, 2, 3, 4],
[ 5, 16, 17, 8],
[ 9, 20, 21, 12]])
>>> # Conditional assignment on an array
>>> np.where(a < 10, a, 10)
array([[ 1, 2, 3, 4],
[ 5, 10, 10, 8],
[ 9, 10, 10, 10]])
>>>
|
----------
讨论
----------
todo...
NumPy是Python领域中很多科学与工程库的基础同时也是被广泛使用的最大最复杂的模块。
即便如此,在刚开始的时候通过一些简单的例子和玩具程序也能帮我们完成一些有趣的事情。
通常我们导入NumPy模块的时候会使用语句import numpy as np。
这样的话你就不用再你的程序里面一遍遍的敲入numpy只需要输入np就行了节省了不少时间。
如果想获取更多的信息你当然得去NumPy官网逛逛了网址是 http://www.numpy.org

View File

@@ -5,14 +5,85 @@
----------
问题
----------
todo...
你需要执行矩阵和线性代数运算,比如矩阵乘法、寻找行列式、求解线性方程组等等。
|
----------
解决方案
----------
todo...
NumPy库有一个矩阵对象可以用来解决这个问题。
矩阵类似于3.9小节中数组对象,但是遵循线性代数的计算规则。下面的一个例子展示了矩阵的一些基本特性:
.. code-block:: python
>>> import numpy as np
>>> m = np.matrix([[1,-2,3],[0,4,5],[7,8,-9]])
>>> m
matrix([[ 1, -2, 3],
[ 0, 4, 5],
[ 7, 8, -9]])
>>> # Return transpose
>>> m.T
matrix([[ 1, 0, 7],
[-2, 4, 8],
[ 3, 5, -9]])
>>> # Return inverse
>>> m.I
matrix([[ 0.33043478, -0.02608696, 0.09565217],
[-0.15217391, 0.13043478, 0.02173913],
[ 0.12173913, 0.09565217, -0.0173913 ]])
>>> # Create a vector and multiply
>>> v = np.matrix([[2],[3],[4]])
>>> v
matrix([[2],
[3],
[4]])
>>> m * v
matrix([[ 8],
[32],
[ 2]])
>>>
可以在numpy.linalg子包中找到更多的操作函数比如
.. code-block:: python
>>> import numpy.linalg
>>> # Determinant
>>> numpy.linalg.det(m)
-229.99999999999983
>>> # Eigenvalues
>>> numpy.linalg.eigvals(m)
array([-13.11474312, 2.75956154, 6.35518158])
>>> # Solve for x in mx = v
>>> x = numpy.linalg.solve(m, v)
>>> x
matrix([[ 0.96521739],
[ 0.17391304],
[ 0.46086957]])
>>> m * x
matrix([[ 2.],
[ 3.],
[ 4.]])
>>> v
matrix([[2],
[3],
[4]])
>>>
|
----------
讨论
----------
todo...
很显然线性代数是个非常大的主题,已经超出了本书能讨论的范围。
但是如果你需要操作数组和向量的话NumPy是一个不错的入口点。
可以访问NumPy官网 http://www.numpy.org 获取更多信息。

View File

@@ -5,14 +5,114 @@
----------
问题
----------
todo...
你想从一个序列中随机抽取若干元素,或者想生成几个随机数。
|
----------
解决方案
----------
todo...
random模块有大量的函数用来产生随机数和随机选择元素。
比如要想从一个序列中随机的抽取一个元素可以使用random.choice()
.. code-block:: python
>>> import random
>>> values = [1, 2, 3, 4, 5, 6]
>>> random.choice(values)
2
>>> random.choice(values)
3
>>> random.choice(values)
1
>>> random.choice(values)
4
>>> random.choice(values)
6
>>>
为了提取出N个不同元素的样本用来做进一步的操作可以使用random.sample()
.. code-block:: python
>>> random.sample(values, 2)
[6, 2]
>>> random.sample(values, 2)
[4, 3]
>>> random.sample(values, 3)
[4, 3, 1]
>>> random.sample(values, 3)
[5, 4, 1]
>>>
如果你仅仅只是想打乱序列中元素的顺序可以使用random.shuffle()
.. code-block:: python
>>> random.shuffle(values)
>>> values
[2, 4, 6, 5, 3, 1]
>>> random.shuffle(values)
>>> values
[3, 5, 2, 1, 6, 4]
>>>
生成随机整数请使用random.randint()
.. code-block:: python
>>> random.randint(0,10)
2
>>> random.randint(0,10)
5
>>> random.randint(0,10)
0
>>> random.randint(0,10)
7
>>> random.randint(0,10)
10
>>> random.randint(0,10)
3
>>>
为了生成0到1范围内均匀分布的浮点数使用random.random()
.. code-block:: python
>>> random.random()
0.9406677561675867
>>> random.random()
0.133129581343897
>>> random.random()
0.4144991136919316
>>>
如果要获取N位随机位(二进制)的整数使用random.getrandbits()
.. code-block:: python
>>> random.getrandbits(200)
335837000776573622800628485064121869519521710558559406913275
>>>
|
----------
讨论
----------
todo...
random模块使用Mersenne Twister算法来计算生成随机数。这是一个确定性算法
但是你可以通过random.seed()函数修改初始化种子。比如:
.. code-block:: python
random.seed() # Seed based on system time or os.urandom()
random.seed(12345) # Seed based on integer given
random.seed(b'bytedata') # Seed based on byte data
除了上述介绍的功能random模块还包含基于均匀分布、高斯分布和其他分布的随机数生成函数。
比如random.uniform()计算均匀分布随机数random.gauss()计算正态分布随机数。
对于其他的分布情况请参考在线文档。
在random模块中的函数不应该用在和密码学相关的程序中。
如果你确实需要类似的功能可以使用ssl模块中相应的函数。
比如ssl.RAND_bytes()可以用来生成一个安全的随机字节序列。

View File

@@ -5,14 +5,104 @@
----------
问题
----------
todo...
你需要执行简单的时间转换,比如天到秒,小时到分钟等的转换。
|
----------
解决方案
----------
todo...
为了执行不同时间单位的转换和计算请使用datetime模块。
比如为了表示一个时间段可以创建一个timedelta实例就像下面这样
.. code-block:: python
>>> from datetime import timedelta
>>> a = timedelta(days=2, hours=6)
>>> b = timedelta(hours=4.5)
>>> c = a + b
>>> c.days
2
>>> c.seconds
37800
>>> c.seconds / 3600
10.5
>>> c.total_seconds() / 3600
58.5
>>>
如果你想表示指定的日期和时间先创建一个datetime实例然后使用标准的数学运算来操作它们。比如
.. code-block:: python
>>> from datetime import datetime
>>> a = datetime(2012, 9, 23)
>>> print(a + timedelta(days=10))
2012-10-03 00:00:00
>>>
>>> b = datetime(2012, 12, 21)
>>> d = b - a
>>> d.days
89
>>> now = datetime.today()
>>> print(now)
2012-12-21 14:54:43.094063
>>> print(now + timedelta(minutes=10))
2012-12-21 15:04:43.094063
>>>
在计算的时候需要注意的是datetime会自动处理闰年。比如
.. code-block:: python
>>> a = datetime(2012, 3, 1)
>>> b = datetime(2012, 2, 28)
>>> a - b
datetime.timedelta(2)
>>> (a - b).days
2
>>> c = datetime(2013, 3, 1)
>>> d = datetime(2013, 2, 28)
>>> (c - d).days
1
>>>
|
----------
讨论
----------
todo...
对大多数基本的日期和时间处理问题datetime模块以及足够了。
如果你需要执行更加复杂的日期操作,比如处理失去,模糊时间范围,节假日计算等等,
可以考虑使用 `dateutil模块 <http://pypi.python.org/pypi/python-dateutil>`_
许多类似的时间计算可以使用dateutil.relativedelta()函数代替。
但是,有一点需要注意的就是,它会在处理月份(还有它们的天数差距)的时候填充间隙。看例子最清楚:
.. code-block:: python
>>> a = datetime(2012, 9, 23)
>>> a + timedelta(months=1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'months' is an invalid keyword argument for this function
>>>
>>> from dateutil.relativedelta import relativedelta
>>> a + relativedelta(months=+1)
datetime.datetime(2012, 10, 23, 0, 0)
>>> a + relativedelta(months=+4)
datetime.datetime(2013, 1, 23, 0, 0)
>>>
>>> # Time between two dates
>>> b = datetime(2012, 12, 21)
>>> d = b - a
>>> d
datetime.timedelta(89)
>>> d = relativedelta(b, a)
>>> d
relativedelta(months=+2, days=+28)
>>> d.months
2
>>> d.days
28
>>>

View File

@@ -5,14 +5,90 @@
----------
问题
----------
todo...
你需要查找星期中某一天最后出现的日期,比如星期五。
|
----------
解决方案
----------
todo...
Python的datetime模块中有工具函数和类可以帮助你执行这样的计算。
下面是对类似这样的问题的一个通用解决方案:
.. code-block:: python
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
Topic: 最后的周五
Desc :
"""
from datetime import datetime, timedelta
weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday',
'Friday', 'Saturday', 'Sunday']
def get_previous_byday(dayname, start_date=None):
if start_date is None:
start_date = datetime.today()
day_num = start_date.weekday()
day_num_target = weekdays.index(dayname)
days_ago = (7 + day_num - day_num_target) % 7
if days_ago == 0:
days_ago = 7
target_date = start_date - timedelta(days=days_ago)
return target_date
在交互式解释器中使用如下:
.. code-block:: python
>>> datetime.today() # For reference
datetime.datetime(2012, 8, 28, 22, 4, 30, 263076)
>>> get_previous_byday('Monday')
datetime.datetime(2012, 8, 27, 22, 3, 57, 29045)
>>> get_previous_byday('Tuesday') # Previous week, not today
datetime.datetime(2012, 8, 21, 22, 4, 12, 629771)
>>> get_previous_byday('Friday')
datetime.datetime(2012, 8, 24, 22, 5, 9, 911393)
>>>
可选的start_date参数可以由另外一个datetime实例来提供。比如
.. code-block:: python
>>> get_previous_byday('Sunday', datetime(2012, 12, 21))
datetime.datetime(2012, 12, 16, 0, 0)
>>>
|
----------
讨论
----------
todo...
上面的算法原理是这样的:先将开始日期和目标日期映射到星期数组的位置上(星期一索引为0)
然后通过模运算计算出目标日期要经过多少天才能到达开始日期。然后用开始日期减去那个时间差即得到结果日期。
如果你要像这样执行大量的日期计算的话你最好安装第三方包python-dateutil来代替。
比如下面是是使用dateutil模块中的relativedelta()函数执行同样的计算:
.. code-block:: python
>>> from datetime import datetime
>>> from dateutil.relativedelta import relativedelta
>>> from dateutil.rrule import *
>>> d = datetime.now()
>>> print(d)
2012-12-23 16:31:52.718111
>>> # Next Friday
>>> print(d + relativedelta(weekday=FR))
2012-12-28 16:31:52.718111
>>>
>>> # Last Friday
>>> print(d + relativedelta(weekday=FR(-1)))
2012-12-21 16:31:52.718111
>>>

View File

@@ -1,18 +1,103 @@
============================
3.14 寻找当前月份的某个日期范围
============================
==========================
3.14 计算当前月份的日期范围
==========================
----------
问题
----------
todo...
你的代码需要在当前月份中循环每一天,想找到一个计算这个日期范围的高效方法。
|
----------
解决方案
----------
todo...
在这样的日期上循环并需要事先构造一个包含所有日期的列表。
你可以先计算出开始日期和结束日期然后在你步进的时候使用datetime.timedelta对象递增这个日期变量即可。
下面是一个接受任意datetime对象并返回一个由当前月份开始日和下个月开始日组成的元组对象。
.. code-block:: python
from datetime import datetime, date, timedelta
import calendar
def get_month_range(start_date=None):
if start_date is None:
start_date = date.today().replace(day=1)
_, days_in_month = calendar.monthrange(start_date.year, start_date.month)
end_date = start_date + timedelta(days=days_in_month)
return (start_date, end_date)
有了这个就可以很容易的在返回的日期范围上面做循环操作了:
.. code-block:: python
>>> a_day = timedelta(days=1)
>>> first_day, last_day = get_month_range()
>>> while first_day < last_day:
... print(first_day)
... first_day += a_day
...
2012-08-01
2012-08-02
2012-08-03
2012-08-04
2012-08-05
2012-08-06
2012-08-07
2012-08-08
2012-08-09
#... and so on...
|
----------
讨论
----------
todo...
上面的代码先计算出一个对应月份第一天的日期。
一个快速的方法就是使用date或datetime对象的replace()方法简单的将days属性设置成1即可。
replace()方法一个好处就是它会创建和你开始传入对象类型相同的对象。
所以如果输入参数是一个date实例那么结果也是一个date实例。
同样的如果输入是一个datetime实例那么你得到的就是一个datetime实例。
然后使用calendar.monthrange()函数来找出该月的总天数。
任何时候只要你想获得日历信息那么calendar模块就非常有用了。
monthrange()函数会返回包含星期和该月天数的元组。
一旦该月的天数已知了,那么结束日期就可以通过在开始日期上面加上这个天数获得。
有个需要注意的是结束日期并不包含在这个日期范围内(事实上它是下个月的开始日期)。
这个和Python的slice与range操作行为保持一致同样也不包含结尾。
为了在日期范围上循环,要使用到标准的数学和比较操作。
比如可以利用timedelta实例来递增日期小于号<用来检查一个日期是否在结束日期之前。
理想情况下如果能为日期迭代创建一个同内置的range()函数一样的函数就好了。
幸运的是,可以使用一个生成器来很容易的实现这个目标:
.. code-block:: python
def date_range(start, stop, step):
while start < stop:
yield start
start += step
下面是使用这个生成器的例子:
.. code-block:: python
>>> for d in date_range(datetime(2012, 9, 1), datetime(2012,10,1),
timedelta(hours=6)):
... print(d)
...
2012-09-01 00:00:00
2012-09-01 06:00:00
2012-09-01 12:00:00
2012-09-01 18:00:00
2012-09-02 00:00:00
2012-09-02 06:00:00
...
>>>
这种实现之所以这么简单还得归功于Python中的日期和时间能够使用标准的数学和比较操作符来进行运算。

View File

@@ -5,14 +5,57 @@
----------
问题
----------
todo...
你的应用程序接受字符串格式的输入但是你想将它们转换为datetime对象以便在上面执行非字符串操作。
|
----------
解决方案
----------
todo...
使用Python的标准模块datetime可以很容易的解决这个问题。比如
.. code-block:: python
>>> from datetime import datetime
>>> text = '2012-09-20'
>>> y = datetime.strptime(text, '%Y-%m-%d')
>>> z = datetime.now()
>>> diff = z - y
>>> diff
datetime.timedelta(3, 77824, 177393)
>>>
|
----------
讨论
----------
todo...
datetime.strptime()方法支持很多的格式化代码,比如%Y代表4位数年份%m代表两位数月份。
还有一点值得注意的是这些格式化占位符也可以反过来使用,将日期输出为指定的格式字符串形式。
比如假设你的代码中生成了一个datetime对象
你想将它格式化为漂亮易读形式后放在自动生成的信件或者报告的顶部:
.. code-block:: python
>>> z
datetime.datetime(2012, 9, 23, 21, 37, 4, 177393)
>>> nice_z = datetime.strftime(z, '%A %B %d, %Y')
>>> nice_z
'Sunday September 23, 2012'
>>>
还有一点需要注意的是strptime()的性能要比你想象中的差很多,
因为它是使用纯Python实现并且必须处理所有的系统本地设置。
如果你要在代码中需要解析大量的日期并且已经知道了日期字符串的确切格式,可以自己实现一套解析方案来获取更好的性能。
比如,如果你已经知道所以日期格式是"YYYY-MM-DD",你可以像下面这样实现一个解析函数:
.. code-block:: python
from datetime import datetime
def parse_ymd(s):
year_s, mon_s, day_s = s.split('-')
return datetime(int(year_s), int(mon_s), int(day_s))
实际测试中这个函数比datetime.strptime()快7倍多。
如果你要处理大量的涉及到日期的数据的话,那么最好考虑下这个方案!

View File

@@ -5,14 +5,108 @@
----------
问题
----------
todo...
你有一个安排在2012年9月21日早上9:30的电话会议地点在芝加哥。
而你的朋友在印度的班加罗尔,那么他应该在当地时间几点参加这个会议呢?
----------
解决方案
----------
todo...
对几乎所有涉及到时区的问题你都应该使用pytz模块。这个包提供了Olson时区数据库
它是时区信息的事实上的标准,在很多语言和操作系统里面都可以找到。
pytz模块一个主要用途是将datetime库创建的简单日期对象本地化。比如下面如何表示一个芝加哥时间的示例
.. code-block:: python
>>> from datetime import datetime
>>> from pytz import timezone
>>> d = datetime(2012, 12, 21, 9, 30, 0)
>>> print(d)
2012-12-21 09:30:00
>>>
>>> # Localize the date for Chicago
>>> central = timezone('US/Central')
>>> loc_d = central.localize(d)
>>> print(loc_d)
2012-12-21 09:30:00-06:00
>>>
一旦日期被本地化了, 它就可以转换为其他时区的时间了。
为了得到班加罗尔对应的时间,你可以这样做:
.. code-block:: python
>>> # Convert to Bangalore time
>>> bang_d = loc_d.astimezone(timezone('Asia/Kolkata'))
>>> print(bang_d)
2012-12-21 21:00:00+05:30
>>>
如果你打算在本地化日期上执行计算,你需要特别注意夏令时转换和其他细节。
比如在2013年美国标准夏令时时间开始于本地时间3月13日凌晨2:00(在那时,时间向前跳过一小时)。
如果你正在执行本地计算,你会得到一个错误。比如:
.. code-block:: python
>>> d = datetime(2013, 3, 10, 1, 45)
>>> loc_d = central.localize(d)
>>> print(loc_d)
2013-03-10 01:45:00-06:00
>>> later = loc_d + timedelta(minutes=30)
>>> print(later)
2013-03-10 02:15:00-06:00 # WRONG! WRONG!
>>>
结果错误是因为它并没有考虑在本地时间中有一小时的跳跃。
为了修正这个错误可以使用时区对象normalize()方法。比如:
.. code-block:: python
>>> from datetime import timedelta
>>> later = central.normalize(loc_d + timedelta(minutes=30))
>>> print(later)
2013-03-10 03:15:00-05:00
>>>
|
----------
讨论
----------
todo...
为了不让你被这些东东弄的晕头转向处理本地化日期的通常的策略先将所有日期转换为UTC时间
并用它来执行所有的中间存储和操作。比如:
.. code-block:: python
>>> print(loc_d)
2013-03-10 01:45:00-06:00
>>> utc_d = loc_d.astimezone(pytz.utc)
>>> print(utc_d)
2013-03-10 07:45:00+00:00
>>>
一旦转换为UTC你就不用去担心跟夏令时相关的问题了。
因此,你可以跟之前一样放心的执行常见的日期计算。
当你想将输出变为本地时间的时候,使用合适的时区去转换下就行了。比如:
.. code-block:: python
>>> later_utc = utc_d + timedelta(minutes=30)
>>> print(later_utc.astimezone(central))
2013-03-10 03:15:00-05:00
>>>
当涉及到时区操作的时候,有个问题就是我们如何得到时区的名称。
比如在这个例子中我们如何知道“Asia/Kolkata”就是印度对应的时区名呢
为了查找可以使用ISO 3166国家代码作为关键字去查阅字典pytz.country_timezones。比如
.. code-block:: python
>>> pytz.country_timezones['IN']
['Asia/Kolkata']
>>>
当你阅读到这里的时候有可能pytz模块以及不再建议使用了因为PEP431提出了更先进的时区支持。
但是这里谈到的很多问题还是有参考价值的(比如使用UTC日期的建议等)。