2007-08-31

 

Python Cookbook 4.7 在2维列表中删除或重排某些列

需求:

你有一个二维列表,需要重新获得一个列表,其中行不变,而将某些列重排或者删除.

讨论:

使用列表递推式可以处理这个问题,比如你有如下的列表:

listOfRows = [ [1,2,3,4], [5,6,7,8], [9,10,11,12] ]

现在你需要将第二列删除,并将第四列和第三列互换位置,用一个列表递推式就能出色的完成任务:

newList = [ [row[0], row[3], row[2]] for row in listOfRows ]

结果如下:

[[1, 4, 3], [5, 8, 7], [9, 12, 11]]

另外的办法,稍微复杂一些,是使用临时的列表来保存要调整的位置:

newList = [ [row[ci] for ci in (0, 3, 2)] for row in listofRows ]

我经常使用列表的列表来表示二维数组,而在处理这类问题的过程中经常遇到列的处理,比如重排某些列,忽略其中的一些数值等.使用列表递推式来处理这一类问题看起来不是那么显然.
列表递推式创建一个新列表,而不是改变原来的.但是即使你需要修改现有的列表,使用列表递推式然后赋值给现有列表也是很好的方法.比如,你想修改listOfRows,就按照本节的需求,你可能这样写:

listOfRows[:] = [ [row[0], row[3], row[2]] for row in listOfRows ]

但是请注意,遇到这样的问题,请是用本节给出的第二个列子的办法,用一个临时列表保存列的位置,而不是硬编码.也许你不喜欢将两个递推式嵌套起来,但是它会更安全一些.如果你使用这种方法,还能得到附加的好处,因为增强的灵活性,你可以将它封装成一个函数,将想要调整的顺序做为参数传递进去:

def pick_and_reorder_columns(listofRows, column_indexes):
    return [ [row[ci] for ci in column_indexes] for row in listofRows ]
columns = 0, 3, 2
newListOfPandas = pick_and_reorder_columns(oldListOfPandas, columns)
newListOfCats = pick_and_reorder_columns(oldListOfCats, columns)

上面的代码和前面的 一样,调整了列表中列的顺序,但是它操作的是两个列表.
最后要注意的,有些人喜欢将列表递推式中的变量部分替换成函数返回值,而不是直接使用,比如在:

[row[ci] for ci in column_indexes]

可以使用map函数,用__getitem__来代替[],所以可以这样写:

map(row._ _getitem_ _, column_indexes)

取决于你的Python版本,也许map版的会更快一些.不过我坚持认为最简单的列表递推式是最好的 .


2007-08-29

 

Python Cookbook 4.6 将嵌套列表转换为单列表

需求:

有些列表的元素可能还是一个列表,而这样的嵌套可能持续下去.你需要遍历列表的每一个元素,并将嵌套的列表元素转换为单列表(及列表中不存在列表).

讨论:

我们需要首先讨论一下什么元素是"子列表",并要将它扩展到什么标准.简单来说 ,我们需要提供一个断言来说明什么样的元素是需要我们扩展的.(断言是一个函数,可以对于任何对象调用,它返回它的真实性,在这里,true表示可以扩展,false表示不能).默认的情况,我们可以说列表(list)和元素是可以扩展(tuple)的,而其它的类型不可以.然后写下下面的代码:
def list_or_tuple(x):
return isinstance(x, (list, tuple))
def flatten(sequence, to_expand=list_or_tuple):
for item in sequence:
if to_expand(item):
for subitem in flatten(item, to_expand):
yield subitem
else:
yield item
在嵌套列表中处理,或者说,在一个树中遍历叶子节点,是很多应用程序都会遇到的问题.你从一个嵌套结构开始,而只关心里面的元素,并不关心它的结构,比如:
for x in flatten([1, 2, [3, [  ], 4, [5, 6], 7, [8,], ], 9]):
print x,
输出1 2 3 4 5 6 7 8 9.

在这类问题中,核心问题是需要知道什么元素是需要扩展的,或者对什么元素使用yield,并不是想像中的那么明显.所以我避开了这个问题,将它交给一个代理函数,有用户决定什么类型是需要扩展的,默认只处理列表类型和元组类型.
在flatten模块中,我们需要再写一个断言函数,因为用户可能希望扩展一切具有迭代特性的对象,当然除了字符串,因为它是不可修改的,而且本身就是常量,而不是子列表.
为了判断一个对象是否具有可迭代属性,调用它的iter方法就可以了,如果返回TypeError异常,就说明它是不可迭代的.判读一个对象是不是字符串类型,看看它是不是basestring的子类就可以了.所以,这个断言并不难写:
def nonstring_iterable(obj):
try: iter(obj)
except TypeError: return False
else: return not isinstance(obj, basestring)
现在调用者可以使用nonstring_iterable,如果它希望将任何可迭代对象扩展的话.不过,最好不要把nonstring_iterable做为断言的默认值,因为它会比list_or_tuple慢3倍.
我们也可以写一个非递归的版本.这样可以使你避免达到Python递归的深度上限.这个上限一般是几千层.替代递归的方法就是建立一个后入先出的堆栈 (LIFO).在这里,我们可以使用iterator的列表来实现:
def flatten(sequence, to_expand=list_or_tuple):
iterators = [ iter(sequence) ]
while iterators:
# loop on the currently most-nested (last) iterator
for item in iterators[-1]:
if to_expand(item):
# subsequence found, go loop on iterator on subsequence
iterators.append(iter(item))
break
else:
yield item
else:
# most-nested iterator exhausted, go back, loop on its parent
iterators.pop( )
if语句的子句对每一个传递进的item进行操作,在那个子句中我们将item的迭代器放入iterators中,然后跳出for循环,回到外层的while循环, 它会对我们添加进的迭代器进行新的for循环.if语句的else语句是将任何不需要扩展的元素进行yield操作.
for语句的else语句在没有调用break且for执行完毕后运行,也就是说,当前列表已经遍历完毕.所以我们删除它的迭代器,回到while循环.while循环会开始新的循环,或者中止.实际上,我们记录了迭代的状态.
上面的非递归代码也适用于一般的简单递归问题.如果你认为递归比非递归要快,那么你要失望了,根据我自己的经验,非递归解法要比递归解法慢10%,这是在一系列例子运行后总结的结果.

相关说明:

iter(...)
    iter(collection) -> iterator
    iter(callable, sentinel) -> iterator

    Get an iterator from an object.  In the first form, the argument must
    supply its own iterator, or be a sequence.
    In the second form, the callable is called until it returns the sentinel.

2007-08-28

 

Python Cookbook 4.5 创建多维列表,避免元素共享引用

需求:

你需要简历多维列表,并且要避免元素引用共享.

讨论:

为了创建列表且避免引用共享,使用列表递推式,比如,建立一个5*10的数组,并初始化为0:

multilist = [[0 for col in range(5)] for row in range(10)]

当新接触Python时,对它的用列表乘法来初始化数组很兴奋,那真是一个聪明的语法:

>>> alist = [0] * 5

这样来获得包含5个元素的数组很方便.
很自然,也会想到2维数组可以这样写:

>>> multi = [[0] * 5] * 3
>>> print multi
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]

这样写看起来有效,但是很快新手们会遇到问题:

>>> multi[0][0] = 'oops!'
>>> print multi
[['oops!', 0, 0, 0, 0], ['oops!', 0, 0, 0, 0], ['oops!', 0, 0, 0, 0]]

这个问题曾经困扰了很多人, 尤其是新手.为了说明问题,我们最好首先把它分解成两部分:

>>> row = [0] * 5 # a list with five references to 0
>>> multi = [row] * 3 # a list with three references to the row object

这个分解说明了问题,它和前面的代码功能是一样的,如果你改变了multi[0][0]的值,也会改变multi[1][0].
上面代码后面的注释说明问题,用列表和数字相乘创建新的列表,并且拥有以前列表元素的内容.在这种情况下,创建了row,我们不用关心引用是否被复制,因为引用的是一个数字, 也可以推广到不可修改对象.也就是说,如果引用的对象是一个不可修改对象,那么该对象和该对象的引用就没有什么实质的区别.在第二行 ,我们从row又创建了一个列表,它的里面包含的都是row的应用.因此,multi包含了3个row对象的引用.所以,当row的一个元素修改后,其余的也会表现出来.
而列表递推式,就像本节开始给出的代码,避免了这个问题.在进行列表递推式的时候,处于内层的递推式不会进行引用共享.如果你想更深入的讨论这个问题,我们甚至可以说你连内层的递推式也不需要.换句话说,如果我问下面这个语句是不是等价:

multilist = [[0]*5 for row in range(10)]

那么答案是yes.事实上,使用这种方法,比使用嵌套递推式快两倍,那么为什么我们不推荐使用后面这种方法呢?因为提升速度的绝对值太小了,对你的程序性能影响并不大,而且重要的是,它牺牲了可读性!

标签:


 

Python 学习:单链表的实现

要定义一个单链表,首先定义链表元素:Element.
它包含3个字段:
list:标识自己属于哪一个list
datum:改元素的value
next:下一个节点的位置

class LinkedList(object):
   
    class Element(object):
       
        def __init__(self,list,datum,next):
            self._list = list
            self._datum = datum
            self._next = next

        def getDatum(self):
            return self._datum

        datum = property(
            fget = lambda self: self.getDatum())

        def getNext(self):
            return self._next

        next = property(
            fget = lambda self: self.getNext())

Element的构造函数很简单,只是用参数初始化新的节点.

接下来,我们定义LinkedList的初始化函数:

    def __init__(self):

        self._head = None
        self._tail = None

这个函数初始化列表的头和尾元素.
另,我们也定义一个链表清空函数,用于将链表初始化为空链表:

    def purge(self):
        self._head = None
        self._tail = None

我们为链表类定义3个属性:head,tail和isEmpty,分别表示链表的头,尾和判断链表是否为空.

    def getHead(self):
        return self._head
    head = property(
        fget = lambda self: self.getHead())
    def getTail(self):
        return self._tail
    tail = property(
        fget = lambda self: self.getTail())
    def IsEmpty(self):
        return self._head == None
    isEmpty = property(
        fget = lambda self: self.IsEmpty())

我们为链表类再定义两个属性,first和last, 用于获得链表的头和尾元素的值.当链表为空时,抛出ContainerEmpty异常:

    def getFirst(self):
        if self._head is None:
            raise ContainerEmpty
        return self._head._datum
    first = property(
        fget = lambda self: self.getFirst())

    def getLast(self):
        if self._tail is None:
            raise ContainerEmpty
        return self._tail._datum
    last = property(
        fget = lambda self: self.getLast ())

prepend函数用于将元素插入链表头,这样,插入后的元素变成新的链表头:

    def prepend(self,item):
        tmp = self.Element (self,item,self._head)
        if self._head is None:
            self._tail = tmp
        self._head = tmp

append函数用于给链表末尾添加元素.当链表为空时,添加的元素成为链表的头元素,然后,将元素添加到链表的尾部.

    def append(self,item):
        tmp = Element(self,item,None)
        if self._head is None:
            self._head = tmp
        else:
            self._tail._next = tmp
        self._tail = tmp

给链表定义copy函数,它是 一个浅拷贝函数,首先建立一个空链表,然后逐个添加元素:

    def __copy__(self):
        lst = LinkedList()
        ptr = self._head
        while ptr is not None:
            lst.append(ptr._datum)
            ptr = ptr._next
        return lst

定义链表的删除函数extract,用于删除链表中的特定元素.首先查找该元素的位置,如果不存在,提示KeyError异常.当元素是头元素时,将其下一个节点变为头元素;当其节点是尾元素时,将上一个节点变成尾元素.然后删除它.

    def extract(self,item):
        prePtr = ptr = self._head
        while ptr is not None and ptr._datum is not item:
            prePtr = ptr
            ptr = ptr._next
        if ptr is None:
            raise KeyError
        if ptr == self._head:
            self._head = ptr._next
        else:
            prePtr._next = ptr._next
        if ptr == self._tail:
            self._tail = prePtr

find函数用于查找指定的元素,当没有找到时,返回None:

    def find(self,item):
        ptr = self._head
        while ptr is not None and ptr._datum is not item:
            ptr = ptr._next
        return ptr

至此,一个简单的单链表就已经实现了,可以继续扩展它的功能.






标签:


2007-08-27

 

Python 学习:扩展Python的列表--Array

Python默认的List类型可以做为数组来使用,可是它有一些特点:

1.允许负索引,这是一般的Array类型所不允许的.
2.只能从0开始索引,有时候我们需要从一个特定的数字开始索引.

我们首先来构造一个简单的Array类型,它有两个简单的属性:
_data:用于保存数据
_baseIndex:用于保存起始索引号

写下下面的类:

class Array(object):
    def __init__(self,length = 0,baseIndex = 0):
        assert length >= 0
        self._data = [None for x in xrange(length)]
        self._baseIndex = baseIndex

该类可以接收两个参数,一个是初始的长度,另一个是起始的索引数.默认是0 .
接下来,我们定义Array的拷贝函数.这样,以后我们就可以这样使用它:

import copy
a = Array(5)
b = copy.copy(a)

为了实现这个功能,我们需要定义__copy__函数,这样,在使用copy的时候,就能调用我们的拷贝函数:

    def __copy__(self):
        result = Array(len(self._data),self._baseIndex)
        for i,item in enumerate(self._data):
            result._data[i] = item
        return result

为了像列表一样,使用[]来访问元素,我们需要定义两个函数:__getattr__和__setattr__,当索引值不存在时,也要报出异常,为了计算出合适的索引,再定义一个getOffset函数:

    def getOffset(self,index):
        offset = index - self._baseIndex
        if offset < 0 or offset >= len(self._data):
            raise IndexError
        return offset
    def __getattr__(self,index):
        return self._data[self.getOffset(index)]
    def __setattr__(self,index,value):
        self._data[self.getOffset(index)] = value

这样,我们就能像访问列表一样访问Array的元素.

同时,为了给用户提供获得Array信息的能力,我们提供了两个属性(property),data和baseIndex,定义属性,可以使用fget和fset访问器:

    def getData(self):
        return self._data
    data=property(
        fget = lambda self: self.getData())
    def getBaseIndex(self):
        return self._baseIndex
    def setBaseIndex(self,index):
        self._baseIndex = index
    baseIndex=property(
        fget = lambda self: self.getBaseIndex(),
        fset = lambda self,index: self.setBaseIndex(index))

我们为Array定义修改大小的接口.
为此,我们首先为Array提供获得长度的接口__len__,然后可以定义fget和fset访问器.

    def __len__(self):
        return len(self._data)
    def setLenght(self,value):
        if len(self._data)!=value:
            newData = [None for i in xrange(value)]
            m = min(len(self._data),value)
            for i in xrange(m):
                newData[i] = self._data[i]
            self._data = newData
    length=property(
        fget = lambda self: self.__len__(),
        fset = lambda self,value: self.setLenght(value))

至此,一个简单的扩展的Array类型就定义完毕了.


标签:


 

Python Cookbook 4.4 在序列中遍历它的元素和索引

需求:

你需要遍历一个序列,同时,你也要知道当前的元素的索引号.(你也许要改变它们的顺序),而Python使用的遍历方式不依赖于索引号.

讨论:

使用Python的enumerate可以解决这个问题,比如:
 for index, item in enumerate(sequence):
if item > 23:
sequence[index] = transform(item)
这比使用索引来访问序列更清晰,可读和快速:

for index in range(len(sequence)):
   if sequence[index] > 23:
      sequence[index] = transform(sequence[index])

遍历序列是很常见的需求,Python也加强了这方面的处理,你可以很方便的处理这样的需求.换句话说,最Python的使用方法是:

for item in sequence:
    process(item)

而不是和一些低级语言一样,需要按照元素的索引号来访问数据.

for index in range(len(sequence)):
    process(sequence[index])

直接遍历更清晰,简洁,快速和通用(你可以遍历任何序列,而使用索引只能遍历列表)
然而,有些情况下,你需要知道索引值,最常见的用法就是重新为序列元素绑定值.为了满足这个需求,Python提供了内建函数enumerate,这个函数可以接收任何可迭代对象为参数,并返回一个值对(index,item),可以使用下面的for循环来完成:

for index, item in enumerate(sequence):

这样能同时得到索引值和元素.
对于列表L,可以使用d=dict(enumerate),这样返回了一个和L等价的字典.其中d[i] is L[i]

相关说明:

class enumerate(object)
 |  enumerate(iterable) -> iterator for index, value of iterable
 |
 |  Return an enumerate object.  iterable must be an other object that supports
 |  iteration.  The enumerate object yields pairs containing a count (from
 |  zero) and a value yielded by the iterable argument.  enumerate is useful
 |  for obtaining an indexed list: (0, seq[0]), (1, seq[1]), (2, seq[2]), ...
 |
 |  Methods defined here:
 |
 |  __getattribute__(...)
 |      x.__getattribute__('name') <==> x.name
 |
 |  __iter__(...)
 |      x.__iter__() <==> iter(x)
 |
 |  next(...)
 |      x.next() -> the next value, or raise StopIteration


标签:


 

Python Cookbook 4.3 返回列表中的元素



---------- Forwarded message ----------
From: 侯志刚 <hzgnpu@gmail.com>
Date: 2007-8-27 上午11:25
Subject: Python Cookbook 4.3 返回列表中的元素
To: hzgnpu@gmail.com

需求:

你有列表L和索引i,如果L[i]元素有效,你需要得到它的值,否则,返回默认值v,如果L是一个字典,你可以使用L.get(i,v),但是List没有get方法.

讨论:

很显然,我们需要自己写一个函数,最简单的就是最有效的:
def list_get(L, i, v=None):
if -len(L) <= i < len(L): return L[i]
else: return v
本节的函数首先判断i是否是一个有效的索引值,它使用了Python的列表索引规则:合法的索引值处于[-len(L),len(L))区间内.如果大部分的索引值都是有效的,可以尝试使用下面的方法:
def list_get_egfp(L, i, v=None):
try: return L[i]
except IndexError: return v
然而,除非几乎全部的索引都是有效的,上面的方法可能最多会比本节给出的函数慢4倍.
我也尝试了许多不同的方法来实现需求,可是不知道什么原因,总是比list_get慢一些,一般的规则:当你写Python代码时,使用简单而清晰的方法而不是复杂或紧密的代码.这样你的代码往往运行的更快 ,更一直,也会让你的代码更稳定,更易维护.


标签:


2007-08-26

 

Python Cookbook 4.2 用递推式构造列表(List Comprehensions)

需求:

你需要构造一个新的列表,列表中的元素是从一个已知列表中的元素计算而得到的.

讨论:

比如你要创建一个列表,里面的元素是另一个列表中的元素加23后得到的.使用递推式构造列表是最理想的方法:

thenewlist = [x + 23 for x in theoldlist]

如果你希望用一个列表中大于5的元素构造一个新的列表,使用递推式也是很方便的:

thenewlist = [x for x in theoldlist if x > 5]

如果你希望将上面的两种情况组合起来,可以使用if表达式, 还可以使用计算式.比如给选中的元素加23,用一句话写成:

thenewlist = [x + 23 for x in theoldlist if x > 5]

优雅,清晰和实用,是Python的核心思想.递推式表现了3者的完美接合.的确,递推式是构造列表的最好方法,有时候,甚至也是修改列表的不错的选择.比如,你需要将列表中大于100的数改成100,可以这样写:

L[:] = [min(x,100) for x in L]

给列表的完整切片赋值会修改列表的内容,而不是仅仅重新绑定变量名,如果你写成L=...就是重新绑定变量名了.
如果你仅仅想对列表进行迭代,最好还是使用循环,而不是递推式,可以参考下一节.
如果有另外的内建方法可以完成同样的功能,最好也不要使用迭代式.比如复制列表 ,使用L1 = list(L),不要用:

L1 = [x for x in L]

另外,如果要对列表中的每一个元素进行函数操作,并将其返回值作为元素使用,请使用map函数,L1 = map(f,L),而不是[f(x) for x in L].不过在大多数情况下,使用递推式是比较方便的.
在Python2.4中,如果列表比较长,而且你一次只需要其中的一个元素, 使用生成表达式比较合适.生成表达式的写法和递推式是很类似的,它使用()来区分.在Python2.3中,比如我们要获得列表元素的和,而不是其中的每一个元素,可以这样写:

total = sum([x + 23 for x in theoldlist if x > 5])

在Python2.4中,可以不要[]:

total = sum(x + 23 for x in theoldlist if x > 5)

除了少写一对[],表达式也避免了维护一个列表,节省了内存空间.尤其当列表很长的时候,可以提高速度.

标签:


2007-08-25

 

AsciiDoc中文标题-出现问题原因

昨天下载了AsciiDoc,想照着例子做个练习,没想到一开始就遇到了麻烦:
当把title项改成汉字的时候,总会报出类似:
ERROR: xxx.txt: line 7: closing [blockdef-listing] delimiter expected
的错误.
上网没有找到解决办法,只有自己分析了.给asciidoc.py下了断点:
写了下面这个测试文件:

这是一个标题
==========

标题包含6个汉字,下面跟着12个=.
分别用UTF-8和UTF-8(No BOM)保存文件,对asciidoc.py进行跟踪:

当跟到

title_len = char_len(title)
ul_len = char_len(ul)

时,发现title_len的返回值是6或7(带BOM),和我预想的12不同.
所以在底下执行

if not (ul_len-3 < title_len < ul_len+3): return False

就返回了,造成了不能正常执行完毕.
看来,原作者没有考虑到在中文环境下,一个汉字占2个ascii字符的位置的情况.

所以我将那两行改成了

title_len = len(title)
ul_len = len(ul)

心想反正在UTF-8情况下,字符都是占用2个字节,只要直接比较len就可以了.

结果完全出乎我的意料:

title_len的值和ul_len的值变成了:18(21,带BOM的情况)和12!
看来UTF-8下,=还是一个字节,而汉字也不是2个字节!

上网上找到下面一段话:

UTF-8 使用一至四个字节为每个字符编码。128 个 ASCII 字符(Unicode 范围由 U+0000 至 U+007F)只需一个字节,带有变音符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文及马尔代夫语(Unicode 范围由 U+0080 至 U+07FF)需要二个字节,其他基本多文种平面(BMP)中的字符(CJK属于此类-Qieqie注)使用三个字节,其他 Unicode 辅助平面的字符使用四字节编码。

看来,问题不是这么简单了.

无奈,我注释掉

if not (ul_len-3 < title_len < ul_len+3): return False

这样就可以编译带中文标题的文件了,但不知道对别的什么情况有影响了.

标签:


2007-08-24

 

Python Cookbook 4.1 复制(拷贝)对象

需求:

你想复制一个对象.因为在Python中,无论你把对象做为参数传递,做为函数返回值,都是引用传递的.

讨论:

标准库中的copy模块提供了两个方法来实现拷贝.一个方法是copy,它返回和参数包含内容一样的对象.

import copy
new_list = copy.copy(existing_list)

有些时候,你希望对象中的属性也被复制,可以使用deepcopy方法:

import copy
new_list_of_dicts = copy.deepcopy(existing_list_of_dicts)

当你对一个对象赋值的时候(做为参数传递,或者做为返回值),Python和Java一样,总是传递原始对象的引用,而不是一个副本.其它一些语言当赋值的时候总是传递副本.Python从不猜测用户的需求 ,如果你想要一个副本,你必须显式的要求.
Python的行为很简单,迅速,而且一致.然而,如果你需要一个对象拷贝而并没有显式的写出来,会出现问题的,比如:
>>> a = [1, 2, 3]
>>> b = a
>>> b.append(5)
>>> print a, b
[1, 2, 3, 5] [1, 2, 3, 5]
在这里,变量a和b都指向同一个对象(一个列表),所以,一旦你修改了二者之一,另外一个也会受到影响.无论怎样,都会修改原来的对象.
注意:
要想成为一个Python高手,首先要注意的问题就是对象的变更操作和赋值,它们都是针对对象的引用操作的.一个语句比如a = []将a重新绑定给一个新对象,但不会影响以前的对象.然而,对象复制却不同,当对象复制后,对象变更操作就有了区别.
如果你想修改一个对象,而且想让原始的对象不受影响,那你就需要对象复制.正如本节说的一样,你可以使用copy模块中的两个方法来实现需求.一般的,可以使用copy.copy,它可以进行对象的浅复制(shallow copy),它复制了对象,但对于对象中的元素,依然使用引用.
浅复制,有时无法获得一个和原来对象完全一致的副本,如果你想修改对象中的元素,不仅仅是对象本身的话:

>>> list_of_lists = [ ['a'], [1, 2], ['z', 23] ]
>>> copy_lol = copy.copy(lists_of_lists)
>>> copy_lol[1].append('boo')
>>> print list_of_lists, copy_lol
[['a'], [1, 2, 'boo'], ['z', 23]] [['a'], [1, 2, 'boo'], ['z', 23]]

在这里,变量list_of_lists,copy_lol指向了两个不同的对象,所以我们可以修改它们任何一个, 而不影响另外一个.然而,如果我们修改了一个对象中的元素,那么另一个也会受影响,因为它们中的元素还是共享引用.
如果你希望复制一个容器对象,以及它里面的所有元素(包含元素的子元素),使用copy.deepcopy,这个方法会消耗一些时间和空间,不过,如果你需要完全复制,这是唯一的方法.
对于一般的浅拷贝,使用copy.copy就可以了,当然,你需要了解你要拷贝的对象.要复制列表L,使用list(L),要复制一个字典d,使用dict(d),要复制一个集合s,使用set(s),这样,我们总结出一个规律,如果你要复制一个对象o,它属于内建的类型t,那么你可以使用t(o)来 获得一个拷贝.dict也提供了一个复制版本,dict.copy,这个和dict(d)是一样,我推荐你使用后者,这个使得代码更一致,而且还少几个字符.
要复制一个别的类型,无论是你自己写的还是使用库中的,使用copy.copy,如果你自己写一个类,没有必要费神去写clone和copy函数,如果你想定义自己的类复制的方式,实现一个__copy__,或者__getstate__和__setstate__.如果你想定义自己类型的deepcopy,实现方法__deepcopy__.
注意你不用复制不可修改对象(string,数字,元组),因为你不用担心修改它们.如果你想尝试一下复制,依然会得到原来的.虽然无伤大雅,不过真的浪费尽力:

>>> s = 'cat'
>>> t = copy.copy(s)
>>> s is t
True

is操作符用于不仅判断两个对象是否完全一致,而且是同一个对象(is判断标识符,要比较内容,使用==),判断标识符是否相等对于不可修改对象没有什么意义.然而 ,判断标识符对于可修改对象有时候是很重要的,比如,你不确定a和b是否指向同一个对象,使用a is b会立刻得到结果.这样你可以自己判断是否需要使用对象拷贝.
注意:
你可以使用另一种拷贝方式,给定一个列表L,无论是完整切片L[:]或者列表解析[x for x in L],都会获得L的浅拷贝,试试L+[],L*1...但是上面两种方法都会使人迷惑,使用list(L)最清晰和快速,当然,由于历史原因,你可能会经常看到L[:]的写法.
对于dict,你可能见过下面的复制方法:
>>> for somekey in d:
...         d1[somekey] = d[somekey]

或者更简单一些的方法,d1={},d1.update(d),无论怎样,这些代码都是缺乏效率的,使用d1=dict(d)吧.

相关说明:

copy(x)
    Shallow copy operation on arbitrary Python objects.

    See the module's __doc__ string for more info.

deepcopy(x, memo=None, _nil=[])
    Deep copy operation on arbitrary Python objects.

    See the module's __doc__ string for more info.

标签:


2007-08-23

 

Python Cookbook 3.16 查看外汇兑换汇率

需求:

你想定期的查看两种货币间的兑换汇率(用linux的crontab或window的计划任务),从网上获得,并获得邮件通知.

讨论:

本节的代码和其它类似需求很相似,都是从网上获得数据,比如汇率,股票价格,甚至天气情况.让我们在这里看看如果获得美元和加元的汇率 ,从加拿大银行的网页获得.
import httplib
import smtplib
# configure script's parameters here
thresholdRate = 1.30
smtpServer = ' smtp.freebie.com'
fromaddr = 'foo@bar.com'
toaddrs = 'your@corp.com'
# end of configuration
url = '/en/financial_markets/csv/exchange_eng.csv'
conn = httplib.HTTPConnection(' www.bankofcanada.ca')
conn.request('GET', url)
response = conn.getresponse( )
data = response.read( )
start = data.index('United States Dollar')
line = data[start:data.index('\n', start)] # get the relevant line
rate = line.split(',')[-1] # last field on the line
if float(rate) < thresholdRate:
# send email
msg = 'Subject: Bank of Canada exchange rate alert %s' % rate
server = smtplib.SMTP(smtpServer)
server.sendmail(fromaddr, toaddrs, msg)
server.quit( )
conn.close( )
当需要知道外汇汇率时,最好能自动进行货币转换.本节的代码很直白,访问网页,获得CSV,它里面描述了汇率信息:
 Date (m/d/year),11/12/2004,11/15/2004, ... ,11/19/2004,11/22/2004
$Can/US closing rate,1.1927,1.2005,1.1956,1.1934,1.2058,1.1930,
United States Dollar,1.1925,1.2031,1.1934,1.1924,1.2074,1.1916,1.1844
...
这个脚本首先查找需要的币种(United States Dollar),然后找到最后, 获得今天的数据.如果你不太明天它是如何工作的,可以分开来看:
US = data.find('United States Dollar')  # find the index of the currency
endofUSline = data.index('\n', US) # find index for that line end
USline = data[US:endofUSline] # slice to make one string
rate = USline.split(',')[-1] # split on ',' and return last field
最后用email发送感兴趣的信息,你自己可以配置自己喜欢的信息.

标签:


 

Python Cookbook 3.15 计算信用卡的校验和

需求:

你想计算一下自己信用卡的校验和,并判读它是否满足Luhn标准.

讨论:

Luhn模10是信用卡号的标准,在Python中并没有对应的内建函数,不过我们可以自己实现:
 def cardLuhnChecksumIsValid(card_number):
""" checks to make sure that the card passes a luhn mod-10 checksum """
sum = 0
num_digits = len(card_number)
oddeven = num_digits & 1
for count in range(num_digits):
digit = int(card_number[count])
if not (( count & 1 ) ^ oddeven):
digit = digit * 2
if digit > 9:
digit = digit - 9
sum = sum + digit
return (sum % 10) == 0

本节的代码最初用于 一个电子商务系统.
它可以节省你的时间,用于判断用户提供的信用卡号是否是有效的,因为你不用花钱去验证卡号是否有效.这个代码也可以广泛使用因为许多政府都使用Luhn校验算法.
如果你想用一行代码实现上面的功能,你真的找错书了(也许你应该去读Perl Cookbook),当然,我也提供一个版本,试试:
 checksum = lambda a: (
10 - sum([int(y)*[7,3,1][x%3] for x, y in enumerate(str(a)[::-1])])%10)%10

标签:


 

Python Cookbook 3.14 将Python做为简易计算器

需求:

你想将Python做为一个简易的计算器,计算十进制小数(非float),并能整齐的将结果显示出来.

讨论:

为了实现计算,我们可以使用decimal模块,我们接收输入行,用计算符号将它们连接起来,接收空行来显示当前结果,q来结束程序.
import decimal, re, operator
parse_input = re.compile(r'''(?x) # allow comments and whitespace in the RE
(\d+\.?\d*) # number with optional decimal part
\s* # optional whitespace
([-+/*]) # operator
$''') # end-of-string
oper = { '+': operator.add, '-': operator.sub,
'*': operator.mul, '/': operator.truediv,
}
total = decimal.Decimal('0')
def print_total( ):
print '== == =\n', total
print """Welcome to Adding Machine:
Enter a number and operator,
an empty line to see the current subtotal,
or q to quit: """
while True:
try:
tape_line = raw_input( ).strip( )
except EOFError:
tape_line = 'q'
if not tape_line:
print_total( )
continue
elif tape_line == 'q':
print_total( )
break
try:
num_text, op = parse_input.match(tape_line).groups( )
except AttributeError:
print 'Invalid entry: %r' % tape_line
print 'Enter number and operator, empty line for total, q to quit'
continue
total = oper[op](total, decimal.Decimal(num_text))
Python的交互式解析器经常可以用于计算器,不过我们写的计算器也能达到相同的目的.比如,对于一个类似表达式2345634+2894756-2345823 来说,要保证你输入的数字都是正确的也很不容易.我们的计算器用简单的,整齐的方式显示数字,让你更清楚自己都输入了什么数字.另外,decimal模块计算数字的方式和我们真实生活中是一样的,而不是使用float计算.
当你在一个shell中运行这个脚本的时候(这个脚本不能在交互式解析器中使用),脚本提示你输入, 然后就等待.输入一个数字(可以是小数),一个运算符(+,-,*,/),然后回车.这个脚本开始累加数字.要看当前的计算结果,输入回车就可以了.要退出程序,输入q就可以了.这个脚本实现了最简单的计算器,并提供了简易的界面.
本节的代码相当简单,所以可以有很多加强的地方.一个比较有用的就是记录下输入.实现起来很简单,在循环前处理就好了:

tapefile = open(' tapefile.txt', 'a')

然后输入用户输入就可以了,注意使用try/except:

tapefile.write(tape_line+'\n')

你也可以在输出结果的时候,同时将结果写入文件中去:
 def print_total( ):
print '== == =\n', total
tapefile.write('== == =\n' + str(total) + '\n')
file对象的write方法并不自己添加回车,所以我们需要自己写入回车符.另外我们也可以使用别的方式来实现这个功能:

print >>tapefile, '== == =\n', total

有些人不喜欢print >> somefile的写法,不过在这里真的很方便.
另外的优化比如缓冲输入,或者添加clear方法,甚至添加一个GUI界面,当然,有兴趣的读者可以自己实现.
本节中使用的一个要点就是oper字典,它将字符(+,-,*,/)和内建的operator方法关联起来,如果没有使用这个,就要使用一堆if/elif语句了,比如:
if op == '+':
total = total + decimal.Decimal(num_text)
elif op == '-':
total = total - decimal.Decimal(num_text)
elif op == '*':
<line_annotation>... and so on ...</line_annotation>
Python的字典是很灵活和强大的,可以让代码更简洁,可维护,并去掉重复代码.

标签:


2007-08-22

 

Python Cookbook 3.13 格式化十进制小数转换为货币形式

需求:

你需要做一些简单的金融计算,并希望将结果表示为欧洲货币形式.

讨论:

在新的decimal模块中,有一个修改过的moneyfmt函数,我们可以使用它来实现功能.
 import decimal
""" calculate Italian invoice taxes given a subtotal. """
def italformat(value, places=2, curr='EUR', sep='.', dp=',', pos='', neg='-',
overall=10):
""" Convert Decimal ``value'' to a money-formatted string.
places: required number of places after the decimal point
curr: optional currency symbol before the sign (may be blank)
sep: optional grouping separator (comma, period, or blank) every 3
dp: decimal point indicator (comma or period); only specify as
blank when places is zero
pos: optional sign for positive numbers: "+", space or blank
neg: optional sign for negative numbers: "-", "(", space or blank
overall: optional overall length of result, adds padding on the
left, between the currency symbol and digits
"""
q = decimal.Decimal((0, (1,), -places)) # 2 places --> '0.01'
sign, digits, exp = value.quantize(q).as_tuple( )
result = [ ]
digits = map(str, digits)
append, next = result.append, digits.pop
for i in range(places):
if digits:
append(next( ))
else:
append('0')
append(dp)
i = 0
while digits:
append(next( ))
i += 1
if i == 3 and digits:
i = 0
append(sep)
while len(result) < overall:
append(' ')
append(curr)
if sign: append(neg)
else: append(pos)
result.reverse( )
return ''.join(result)
# get the subtotal for use in calculations
def getsubtotal(subtin=None):
if subtin == None:
subtin = input("Enter the subtotal: ")
subtotal = decimal.Decimal(str(subtin))
print "\n subtotal: ", italformat(subtotal)
return subtotal
# specific Italian tax law functions
def cnpcalc(subtotal):
contrib = subtotal * decimal.Decimal('.02')
print "+ contributo integrativo 2%: ", italformat(contrib, curr='')
return contrib
def vatcalc(subtotal, cnp):
vat = (subtotal+cnp) * decimal.Decimal ('.20')
print "+ IVA 20%: ", italformat(vat, curr='')
return vat
def ritacalc(subtotal):
rit = subtotal * decimal.Decimal('.20')
print "-Ritenuta d'acconto 20%: ", italformat(rit, curr='')
return rit
def dototal(subtotal, cnp, iva=0, rit=0):
totl = (subtotal+cnp+iva)-rit
print " TOTALE: ", italformat(totl)
return totl
# overall calculations report
def invoicer(subtotal=None, context=None):
if context is None:
decimal.getcontext( ).rounding="ROUND_HALF_UP" # Euro rounding rules
else:
decimal.setcontext(context) # set to context arg
subtot = getsubtotal(subtotal)
contrib = cnpcalc(subtot)
dototal(subtot, contrib, vatcalc(subtot, contrib), ritacalc(subtot))
if _ _name_ _=='_ _main_ _':
print "Welcome to the invoice calculator"
tests = [100, 1000.00, "10000", 555.55]
print "Euro context"
for test in tests:
invoicer(test)
print "default context"
for test in tests:
invoicer(test, context= decimal.DefaultContext)
意大利的税务计算是非常复杂的,所以本节的例子也就比较长.本节的代码只适用于意大利的货品计价用户.我自己用手计算发现太累了,所以写了一个小的Python脚本来计算它.我使用decimal模块来实现这个例子,也是要说明当涉及金钱的计算的时候,千万不要使用float.
为什么说decimal最适合货币计算并不是立刻就能明白的.因为虽然计算很简单,可是对于结果的显示就不那么简单了.本节的italformat函数基于Hettinger 's moneyfmt ,那个在Python标准库中的decimal中有介绍.我进行了一些修改,为了获得自己希望的结果.一个明显的修改就是添加了overall参数,这个参数使用overall来构造一个数字,并在数字和货币符号间添加了空格.这样对输出能控制好格式.
注意我在计算小节的部分,使用了subtotal = decimal.Decimal(str(subtin)). 这样就能使用float计算我们需要的结果.
当然,如果你需要使用US $来做为货币符号,或者别的东西,改起来也是很简单的.比如,你要输出$,可以这样设置参数:
def USformat(value, places=2, curr='$', sep=',', dp='.', pos='', neg='-',
overall=10):
...
如果你可能要同时处理多种货币格式,你可以修改这个函数,让它接收一个字典为参数,或者使用别的方式来传递参数.理论上,你可以使用locale模块来处理和地区相关的问题,比如货币格式,但在实际中我很少使用locale,所以功能的具体实现就交给读者吧.
不同的国家有不同的保留小数的方式,decimal使用ROUND_HALF_EVEN 做为默认方式,然而,欧洲货币使用ROUND_HALF_UP,为了使用不同的规则,修改decimal的context,也许修改后的结果不会有什么变化,但是还是要小心这些细微的差别.
你也可以更彻底的修改context,比如自己写context,对于修改context,无论是使用getcontext来获得,还是使用setcontext(mycontexts),都会保存它的值,一直到线程中止.在产品中,一定要注意使用合适的context,尤其是保留小数的方法.因为不同的国家可能是不同的.

相关说明:

decimal.getcontext()
    Returns this thread's context.

    If this thread does not yet have a context, returns
    a new context and sets this thread's context.
    New contexts are copies of DefaultContext.

decimal.setcontext(context)
    Set this thread's context to context.

标签:


 

Python Cookbook 3.12 十进制小数计算

需求:

你需要进行一些简单的小数计算,但是需要得到定点小数的结果,而不是Python默认的float类型.

讨论:

为了获得最通用,最准确的结果,可以使用decimal模块.
 >>> import decimal
>>> d1 = decimal.Decimal('0.3') # assign a decimal-number object
>>> d1/3 # try some division
Decimal("0.1")
>>> (d1/3)*3 # can we get back where we started?
Decimal("0.3")
对于新使用Python的用户来说(尤其是没有使用别的语言的float类型的用户),经常会对简单的计算结果表示惊奇,比如:
>>> f1 = .3                     # assign a float
>>> f1/3 # try some division
0.099999999999999992
>>> (f1/3)*3 # can we get back where we started?
0.29999999999999999
二进制浮点运算是Python中默认的运算方式.在Python的FAQ中有详细的说明.
很多用户,当然,不喜欢只能使用浮点数,他们需要自定义精度, 或者进行金融计算.而且想获得可预知的结果.
新的decimal模块对你计算过程的上下文进行了很好的控制,它允许你自定义精度,或者保留定义保留小数的方法.当然,如果你只是想进行简单的计算,并获得可以预知的结果,decimal默认的设置就能很好的工作了.
只需要注意几点:你可以传递整数,字符串,元组或者一个decimal对象来创建新的decimal对象,但是如果你想从一个float创建decimal,使用str(n),而不是简单的n.另外 ,decimal对象也可以和别的对象交互(简单的四则运算),如整数,长整数,别的decimal对象,但是不能和float交互.这个限制的强制的.decimal被引入Python,提供了精度控制和结果的可预知性,这都是float所不具有的.如果把一个decimal和一个float共同计算,那么就不能达到目的.decimal对象,可以被转换为别的对象,比如int,long,float等,正如我们期望的那样.
还要注意的是,decimal依然是浮点小数,而不是定点小数,如果你需要使用定点小数,可以参考Tim Peter的FixPoint.同样,Python现在也没有money类型,你可以参考下一节来自己实现一个money类型.最后将一点,不是很明确的,当计算的中间结果产生的小数位多于输入的数字时,你可以保留它的小数一用于后续的计算,当算到最后时在进行小数保留,或者你可以在每一步都保留小数,不同的书中推荐不同的做法,我个人倾向于前者,因为它更简单和方便.

标签:


2007-08-21

 

Python Cookbook 3.11 调度命令

需求:

你需要在特定的时间调度命令的执行.

讨论:

标准库中的sched库可以完成这个功能:

import time, os, sys, sched
schedule = sched.scheduler(time.time, time.sleep)
def perform_command(cmd, inc):
schedule.enter(inc, 0, perform_command, (cmd, inc)) # re-scheduler
os.system(cmd)
def main(cmd, inc=60):
schedule.enter (0, 0, perform_command, (cmd, inc)) # 0==right now
schedule.run( )
if _ _name_ _ == '_ _main_ _' :
numargs = len(sys.argv) - 1
if numargs < 1 or numargs > 2:
print "usage: " + sys.argv[0] + " command [seconds_delay]"
sys.exit(1)
cmd = sys.argv[1]
if numargs < 3:
main(cmd)
else:
inc = int(sys.argv[2])
main(cmd, inc)
本节和上节完成了类似的功能,不同的是,上节用自己实现代码的方式,而本节使用sched库来完成.
sched很简单,强大和灵活.使用sched调度的任务在给定时间一定会执行.要使用sched,你首先创建scheduler对象,比如schedule,并包含两个参数.第一个参数用于查找当前时间,它返回一个从标准时间到现在的整数,我们通常称为epoch.第二个参数用于表示延迟多少时间.你可以使用人工的方法来判断时间,比如,使用sched做为模拟程序,当然,手动使用sched衡量时间是高阶应用,在这里不做讨论.
一点你拥有了scheduler对象s,你可以通过s.enter来调度任务,让该任务在N秒后执行(可以传递0,表示立刻执行),也可以是使用s.enterabs,用于设定一个绝对时间.无论那种情况,你传递一个时间(绝对或相对),一个优先级(如果同时有多个任务执行,低优先级的先执行),要执行的函数,还有一个元组表示调用函数的参数.上面两个方法都返回一个事件标识符,一个随机的标识符,用于后来使用s.cancel来取消它.
当完成调度后, 你可以使用s.run来执行,它一直会执行到调度队列为空.在本节中,我们演示了如何调度一个周期执行的事件,函数perform_command做的第一件事情就是重新调度事件,然后再执行.这样做,调度队列永不会为空,而且perform_command函数会被定期执行,在这个函数中,自调度是一个非常重要的写法,不但在sched中,在其它的地方,如果你希望一个事件一旦触发,就会周期的进行,可以使用这个方法.
尽管本节只描述了一个简单的例子,sched依然具有一些有点, 相对于自己写代码来说.在上一节,在cmd执行的期间,需要delay一段时间,假如cmd需要等待的时间是个变量(这也是最常见的情况,比如等待网络,或者比较繁忙的服务等),那么这个任务就不是绝对的周期执行.在本节中,延迟是在cmd执行之前来决定的,所以周期能够保证.如果运行cmd的时间偶尔超过了inc的值,调度会暂时的落后,但很快会在后面赶上,只要cmd的平均运行时间小于inc:sched不会漏掉事件(如果你需要放弃一些事件,使用sched.cancel ).

相关说明:

sched.scheduler.enter(self, delay, priority, action, argument) unbound sched.scheduler method
    A variant that specifies the time as a relative time.

    This is actually the more commonly used interface.

sched.scheduler.enterabs(self, time, priority, action, argument) unbound sched.scheduler method
    Enter a new event in the queue at an absolute time.

    Returns an ID for the event which can be used to remove it,
    if necessary.

sched.scheduler.run(self) unbound sched.scheduler method
    Execute events until the queue is empty.

    When there is a positive delay until the first event, the delay function is called and the event is left in the queue; otherwise, the event is removed from the queue and executed (its action function is called, passing it the argument).  If  the delay function returns prematurely, it is simply restarted.

    It is legal for both the delay function and the action function to to modify the queue or to raise an exception; exceptions are not caught but the scheduler's state remains well-defined so run() may be called again.

    A questionably hack is added to allow other threads to run: just after an event is executed, a delay of 0 is executed, to avoid monopolizing the CPU when other threads are also   runnable.

sched.scheduler.cancel(self, event) unbound sched.scheduler method
    Remove an event from the queue.

    This must be presented the ID as returned by enter(). If the event is not in the queue, this raises RuntimeError.

标签:


2007-08-20

 

Python Cookbook 3.10 重复执行命令

需求:

你需要重复的执行一个命令,并且可以指定优先级.

讨论:

time.sleep函数提供了简单的解决方案.

 import time, os, sys
def main(cmd, inc=60):
while True:
os.system(cmd)
time.sleep(inc)
if _ _name_ _ == '_ _main_ _' :
numargs = len(sys.argv) - 1
if numargs < 1 or numargs > 2:
print "usage: " + sys.argv[0] + " command [seconds_delay]"
sys.exit(1)
cmd = sys.argv[1]
if numargs < 3:
main(cmd)
else:
inc = int(sys.argv [2])
main(cmd, inc)
你可以使用本节的代码来偶然的执行某些命令(如传送),或者重复执行某些指令,如通知浏览器重复加载一个URL,使得总是能获得最新的网页内容.本节代码的结构是使用一个main函数体和使用if _ _name_ _=='_ _main_ _'语句来组织的.这是当脚本用于单独执行时的习惯性编写方式.代码体检查输入参数,然后调用main方法,这是组织代码的最好办法,这样也方便别的模块使用该模块的代码.
main函数接收一个cmd参数,它表示你要执行的系统命令的字符串,还有一个可选参数,及执行间隔时间,默认是60秒.main方法是一个循环过程,使用os.system来执行命令,并使用time.sleep来等待.
脚本的主体用于判断参数,参数都包含在sys.argv里面,第一个参数,sys.argv[0],是脚本的名称,一般可以用于标识自己.接下来,脚本判断了其余的一个或两个参数,第一个(必须)是要执行的命令,另一个(可选)是重复执行的时间间隔.(在传递命令的时候,最好使用引号将字符串包含起来,以免shell出现解析错误),如果没有第二个参数,main函数将使用默认的值,60秒.
需要注意的时候,如果有第二个参数,需要将它从string转换为int(sys.argv里面总是string),只要使用内建的int方法就可以了.
如果第二个参数不是合法的字符(包含非数字序列),调用int方法会抛出异常,程序中止.因为Python的一个设计原则就是:"错误不能隐藏,除非声明需要隐藏".

相关说明:

time.sleep(...)
    sleep(seconds)

    Delay execution for a given number of seconds.  The argument may be
    a floating point number for subsecond precision.

标签:


2007-08-15

 

Python Cookbook 3.9 转换时区

需求:

假如你现在在西班牙,你需要知道中国发生的事件的西班牙时间.

讨论:

对于datetime对时区的支持,可以从第三方包dateutil获得,这里有一个方法来设置本地时区,并打印出时间来看它是否有效:

from dateutil import tz 
import datetime
posixstr = "CET-1CEST-2,M3.5.0/02:00, M10.5.0/03:00"
spaintz = tz.tzstr(posixstr)
print datetime.datetime.now(spaintz).ctime( )

在不同时区中转换也是可以的,而且在生活中可能经常遇到.比如,让我们找出下一界奥林匹克运动会开幕的时间,以西班牙时间为准:

chinatz = tz.tzoffset("China", 60*60*8)
olympicgames = datetime.datetime(2008, 8, 8, 20, 0, tzinfo=chinatz)
print olympicgames.astimezone(spaintz)

前面使用的那个神秘的posixstr是一种使用OPSIX方式来表示时区的字符串,现在在西班牙使用.这个字符串提供了标准和夏令时时区(CST,CEST)的名称,他们的偏移量(UTC+1,UTC+2),以及夏令时开始和结束的时间(3月最后一个周日的早上2点,8月的最后一个周日早上3点).我们可以判断DST时区来保证它们正确:

assert spaintz.tzname(datetime.datetime(2004, 03, 28, 1, 59)) == "CET"
assert spaintz.tzname(datetime.datetime(2004, 03, 28, 2, 00)) == "CEST"
assert spaintz.tzname(datetime.datetime(2004, 10, 31, 1, 59)) == "CEST"
assert spaintz.tzname(datetime.datetime(2004, 10, 31, 2, 00)) == "CET"

这上面的assert都应该能通过.
注意到尽管回到标准时间是早上3点,可是上面还是按照2点来写,是因为2点和3点的时间差, 是很模糊的.那个2点重复了2次:一次是标准时间,一次夏令时.就当前来说,使用Python的datetime不能区分二者,所以建议你保存UTC时间实例,来进行时区转换.
为了在中国和西班牙间进行时区转换,我们使用tzoffset来表示中国时间比UTC早8个小时,注意datetime对象是怎样被建立的,即使是本地时间,也要作这样的转换.如果你没有使用时区信息来创建datetime对象,你将会获得异常:ValueError: astimezone( ) cannot be applied to a naive datetime. datetime对象在创立的时候,总是默认没有时区信息,除非你显式的给出时区信息.为此 ,dateutil提供了tzlocal类型,它用来表示本地时区的时间.
除了目前我们所看到的类型外,dateutil也提供tzutc,它用于创建UTC时间对象.tzfile,它允许使用标准时间二进制文件.tzical,它用于创建iCalender时区对象.当然,还有很多别的类型.

标签:


2007-08-14

 

Python Cookbook 3.8 判断夏令时是否有效

需求:

你需要知道当前的夏令时在你的时区是否有效.

讨论:

很容易想到使用time.daylight方法,但实际上不是的:

import time
def is_dst( ):
    return bool(time.localtime( ).tm_isdst)

在我处于的时区中,time.daylight总是1,因为time.daylight表示一年中有些夏令时存在.并不是说判断今天是否是夏令时.
然而调用time.localtime ,仅当当天是夏令时后,才返回1,否则返回0.本节将这个调用进行的封装,并返回内建的bool类型,你也可以使用time.localtime()[-1]来访问这个值,但是使用tm_isdst更可读一些.

相关说明:

夏令时,又称"日光节约时制"(Daylight Saving Time),是一种为节约能源而人为规定地方时间的制度,在这一制度实行期间所采用的统一时间称为"夏令时"。一般在天亮早的夏季人为将时间提前一小时, 可以使人早起早睡,减少照明量,以充分利用光照资源,从而节约照明用电。各个采纳夏时制的国家具体规定不同。目前全世界有近110个国家每年要实行夏令 时。

据说最早建议使用夏时制的是本杰明・富兰克林,他在任美国驻法国大使期间,由于习惯于当时美国农村贵族的早睡早起生活,早上散步时看到法国人10 点才起床,夜生活过到深夜。于是他在1784年的一期《巴黎杂志》上发表了一篇文章,说法国人的生活习惯浪费了大好的阳光,建议法国人早睡早起,说每年可 以节约6千4百万磅蜡烛。但他当时并没有建议实行夏时制,因为当时根本还没有统一的时区划分。不过夏时制在英语里就是"节约阳光时间"的意思。

直到1907年,一位英国建筑师威廉・维莱特(William Willett)才正式向英国议会提出夏时制的设想,主要是为了节省能源和提供更多的时间用来训练士兵,但议会经过辩论没有采纳。

1916年,德国首先实行夏时制,英国因为怕德国会从中得到更大的效益,因此紧跟着也采取了夏时制,夏时制节约了约15%的煤气和电力,但为了弥 补损失,电力和煤气公司也将价格提高了15%。法国不久也效仿实行。1917年,俄罗斯第一次实行了夏令时,但直到1981年才成为一项经常性的制度。 1918年,参加了第一次世界大战的美国也实行了夏时制,但战后立即取消了。

1942年,第二次世界大战期间,美国又实行了夏时制,1945年战争结束后取消。1966年,美国重新实行夏时制。欧洲大部分国家从1976年,即第四次中东战争导致首次石油危机3年后(1973年)开始实行夏时制。

根据联合国欧洲经济委员会的建议,从1996年起夏令时的有效期推迟到10月份的最后一个星期日。

标签:


 

Python Cookbook 3.7 解析时间数据

需求:

你的程序需要接收时间数据,而这些数据不是按照标准的datetime数据格式"yyyy,mm,dd"来组织的.

讨论:

第三方库dateutil的dateutil.parse提供了简便的解决方案:

import datetime
import dateutil.parser
def tryparse(date):
    # dateutil.parser needs a string argument: let's make one from our
    # `date' argument, according to a few reasonable conventions...:
    kwargs = {  }                                    # assume no named-args
    if isinstance(date, (tuple, list)):
        date = ' '.join([str(x) for x in date])    # join up sequences
    elif isinstance(date, int):
        date = str(date)                           # stringify integers
    elif isinstance(date, dict):
        kwargs = date                              # accept named-args dicts
        date = kwargs.pop('date')                  # with a 'date' str
    try:
        try:
            parsedate = dateutil.parser.parse(date, **kwargs)
            print 'Sharp %r -> %s' % (date, parsedate)
        except ValueError:
            parsedate = dateutil.parser.parse(date, fuzzy=True, **kwargs)
            print 'Fuzzy %r -> %s' % (date, parsedate)
    except Exception, err:
        print 'Try as I may, I cannot parse %r (%s)' % (date, err)
if _ _name_ _ == "_ _main_ _":
    tests = (
            "January 3, 2003",                     # a string
            (5, "Oct", 55),                        # a tuple
            "Thursday, November 18",               # longer string without year
            "7/24/04",                             # a string with slashes
            "24-7-2004",                           # European-format string
            {'date':"5-10-1955", "dayfirst":True}, # a dict including the kwarg
            "5-10-1955",                           # dayfirst, no kwarg
            19950317,                              # not a string
            "11AM on the 11th day of 11th month, in the year of our Lord 1945",
            )
    for test in tests:                             # testing date formats
        tryparse(test)                             # try to parse

dateutil.parse可以工作于多种时间格式.本节演示了一部分的用法.那个解析器可以解析英语国家的月份表示和2或4位的年表示法.当你给它传递的参数没有命名的时候 ,它默认使用"mm-dd-yy",如果发现解析后没有意义,比如例子中给出的"24-7-2004",解析器会尝试"dd-nn-yy",最后,它会尝试"yy-mm-dd".如果命名参数给出了,它会按照命名参数进行解析.
本节的测试代码包含了一些可能会遇到的边界条件,比如以元组的形式传递参数,或者以整数的形式,甚至一个短语.为了测试关键字,tryparse函数还允许字典作为参数,可以将键为date的值转换为时间对象,其余的作为函数的命名参数.
dateutil.parse可以做模糊解析.给出一些提示来让它解析,比如,小时(本节中使用了AM).对于商业级代码,你最好避免模糊解析,做一些预处理,并对解析结果进行检查.

标签:


2007-08-13

 

Python Cookbook 3.6 自动查询假日信息

需求:

每个国家,地区,民族甚至是同一个公司的假期设置都不尽相同.你需要自动查出在两个日期间假期的个数.

讨论:

在两个日期之间,可能有日期不定的节日,比如美国的复活节和劳动节,还有根据复活节计算的节日,比如礼节日(Boxing Day),或者有固定日期的节日, 比如圣诞节.甚至你们公司自己的节日(比如老板的生日).你都可以使用datetime和第三方包util来处理这些情况.
一个比较方便的方法是将不同的情况分别处理,封装在不同的函数里:

import datetime
from dateutil import rrule, easter
try: set
except NameError: from sets import Set as set
def all_easter(start, end):
    # return the list of Easter dates within start..end
    easters = [easter.easter(y) 
               for y in xrange(start.year, end.year+1)]
    return [d for d in easters if start<=d<=end]
def all_boxing(start, end):
    # return the list of Boxing Day dates within start..end
    one_day = datetime.timedelta(days=1)
    boxings = [easter.easter(y)+one_day 
               for y in xrange(start.year, end.year+1)]
    return [d for d in boxings if start<=d<=end]
def all_christmas(start, end):
    # return the list of Christmas Day dates within start..end
    christmases = [datetime.date(y, 12, 25) 
                   for y in xrange(start.year, end.year+1)]
    return [d for d in christmases if start<=d<=end]
def all_labor(start, end):
    # return the list of Labor Day dates within start..end
    labors = rrule.rrule(rrule.YEARLY, bymonth=9, byweekday=rrule.MO(1),
                         dtstart=start, until=end)
    return [d.date( ) for d in labors]   # no need to test for in-between here
def read_holidays(start, end, holidays_file='holidays.txt'):
    # return the list of dates from holidays_file within start..end
    try:
        holidays_file = open(holidays_file)
    except IOError, err:
        print 'cannot read holidays (%r):' % (holidays_file,), err
        return [  ]
    holidays = [  ]
    for line in holidays_file:
        # skip blank lines and comments
        if line.isspace( ) or line.startswith('#'):
            continue
        # try to parse the format: YYYY, M, D
        try:
            y, m, d = [int(x.strip( )) for x in line.split(',')]
            date = datetime.date(y, m, d)
        except ValueError:
            # diagnose invalid line and just go on
            print "Invalid line %r in holidays file %r" % (
                line, holidays_file)
            continue
        if start<=date<=end:
            holidays.append(date)
    holidays_file.close( )
    return holidays
holidays_by_country = {
    # map each country code to a sequence of functions
    'US': (all_easter, all_christmas, all_labor),
    'IT': (all_easter, all_boxing, all_christmas),
}
def holidays(cc, start, end, holidays_file=' holidays.txt'):
    # read applicable holidays from the file
    all_holidays = read_holidays(start, end, holidays_file)
    # add all holidays computed by applicable functions
    functions = holidays_by_country.get(cc, ( ))
    for function in functions:
        all_holidays += function(start, end)
    # eliminate duplicates
    all_holidays = list(set(all_holidays))
    # uncomment the following 2 lines to return a sorted list:
    # all_holidays.sort( )
    # return all_holidays
    return len(all_holidays)    # comment this out if returning list
if _ _name_ _ == '_ _main_ _':
    test_file = open('test_holidays.txt', 'w')
    test_file.write('2004, 9, 6\n')
    test_file.close( )
    testdates = [ (datetime.date(2004, 8,  1), datetime.date(2004, 11, 14)),
                  (datetime.date(2003, 2, 28), datetime.date(2003,  5, 30)),
                  (datetime.date(2004, 2, 28), datetime.date(2004,  5, 30)),
                ]
    def test(cc, testdates, expected):
        for (s, e), expect in zip(testdates, expected):
            print 'total holidays in %s from %s to %s is %d (exp %d)' % (
                    cc, s, e, holidays(cc, s, e, test_file.name), expect)
            print
    test('US', testdates, (1,1,1) )
    test('IT', testdates, (1,2,2) )
    import os
    os.remove(test_file.name)

在我工作的公司里面,有几个工会,公司的节假日由几个工会协商制定.另外,我需要将下雪天和发布产品的日期作为"官方"节假日.为了处理各种情况,比较方便的方法是将不同类型的节假日放在不同的函数中处理,比如上例中我们对all_Easter和all_labor的处理.对于各种不同的情况都有列出 ,所以你很容易写出自己的来.
尽管半开区间(区间左边界被包含而右边界不包含)是Python的标准(因为它有更好的灵活性以及能避免用户的一些使用问题),本节使用全封闭区间来处理.不幸的是,无论时间区间多明确的给出,dateutil还是那样工作.所以我们的选择很明显.
每个函数确保满足你的要求的日期才被返回:一个datetime.date实例列表被传递给函数,而且它们都是处于给定日期区间内的.比如,在all_labor里面,我们强制将使用dateutil的rrule的datetime.datetime类型由转换为datetime.date类型后返回 .
有些公司可能仅仅一次设置某些天为假期(比如下雪天),我们可以使用一个文本文件来保存这些数据.在我们的例子中,read_holidays函数用于处理和分析这个文件,你可以将这一部分代码用"fuzzy"日期分析器来处理.
如果你在程序运行过程中可能会多次查询,最好将读文件部分优化为一次处理,然后将内容保存在列表中以便后面使用.然而,"不成熟的优化是程序罪恶的根源"记得Knuth的话吗?避免使用哪怕最"显然"的优化,也要保证程序的清晰和灵活,假设我们的程序是运行在交互式环境中,每次读取文件避免了判断文件是否被修改的麻烦.
因为不同的国家的节假日不同,所以本节的代码也提供了一个holidays_by_country的字典.你可以根据不同的需要来更新这个字典.需要注意的是这个字典允许不同的生成器函数调用,依据你给定的国家代码.如果你的国家有很多州,你很容易创建一个基于州信息的字典,传递州编号来替代国家编号.holidays函数会调用合适的函数,并组合结果,去除重复数据,并返回结果的个数.当然,你也可以返回结果的列表,仅仅需要去掉上面代码中的两行注释就可以了.

标签:


2007-08-10

 

Python Cookbook 3.5 计算两日期间的工作日数

需求:

你需要计算两个日期间的工作日数,而不是天数.

讨论:

因为对于周末的定义,每个国家,地区甚至公司都不同.所以没有内建的函数来完成这个需求,不过使用datetutil和datetime,可以很方便的实现这个需求.

from dateutil import rrule 
import datetime
def workdays(start, end, holidays=0, days_off=None):
    if days_off is None:
        days_off = 5, 6         # default to: saturdays and sundays
    workdays = [x for x in range(7) if x not in days_off]
    days = rrule.rrule(rrule.DAILY, dtstart=start, until=end,
                       byweekday=workdays)
    return days.count( ) - holidays
if _ _name_ _ == '_ _main_ _':
# test when run as main script
    testdates = [ (datetime.date(2004, 9, 1), datetime.date(2004, 11, 14), 2),
                  (datetime.date(2003, 2, 28), datetime.date(2003, 3, 3), 1), ]
    def test(testdates, days_off=None):
        for s, e, h in testdates:
            print 'total workdays from %s to %s is %s with %s holidays' % (
                        s, e, workdays(s, e, h, days_off), h)
    test(testdates)
    test(testdates, days_off=[6])

这个程序是我用Python写的第一个程序:我需要知道员工培训的具体日子,当给定两个日期的情况下.这个问题在Python2.2前是比较难办的,现在有了datetime和dateutil,变得非常简单了.
函数workdays首先给变量day_off设置了默认值(除非显式的给它赋值).它表示在一周中我们休息的日子.在我的公司中,每个人休息的日子是不同的,但一般情况都不会多于工作的日子,所以对我们来说,修改休息日比修改工作日更好一些.我把它作为函数的一个参数,这样在我需要另外的休息日的时候,可以简单的作为参数传递进来.接下来,函数创建了工作日的列表,里面不包含休息日.剩下要做的就是计算了.
在本节中做最多工作的是一个实例,days, 来自dateutil.rrule类.类rrule可以用来产生很多种不同的尺子.在本例中,我传递了常用的rrule.DAILY,还需要给出起始时间和终止时间,而且都是datetime.date类型.然后,我们只要调用days.count来计算其中的天数.
你可以自己定义周末:只要给day_off里面添加值就好了.在本节中,我们使用惯例的周六和周日作为周末,加入,你的公司是4工作日制,从周二到周五,你可以使用day_off=(5,6,0),只要以可以遍历的形式传递就好了,比如列表和元组.
一个简单的功能加强是自动判断给出的起始日和终止日是否是周末,如果是的话,自动修改day_off的值.更多的功能添加可以是加入病假日,或者查询休假日,而不是需要自己给出每一个休假日.

标签:


2007-08-08

 

Python Cookbook 3.4 计算播放歌曲的时长

需求:

你需要知道播放列表中歌曲的总时长。

讨论:

使用datetime模块的内建函数sum来作统计工作:

import datetime
def totaltimer(times):
    td = datetime.timedelta(0)    # initial value of sum (must be a timedelta)
    duration = sum([
        datetime.timedelta(minutes=m, seconds=s) for m, s in times],
        td)
    return duration
if _ _name_ _== '_ _main_ _':        # test when module run as main script
    times1 = [(2, 36),        # list containing tuples (minutes, seconds)
              (3, 35),
              (3, 45),]
    times2 = [(3, 0),
              (5, 13),
              (4, 12),
              (1, 10),]
    assert totaltimer(times1) == datetime.timedelta(0, 596)
    assert totaltimer(times2) == datetime.timedelta(0, 815)
    print ("Tests passed.\n"
           "First test total: %s\n"
           "Second test total: %s" % (
            totaltimer(times1), totaltimer(times2)))

当我工作的时候,会播放一个很大的播放列表。我想知道自己选择的歌曲的总时长,而不是重新建立一个播放列表,所以写下了这个脚本。
一个datetime.timedelta对象一般返回两个给定的datetime对象的差值 。当然,你也可以创建自己的timedelta对象来衡量任意之间的差值。在在这里,我们需要计算时长,所以timedelta正是我们需要的。
datetime.timedelta函数可以有很多可选参数:days,seconds,microseconds,milliseconds,minutes,hours,weeks.所以,创建对象的时候,你需要使用命名参数,否则自己会被搞混的。如果你仅仅使用了datetime.timedelta(m,n),而没有使用命名参数,该类会认为m表示days,n表示seconds,那真的会产生很奇怪的结果。
使用内建的sum函数来计算一个列表中的值比如timedelta,你需要给sum传递第二个参数来表示初始值,否则它会初始为0,整数0,而你会得到一个异常,因为将整数和timedelta相加。列表中的对象需要支持数字加法。(strings是没有加法的,所以,不要用sum来加string或者别的列表!)。在Python2.4中,对于sum的第一个参数,我们可以用生成表达式来替换[],这样我们可以方便的处理具有大数据量的列表。
为了测试目的,我自己手动创建了一个列表,里面的元组表示分钟和秒。这个脚本可以修改为加强版,比如支持更多的格式(如mm:ss)或者直接从文件中读取数据。

相关说明:

sum(...)
    sum(sequence, start=0) -> value
   
    Returns the sum of a sequence of numbers (NOT strings) plus the value
    of parameter 'start'.  When the sequence is empty, returns start.


标签:


 

Python Cookbook 3.3 在一个时间范围内计算时间差

需求:

给定两个时间,你需要计算它们间相差几周。

讨论:

同样的,标准库中的datetime和第三方包dateutil都是十分方便的(尤其是dateutil的rrule.count方法).当引入和合适的包后,工作变得异常简单:

from dateutil import rrule
import datetime
def weeks_between(start_date, end_date):
    weeks = rrule.rrule(rrule.WEEKLY, dtstart=start_date, until=end_date)
    return weeks.count( )

函数weeks_between使用起始和终止时间作为参数,初始化一个尺子来测量它们之间的差值,然后返回。写代码比描述起来还要方便。这个方法仅仅返回一个整数,比如,8天被认为是2周。很容易来测试一下这个代码:

if _ _name_ _=='_ _main_ _':
    starts = [datetime.date(2005, 01, 04), datetime.date(2005, 01, 03)]
    end = datetime.date(2005, 01, 10)
    for s in starts:
        days = rrule.rrule(rrule.DAILY, dtstart=s, until=end).count( )
        print "%d days shows as %d weeks "% (days, weeks_between(s, end))

测试输出如下:

7 days shows as 1 weeks
8 days shows as 2 weeks

没有必要为尺子取一个名字,如果你不想改变函数体,比如改成一条语句:

return rrule.rrule(rrule.WEEKLY , dtstart=start_date, until=end_date).count( )

也是没有问题的。我个人喜欢给尺子命名因为觉得它有些奇怪,不过它是如此的强大以至于我不能离开它了。

标签:


2007-08-07

 

Python Cookbook 3.2 查找最近的星期五

需求:

你需要知道最近星期五的日期(或者是今天,如果今天是星期五),然后用特定的格式输出.

讨论:

你可以使用datetime模块来实现这个需求:

import datetime, calendar
lastFriday = datetime.date.today( )
oneday = datetime.timedelta(days=1)
while lastFriday.weekday ( ) != calendar.FRIDAY:
    lastFriday -= oneday
print lastFriday.strftime ('%A, %d-%b-%Y')
# emits, e.g.: Friday, 10-Dec-2004

上面的代码方便的实现了查找最近的星期五,并用特定的格式输出,无论这个周五是不是处于同一月,甚至同一年.在这个例子里面,我们查找最近的星期五.星期五的整数表示是4 ,但是为了避免使用这个"魔幻数字",我们使用Python标准库的calendar模块中的calendar.FRIDAY来表示(当然,我敢确定,它的值也是4).我们设置了一个变量叫做lastFriday,它保存今天的日期,然后让它递减,直到它的weekday值为4.
一旦我们找到了需要的日期,格式化输出就变得异常简单,使用datetime.date的strftime方法就可以了.
另一个更简洁的做法是使用内建的常量datetime.date.resolution来替代datetime.timedelta来表示一天 .

import datetime, calendar
lastFriday = datetime.date.today( )
while lastFriday.weekday( ) != calendar.FRIDAY:
    lastFriday -= datetime.date.resolution
print lastFriday.strftime('%d-%b-%Y')

类属性datetime.date.resolution表示了一天的时间.然而,resolution可能会混淆你,因为它随着不同的datetime模块其值也是不同的.对于date模块,timedelta(day=1),但是对于time和datetime类,它们的timedelta(microseconds=1),你可以进行混合匹配,但容易搞混.使用本节开始的方法,会更通用一些,也不容易搞混.而且,怎么说它也是最Python的写法(那可是官方版本).
一个重要的需要加强的地方是我们不需要使用循环,一次次的减少时间.我们可以一次性得到目标,使用算数计算就OK了:

import datetime, calendar
today = datetime.date.today ( )
targetDay = calendar.FRIDAY
thisDay = today.weekday( )
deltaToTarget = (thisDay - targetDay) % 7
lastFriday = today - datetime.timedelta(days=deltaToTarget)
print lastFriday.strftime('%d-%b-%Y')

要始终追求最清晰的方式,而不是效率.记住Hoare的名言:"在编程中不成熟的优化是罪恶的根源".让我们看看为什么优化是不成熟的.
用最普通的装备在一个4年的老机器上,跑着linux和Python2.4 ,用本节开始的方法,用时18.4毫秒,用最后的方法,用时10.1毫秒.
在你的一生中,并不会总去计算上周5是什么时候,对吧.接下来...(一些证明词).

相关说明:

weekday(...)
    Return the day of the week represented by the date.
    Monday == 0 ... Sunday == 6

strftime(...)
    format -> strftime() style string.

标签:


 

Python Cookbook 3.1 计算昨天和明天

需求:

你需要得到今天的日期,然后计算昨天的或明天的日期.

讨论:

任何需要计算时间"变化"或"差值"的时候,请考虑使用timedelta:

import datetime
today = datetime.date.today( )
yesterday = today - datetime.timedelta(days=1)
tomorrow = today + datetime.timedelta(days=1)
print yesterday, today, tomorrow
#emits: 2004-11-17 2004-11-18 2004-11-19

本节提出的问题在Python邮件列表里面经常被问道.当我们第一次面对这个问题的时候,直觉的想法是写下代码如:yesterday = today-1,它会报出异常: TypeError: unsupported operand type(s) for -: ' datetime.date' and 'int'.
有些人认为这是Python的一个bug,它们期望Python能猜测用户的目的.然而,Python简单和强大的一个信条就是:遇到不清楚的时候,拒绝猜测.假如要尝试猜测的话,必须要认真考虑你指的一天,一秒,还是一年.
Python不会猜测你的需求,而是让你表达的更明确,如果你希望给一个时间减去一天,就显示的写出这样的代码.如果,你希望给一个时间加上一秒,你可以使用timedelta和datetime.datetime对象 ,然后可以使用相同的语法来实现.像这样,对于每一个你希望完成的任务,都会只有一个明确的方式来完成.这样做也会增加很多灵活性,而且不让问题变得更复杂.看看下面的脚本:

>>> anniversary = today + datetime.timedelta(days=365)          # add 1 year
>>> print anniversary
2005-11-18
>>> t = datetime.datetime.today( )                               # get right now
>>> t
datetime.datetime(2004, 11, 19, 10, 12, 43, 801000)
>>> t2 = t + datetime.timedelta(seconds=1)                      # add 1 second
>>> t2
datetime.datetime(2004, 11, 19, 10, 12, 44, 801000)
>>> t3 = t + datetime.timedelta(seconds=3600)                   # add 1 hour
>>> t3
datetime.datetime(2004, 11, 19, 11, 12, 43, 801000)

请记住,如果你爱好对时间进行计算,第三方包,比如dateutil和经典的mx.Datetime都很适合,如:

from dateutil import relativedelta
nextweek = today + relativedelta.relativedelta(weeks=1)
print nextweek
#emits: 2004-11-25

然而,"总是用最简单的方式来工作",为了简单起见, 用datetime.timedelta就足够了.

相关说明:

class timedelta(__builtin__.object)
 Difference between two datetime values.

支持的操作有:
abs,四则运算及常见比较.

 Data descriptors defined here:

 days
     Number of days.

 microseconds
     Number of microseconds (>= 0 and less than 1 second).

 seconds
     Number of seconds (>= 0 and less than 1 day).

 ----------------------------------------------------------------------
 Data and other attributes defined here:

 max = datetime.timedelta(999999999, 86399, 999999)

 min = datetime.timedelta(-999999999)

 resolution = datetime.timedelta(0, 0, 1)

标签:


This page is powered by Blogger. Isn't yours?