flask-cache源码简要分析

flask-cache源码文件包含下面三个文件:
  1. __init__.py:flask-cache主要功能,提供了对用户使用的各种函数;
  2. backends.py:对werkzeug.contrib.cache进行一些封装以及扩展,为__init__.py提供多种缓存类型;
  3. jinja2ext.py:jinjia2的扩展,用于缓存jinjia2模版;

jinja2ext.py

首先我们来简要剖析下flask-cache扩展jinjia2,用于缓存jinjia2模版片段。该文件提供一个类:CacheExtension,该类的父类是jinjia2.ext.Extension,在父类的基础上对parse(self, parser)进行了扩展(修改)。具体的代码需要对jinjia2有些了解,尤其是jinjia2.ext.Extension,这里不啰嗦。

backends.py

这里包含了flask-cache对外提供的内置的缓存类型:NullCache、SimpleCache、MemcachedCache 、GAEMemcachedCache、RedisCache 、FileSystemCache、SASLMemcachedCache。

  • NullCache是对werkzeug.contrib.cache中的NullCache进行简单封装;
  • SimpleCache是对werkzeug.contrib.cache中的SimpleCache进行简单封装;
  • MemcachedCache是对werkzeug.contrib.cache中的MemcachedCache进行简单封装;
  • GAEMemcachedCache是对werkzeug.contrib.cache中的GAEMemcachedCache进行简单封装;
  • RedisCache是对werkzeug.contrib.cache中的RedisCache进行简单封装;
  • FileSystemCache是对werkzeug.contrib.cache中的FileSystemCache进行简单封装;
  • SASLMemcachedCache是flask-cache扩展了werkzeug.contrib.cache中的基础类MemcachedCache,实现了一个值跨多个不同的key存储;

__init__.py

文件里面只包含一个类:Cache。该类最重要的两个函数:cached和memoize,都能作为装饰器使用。cached主要用于缓存视图函数或者非视图函数,而memoize是Memoization <http://en.wikipedia.org/wiki/Memoization>的一种体现,主要用于缓存函数结果,这样多次调用的函数,只需要计算一次。

阅读全文 »

Python魔术方法指南之二

反射 你可以通过魔术方法控制控制使用 isinstance() 和 issubclass() 内置方法的反射行为。这些魔术方法是: __instancecheck__(self, instance) :检查一个实例是不是你定义的类的实例 __subclasscheck__(self, subclass) :检查一个类是不是你定义的类的子类 这些方法的用例似乎很少,这也许是真的。我不会花更多的时间在这些魔术方法上因为他们并不是很重要,但是他们的确反应了Python中的面向对象编程的一些基本特性:非常容易的去做一些事情,即使并不是很必须。这些魔术方法看起来并不是很有用,但是当你需要的时候你会很高兴有这种特性。 可以调用的对象 你也许已经知道,在Python中,方法也是一种高等的对象。这意味着他们也可以被传递到方法中就像其他对象一样。这是一个非常惊人的特性。在Python中,一个特殊的魔术方法可以让类的实例的行为表现的像函数一样,你可以调用他们,将一个函数当做一个参数传到另外一个函数中等等。这是一个非常强大的特性让Python编程更加舒适甜美。 __call__(self, [args...]) :允许一个类的实例像函数一样被调用。实质上说,这意味着 x() 与 x.__call__() 是相同的。注意 __call__ 参数可变。这意味着你可以定义 __call__ 为其他你想要的函数,无论有多少个参数。 __call__ 在那些类的实例经常改变状态的时候会非常有效。调用这个实例是一种改变这个对象状态的直接和优雅的做法。用一个实例来表达最好不过了: class Entity: def __init__(self, size, x, y): self.x, self.y = x, y self.size = size def __call__(self, x, y): self.x, self.y = x, y 会话管理 在Python 2.5中,为了代码利用定义了一个新的关键词 with 语句。会话控制在Python中不罕见(之前是作为库的一部分被实现),直到 PEP343 被添加后。它被成为一级语言结构。你也许之前看到这样的语句: with open('foo.txt') as bar: # perform some action with bar 回话控制器通过包装一个 with 语句来设置和清理行为。回话控制器的行为通过两个魔术方法来定义: __enter__(self) 定义当使用 with 语句的时候会话管理器应该初始块被创建的时候的行为。注意 __enter__ 的返回值被 with 语句的目标或者 as 后的名字绑定。 __exit__(self, exception_type, exception_value, traceback) 定义当一个代码块被执行或者终止后会话管理器应该做什么。它可以被用来处理异常,清楚工作或者做一些代码块执行完毕之后的日常工作。如果代码块执行成功, exception_type , exception_value , 和 traceback 将会是 None 。否则的话你可以选择处理这个异常或者是直接交给用户处理。如果你想处理这个异常的话,确认 __exit__ 在所有结束之后会返回 True 。如果你想让异常被会话管理器处理的话,那么就这样处理。 __enter__ 和 __exit__ 对于明确有定义好的和日常行为的设置和清洁工作的类很有帮助。你也可以使用这些方法来创建一般的可以包装其他对象的会话管理器。以下是一个例子。 class Closer: '''通过with语句和一个close方法来关闭一个对象的会话管理器''' def __init__(self, obj): self.obj = obj def __enter__(self): return self.obj # bound to target def __exit__(self, exception_type, exception_val, trace): try: self.obj.close() except AttributeError: # obj isn't closable print 'Not closable.' return True # exception handled successfully 以下是一个使用 Closer 的例子,使用一个FTP链接来证明(一个可关闭的套接字): from magicmethods import Closer from ftplib import FTP with Closer ( FTP ( 'ftp.somesite.com' )) as conn : ... conn . dir () ... conn . dir () with Closer ( int ( 5 )) as i : ... i += 1 ... Not closable. i 6 你已经看到了我们的包装器如何静默的处理适当和不适当的使用行为。这是会话管理器和魔术方法的强大功能。 创建对象的描述器 描述器是通过得到,设置,删除的时候被访问的类。当然也可以修改其他的对象。描述器并不是鼓励的,他们注定被一个所有者类所持有。当创建面向对象的数据库或者类,里面含有相互依赖的属性时,描述器将会非常有用。一种典型的使用方法是用不同的单位表示同一个数值,或者表示某个数据的附加属性(比如坐标系上某个点包含了这个点到远点的距离信息)。 为了构建一个描述器,一个类必须有至少 __get__ 或者 __set__ 其中一个,并且 __delete__ 被实现。让我们看看这些魔术方法。 __get__(self, instance, owner) 定义当描述器的值被取得的时候的行为, instance 是拥有者对象的一个实例。 owner 是拥有者类本身。 __set__(self, instance, value) 定义当描述器值被改变时候的行为。 instance 是拥有者类的一个实例 value 是要设置的值。 __delete__(self, instance) 定义当描述器的值被删除的行为。instance是拥有者对象的实例。 以下是一个描述器的实例:单位转换。 class Meter(object): '''Descriptor for a meter.''' def __init__(self, value=0.0): self.value = float(value) def __get__(self, instance, owner): return self.value def __set__(self, instance, value): self.value = float(value) class Foot(object): '''Descriptor for a foot.''' def __get__(self, instance, owner): ...

阅读全文 »

Python魔术方法指南之一

介绍 此教程为我的数篇文章中的一个重点。主题是魔术方法。 什么是魔术方法?他们是面向对象的Python的一切。他们是可以给你的类增加”magic”的特殊方法。他们总是被双下划线所包围(e.g. __init__ 或者 __lt__ )。然而他们的文档却远没有提供应该有的内容。Python中所有的魔术方法均在Python官方文档中有相应描述,但是对于他们的描述比较混乱而且组织比较松散。很难找到有一个例子(也许他们原本打算的很好,在开始语言参考中有描述很详细,然而随之而来的确是枯燥的语法描述等等)。 所以,为了修补我认为Python文档应该修补的瑕疵,我决定给Python中的魔术方法提供一些用平淡的语言和实例驱使的文档。我在开始已经写了数篇博文,现在在这篇文章中对他们进行总结。 我希望你能够喜欢这篇文章。你可以将之当做一个教程,一个补习资料,或者一个参考。本文章的目的仅仅是为Python中的魔术方法提供一个友好的教程。 构造和初始化 每个人都知道一个最基本的魔术方法, __init__ 。通过此方法我们可以定义一个对象的初始操作。然而,当我调用 x = SomeClass() 的时候, __init__ 并不是第一个被调用的方法。实际上,还有一个叫做 __new__ 的方法,来构造这个实例。然后给在开始创建时候的初始化函数来传递参数。在对象生命周期的另一端,也有一个 __del__ 方法。我们现在来近距离的看一看这三个方法: __new__(cls, [...) __new__ 是在一个对象实例化的时候所调用的第一个方法。它的第一个参数是这个类,其他的参数是用来直接传递给 __init__ 方法。 __new__ 方法相当不常用,但是它有自己的特性,特别是当继承一个不可变的类型比如一个tuple或者string。我不希望在 __new__ 上有太多细节,因为并不是很有用处,但是在 Python文档 中有详细的阐述。 __init__(self, […) 此方法为类的初始化方法。当构造函数被调用的时候的任何参数都将会传给它。(比如如果我们调用 x = SomeClass(10, 'foo') ),那么 __init__ 将会得到两个参数10和foo。 __init__ 在Python的类定义中被广泛用到。 __del__(self) 如果 __new__ 和 __init__ 是对象的构造器的话,那么 __del__ 就是析构器。它不实现语句 del x (所以代码将不会翻译为 x.__del__() )。它定义的是当一个对象进行垃圾回收时候的行为。当一个对象在删除的时候需要更多的清洁工作的时候此方法会很有用,比如套接字对象或者是文件对象。注意,因为当解释器退出的时候如果对象还存在,不能保证 __del__ 能够被执行 放在一起的话,这里是一个 __init__ 和 __del__ 实际使用的例子。 from os.path import join class FileObject: '''给文件对象进行包装从而确认在删除时文件流关闭''' def __init__(self, filepath='~', filename='sample.txt'): #读写模式打开一个文件 self.file = open(join(filepath, filename), 'r+') def __del__(self): self.file.close() del self.file 让定制的类工作起来 使用Python的魔术方法的最大优势在于他们提供了一种简单的方法来让对象可以表现的像内置类型一样。那意味着你可以避免丑陋的,违反直觉的,不标准的的操作方法。在一些语言中,有一些操作很常用比如: if instance.equals(other_instance): # do something 在Python中你可以这样。但是这会让人迷惑且产生不必要的冗余。相同的操作因为不同的库会使用不同的名字,这样会产生不必要的工作。然而有了魔术方法的力量,我们可以定义一个方法(本例中为 __eq__ ),就说明了我们的意思: if instance == other_instance: #do something 这只是魔术方法的功能的一小部分。它让你可以定义符号的含义所以我们可以在我们的类中使用。就像内置类型一样。 用于比较的魔术方法 Python对实现对象的比较,使用魔术方法进行了大的逆转,使他们非常直观而不是笨拙的方法调用。而且还提供了一种方法可以重写Python对对象比较的默认行为(通过引用)。以下是这些方法和他们的作用。 __cmp__(self, other) __cmp__ 是最基本的用于比较的魔术方法。它实际上实现了所有的比较符号( ,==,!=,etc.),但是它的表现并不会总是如你所愿(比如,当一个实例与另一个实例相等是通过一个规则来判断,而一个实例大于另外一个实例是通过另外一个规则来判断)。如果 self other 的话 __cmp__ 应该返回一个负数,当 self == o 的时候会返回0 ,而当 self other 的时候会返回正数。通常最好的一种方式是去分别定义每一个比较符号而不是一次性将他们都定义。但是 __cmp__ 方法是你想要实现所有的比较符号而一个保持清楚明白的一个好的方法。 __eq__(self, other) 定义了等号的行为, == 。 __ne__(self, other) 定义了不等号的行为, != 。 __lt__(self, other) 定义了小于号的行为, 。 __gt__(self, other) 定义了大于等于号的行为, = 。 举一个例子,创建一个类来表现一个词语。我们也许会想要比较单词的字典序(通过字母表),通过默认的字符串比较的方法就可以实现,但是我们也想要通过一些其他的标准来实现,比如单词长度或者音节数量。在这个例子中,我们来比较长度实现。以下是实现代码: class Word(str): '''存储单词的类,定义比较单词的几种方法''' def __new__(cls, word): # 注意我们必须要用到__new__方法,因为str是不可变类型 # 所以我们必须在创建的时候将它初始化 if ' ' in word: print "Value contains spaces. Truncating to first space." word = word[:word.index(' ')] #单词是第一个空格之前的所有字符 return str.__new__(cls, word) def __gt__(self, other): return len(self) len(other) def __lt__(self, other): return len(self) len(other) def __ge__(self, other): return len(self) = len(other) def __le__(self, other): return len(self) = len(oth ...

阅读全文 »

Python高级话题之三---生成器(Generators)

简介

什么是Python的生成器?从语法上来说,生成器是一个带yield语句的函数。一个函数或者子程序只返回一次,但是一个生成器能暂停执行并返回一个中间的结果,反回一个值给调用者并暂停执行。当生成器的next()方法被调用的时候,它会准确地从离开的地方继续。

简单的说就是在函数的执行过程中,yield语句会把你需要的值返回给调用生成器的地方,然后退出函数,下一次调用生成器函数的时候又从上次中断的地方开始执行,而生成器内的所有变量参数都会被保存下来供下一次使用。

举例

def fib(max):  
    a, b = 1, 1  
    while a < max:  
        yield a #generators return an iterator that returns a stream of values.  
        a, b = b, a+b  


>>> fib(15)
<generator object fib at 0x1006033c0>


在函数fib(max)内定义了一个生成器,但是对fib(max)的调用永远只能获得一个单独的生成器对象,而不是执行函数里面的语句,这个对象(generator object)包含了函数的原始代码和函数调用的状态,这状态包括函数中变量值以及当前的执行点——函数在yield语句处暂停(suspended),返回当前的值并储存函数的调用状态,当需要下一个条目(item)时,可以再次调用next,从函数上次停止的状态继续执行,知道下一个yield语句。


生成器和函数的主要区别在于函数 return a value,生成器 yield a value同时标记或记忆 point of the yield 以便于在下次调用时从标记点恢复执行。 yield 使函数转换成生成器,而生成器反过来又返回迭代器。

阅读全文 »

Python高级话题之二----列表解析和生成器表达式

列表解析简介:

列表解析(list comprehension)来自函数式编程语言Haskell。它是一个非常有用、简单而且灵活的工具,可以用来动态地创建列表。它在Python2.0中被加入。

列表解析语法:

[expr for iter_var in iterable] 或 [expr for iter_var in iterable if cond_expr]


第一种语法:首先迭代iterable里所有内容,每一次迭代,都把iterable里相应内容放到iter_var中,再在表达式中应用该iter_var的内容,最后用表达式的计算值生成一个列表。
第二种语法:加入了判断语句,只有满足条件的内容才把iterable里相应内容放到iter_var中,再在表达式中应用该iter_var的内容,最后用表达式的计算值生成一个列表。

列表解析举例:

>>> [i + 1 for i in range(10)]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]


>>> [i + 1 for i in range(10) if i % 2]
[2, 4, 6, 8, 10]

生成器表达式简介:

生成器表达式是在python2.4中引入的,当序列过长, 而每次只需要获取一个元素时,应当考虑使用生成器表达式而不是列表解析。生成器表达式的语法和列表解析一样,只不过生成器表达式是被()括起来的,而不是[],如下:

生成器表达式语法:

(expr for iter_var in iterable) 或 (expr for iter_var in iterable if cond_expr) 

生成器表达式举例:

>>> L= (i + 1 for i in range(10) if i % 2) 

生成器表达式与列表解析比较:


生成器表达式并不真正创建数字列表, 而是返回一个生成器,这个生成器在每次计算出一个条目后,把这个条目“产生”(yield)出来。 生成器表达式使用了“惰性计算”,只有在检索时才被赋值( evaluated),所以在列表比较长的情况下使用内存上更有效。

阅读全文 »

Python高级话题之一----迭代器(iterator)

简介

首先我们从维基上看对迭代器的定义:迭代器(iterator)有时又称游标(cursor)是程式设计的软件设计模式,可在容器物件(container,例如链表或阵列)上遍访的接口,设计人员无需关心容器物件的内容。
在python中,对迭代器的定义是为类似序列的对象提供了一个具有序列特性的接口。它是在python2.2版本中引进进来的。python的迭代器无缝地支持序列对象,而且它还支持非序列类型,包括用户定义的对象。
python中的迭代器用起来很灵巧,你可以迭代不是序列对象但表现出序列行为的对象,例如字典的键、一个文件的行,等等。当你使用循环迭代一个对象条目时,你几乎分辨不出它是迭代器还是序列。你不必去关注这些,因为python让它像一个序列那样操作。

迭代器的作用(优点)

  • PEP234中对迭代器的作用有着明确的记录:
  • 提供了可扩展的迭代器接口;
  • 对字典、列表的迭代带来了性能的提升;
  • 创建真正的迭代接口,而不是原来的随机对象访问;
  • 与所有已经存在的用户定义的类以及扩展的模块序列和映射的对象向后兼容;
  • 迭代非序列对象时,可以创建更加简洁可读的代码;

如何迭代

从根本上说,迭代器就是有一个next()方法的对象,而不是通过索引来计数的。当你或是一个循环机制(例如for语句)需要下一个项时,调用迭代器的next()方法就可以获得它。条目全部取出后,会引发一个StopIteration异常,这并不是表示错误的发生,只是告诉外部的调用者,迭代完成。可以使用iter()函数来创建迭代器,另外还有一种就是自定义类。


方法1.
>>> lst = range(2)
>>> it = iter(lst)
>>> it
<listiterator object at 0x00BB62F0>

方法2.
class A:
    def __iter__(self):
        return self


    def next(self):
        if has_next_value(self):
            return next_value
        else:
            raise StopIteration
class B:
    def __iter__(self):
        return iterator_obj

A,B分别为两种对象模式(都是示例代码)。模式A表示,在A中定义了next方法,因此__iter__简单地返回自身即可。当不存在下一个值时,引发StopIteration异常。模式B表示,它使用了其它的Iterator对象,因此只需要定义__iter__即可,next不需要定义,因为返回的Iterator对象已经含有next方法了。如果是自已实现next方法,那么在返回值之前需要记住当前的状态,以便下一次运行时,可以取下一个值。


迭代器的缺点

迭代器不能回退,只能往前进行迭代。迭代器也不是线程安全的,在多线程环境中对可变集合使用迭代器是一个危险的操作。但如果小心谨慎,或者干脆贯彻函数式思想坚持使用不可变的集合,那这也不是什么大问题


python中内置的迭代数据类型

>>> a = [122, 221, 333]
>>> b = iter(a)
>>> b.next()
122
>>> b.next()
221
>>> b.next()
333
>>> b.next()
Traceback (most recent call last):
  File "", line 1, in ?
StopIteration
字典和文件也是python两个可迭代的数据类型,例子也就省略了。

阅读全文 »

Python 101:异常处理

原文地址: http://www.blog.pythonlibrary.org/2012/09/12/python-101-exception-handling/ Python提供一个强大的异常处理机制。异常处理是每个程序员需要学习的东西。它允许程序员在发生异常的时候继续他们的程序或正常地终止应用程序。 Python使用了try/except/finally声明。我们将花一些时间学习有关标准异常,如何创建一个自定义的异常,以及如何获得异常信息,以便进行调试的时候需要到它。 基本的异常处理 首先必须说,直接的异常处理(捕捉所有异常)是通常不推荐!你会看到如何使用直接的异常处理(捕捉所有异常),但我从来不会使用到它。直接的异常(捕捉所有异常)处理看起来像这样: try: print monkey except: print "This is an error message!" 正如你可以看到的,这将捕获所有异常,但你真的不知道任何有关异常的信息,因此处理起来相当棘手。因为你不知道什么异常被捕捉了,也不能打印出一个非常有用的信息给用户。但是从另外一方面来说,如果有些时候使用最直接的异常处理(捕捉所有的异常)的话也许还是有些作用的,我的一个读者指出:数据库的交互就是使用这种情况的最好场景。我发现我在处理数据库的时候通常会用到很宽泛的异常,因此我也对读者的观点表示一定的赞同。 让我们来看看一些正常的异常处理的例子: try: import spam except ImportError: print "Spam for eating, not importing!" try: print python except NameError, e: print e try: 1 / 0 except ZeroDivisionError: print "You can't divide by zero!" 第一个例子在导入spam的时,如果python没有找到它的时候会触发。一个很好的使用这种异常处理的场景就是导入一个替换模块。像python2.5以及以上的版本,导入md5模块会抛出ImportError异常,因为这个模块已经被hashlib 替换了。 另一个例外是NameError。当一个变量没有被定义,你就会得到这个异常。您还会注意到,我们在异常的部分增加了一个逗号和“e”。这让我们不仅能够捕获到错误,还能打印出来的异常信息,在这个例子中的异常信息是name ‘python’ is not defined。 第三个也是最后一个异常是如何捕捉零作为分母的一个例子。是的,不管如何,用户仍然试图除以零,往往你告诉他们不要。 现在,如果你想捕获多个错误但不是所有的错误,你会怎么做?让我们来看看! try: 5 + "python" except TypeError: print "You can't add strings and integers!" except WindowsError: print "A Windows error has occurred. Good luck figuring it out!" finally: print "The finally section ALWAYS run!" 在上面的代码中,你们将看到在一个try下有两个except声明。您可以添加尽可能多的异常当你想要或需要做的时候。 获取整个堆栈回溯 如果你想获得整个追踪的异常怎么办?Python有一个模块能达到这个目的,这就是所谓的、逻辑的traceback。这里有一个快速的小例子: import traceback try: with open("C:/path/to/py.log") as f: for line in f: print line except IOError, exception: print exception print print traceback.print_exc() 上面的代码将打印出异常信息,打印一个空行,然后打印整个追踪使用traceback模块的print_exc的方法。还有一堆其他方法在traceback模块,让您的自定义输出格式或获得各个部分的堆栈跟踪。更多细节和例子,但你应该检查出的 文档 。 还有另一种方法,得到整个回溯不使用 traceback 模块,至少不直接。相反,您可以使用Python的日志记录模块。下面是一个简单的例子: import logging logging.basicConfig(filename="sample.log", level=logging.INFO) log = logging.getLogger("ex") try: raise RuntimeError except RuntimeError, err: log.exception("RuntimeError!") 这将创建一个日志文件在同一目录下,在运行该脚本中包含以下内容: ERROR:ex:RuntimeError! Traceback (most recent call last): File "C:\Users\mdriscoll\Desktop\exceptionLogger.py", line 7, in module raise RuntimeError RuntimeError 创建一个自定义的异常 当你编写复杂的程序,你可能会发现需要创建自己的异常。幸运的是,在Python中编写一个新的异常是一个很容易的过程。这里有一个很简单的例子: class ExampleException(Exception): pass try: raise ExampleException("There are no droids here") except ExampleException, e: print e 结束语 现在你应该知道如何捕获异常,使用 traceback ,甚至可以创建自己的自定义异常。学会这些, 即使不好的事情发生! 你的脚本将会继续运行。当然,他们也不会帮助,如果电源熄灭(哈哈哈,这是一个玩笑)!学会处理异常能够强化你的代码,能够确保许多不同的情况下你的程序不会崩溃。 ...

阅读全文 »

 写地道的python

原文地址:http://www.jeffknupp.com/blog/2012/10/04/writing-idiomatic-python/ 惯用语法 格式化 Python有一个语言定义的一组标准格式的规则,被称为PEP8。如果您正在浏览Python项目的提交信息,您可能会发现它们充满了PEP8清理信息。原因很简单:如果大家都同意一个共同的的命名和格式化公约,Python代码作为一个整体瞬间变得更容易被新手和经验丰富的开发人员接受。阅读PEP,在编辑器中安装一个PEP8的风格检查插件,并开始编写代码以其他的Python开发人员将共同遵循的方式。下面列出了几个例子。 Identifier Type Format Example 类 骆驼拼写法 class StringManipulator: 变量 使用下划线连接词 words_joined_by_underscore = True 函数 使用下划线连接词 def are_words_joined_by_underscore(words): 私有类成员/函数 下划线作为前缀 def _update_statistics(self): 使用数据 交换两个变量时避免使用临时变量 在python中交换变量没有任何理由需要使用临时变量。我们可以使用元组,使我们的意图更加清晰。 不好的写法 temp = foo foo = bar bar = temp 惯用的 (foo, bar) = (bar, foo) 使用元组来分配参数 在python中,能够实现多个元素同时赋值。那些熟悉LISP可能知道这是'desctructuring bind’。 不好的写法 list_from_comma_separated_value_file = ['dog', 'Fido', 10] animal = list_from_comma_separated_value_file[0] name = list_from_comma_separated_value_file[1] age = list_from_comma_separated_value_file[2] 惯用的 list_from_comma_separated_value_file = ['dog', 'Fido', 10] (animal, name, age) = list_from_comma_separated_value_file 使用“.join”来连接一个单独的字符串列表中的元素 它的速度更快,使用更少的内存,你会看到它无处不反正。需要注意的是两个单引号表示我们创建的字符串列表元素之间的分隔符。 不好的写法 result_list = ['True', 'False', 'File not found'] result_string = '' for result in result_list: result_string += result 惯用的 result_list = ['True', 'False', 'File not found'] result_string = ''.join(result_list) 在dict.get()中使用默认值 经常被忽视的get()的定义是默认的参数。如果不使用默认值(或collections.defaultdict类),你的代码将充斥着混乱。请记住,争取清晰。 不好的写法 log_severity = None if 'severity' in configuration: log_severity = configuration['severity'] else: log_severity = log.Info 惯用的 log_severity = configuration.get('severity', log.Info) 使用上下文管理,以确保资源得到妥善管理 类似的语言的RAII的原则,如C + +和D,上下文管理器(意味着要使用对象的语句),可以使资源管理更安全和更明确的。典型的例子是文件IO。 不好的写法 file_handle = open(path_to_file, 'r') for line in file_handle.readlines(): if some_function_that_throws_exceptions(line): # do something file_handle.close() 惯用的 with open(path_to_file, 'r') as file_handle: for line in file_handle: if some_function_that_throws_exceptions(line): # do something # No need to explicitly call 'close'. Handled by the File context manager 了解itertools模块的内容 如果你像StackOverflow频繁的网站,您可能会注意到,形式的问题的答案:“为什么Python不能有以下明显有用的库函数吗?” 几乎总是引用itertools模块。itertools提供的函数式编程的中坚分子,应被视为基本构建块。更重要的是,itertools的文档中有一个“食谱”一节,提供使用itertools模块地道的实现常见的功能性编程结构。出于某种原因,似乎知道的“食谱”一节的Python开发人员的数量微乎其微。事实上,使用惯用语法的编写代码的开发者必须要知道的一部分是itertools,尤其当你重新造轮子。 控制结构 条件分支应避免被放置在同一行上 使用缩进来表示范围(如果在Python你已经这样做了),可以很容易地确定将被执行的条件语句的一部分。 不好的写法 if name: print (name) print address 惯用的 if name: print (name) print address 避免在一行中有多个语句 虽然语言的定义允许使用分号来让一行有多个语句,但是这样做没有道理的,因为代码更难阅读。通常情况下违背以前的规则。 不好的写法 if this_is_bad_code: rewrite_code(); make_it_more_readable(); 惯用的 if this_is_bad_code: rewrite_code() make_it_more_readable() 在复合的if语句中避免重复的变量名 当一个人想要检查的一些值,反复列出正在检查的变量,它的值是不必要的冗长。使用临时集合的意图明显。 不好的写法 if name == 'Tom' or name == 'Dick' or name == ...

阅读全文 »

在Python中使用测试驱动开发

测试驱动开发(TDD)是一个近些年被实践证明过的过程。把测试带入日常的编码过程而不是编码完成后才进行测试的过程应该是开发人员试图成为习惯的方式而不是空谈的方式。 测试驱动开发的整个过程是很容易被掌握的,而且给我们带来很多的好处--代码质量的提高,但是也清晰和专注于你要达到的目标它是什么以及你要怎样达到目标。测试驱动开发也可以无缝地与敏捷开发一起工作,在结对编程的时候,能够充分被利用,你将在后面会看到。 在本教程中,我将会介绍测试驱动开发的核心概念,提供使用python的nosetests单元测试包的相关例子。另外,我将提供一些可选的软件包。 什么是测试驱动开发? 这里采用了百度百科的测试驱动开发词条的含义: 测试驱动开发,英文全称Test-Driven Development,简称TDD,是一种不同于传统软件开发流程的新型的开发方法。它要求在编写某个功能的代码之前先编写测试代码,然后只编写使测试通过的功能代码,通过测试来推动整个开发的进行。这有助于编写简洁可用和高质量的代码,并加速开发过程。 Kent Beck先生最早在其极限编程(XP)方法论中,向大家推荐“测试驱动”这一最佳实践,还专门撰写了《测试驱动开发》一书,详细说明如何实现。经过几年的迅猛发展,测试驱动开发已经成长为一门独立的软件开发技术,其名气甚至盖过了极限编程。 如上图所示,测试驱动开发的过程如下: 1.编写一个失败的单元测试用例; 2.编写代码使得单元测试用例通过; 3.重构 如果必要的话,为每一个可能,重复此过程。 敏捷开发与测试驱动开发 简单的说,敏捷开发是一种以人为核心、迭代、循序渐进的开发方法。在敏捷开发中,软件项目的构建被切分成多个子项目,各个子项目的成果都经过测试,具备集成和可运行的特征。测试驱动开发是一个与敏捷开发过程中的理想和原则的完美匹配,是一个努力提供增量更新的产品的真正的质量,而不是数量。测试驱动开发是敏捷开发中的一项核心实践和技术,也是一种设计方法论。 单元测试的语法 我们使用Python的单元测试的主要方法是: assert-基础断言,允许用户自己扩展断言; assertEqual(a, b)-判断a和b是否相等; assertNotEqual(a, b)-检查a和b是否不相等; assertIn(a, b)-检查a是否在b中; assertNotIn(a, b)-检查a是否不在b中; assertFalse(a)-检查a的值是否是False; assertTrue(a)-检查a的值是否是True; assertIsInstance(a, TYPE)-检查a的类型是否是“TYPE”; assertRaises(ERROR, a, args)-检查当a使用参数args被调用的时候是否会引发ERROR; 也有些其它的方法提供给我们,您可以查看- Python单元测试文档的 -但是,在我的经验中,上面列出的是最常用的。下面我们将利用这些在我们的例子中。 用python测试驱动开发的例子 我们将要看到一个真正简单的例子,这个例子用来介绍使用python单元测试和测试驱动开发的概念。我们将编写一个非常简单的计算器类,包含加,减和其他简单的方法,你会期望的。按照测试驱动开发的做法,让我们说,我们有一个要求添加功能,这将决定两个数字,并返回输出。让我们来写一个失败的测试。 import unittest class TddInPythonExample(unittest.TestCase): def test_calculator_add_method_returns_correct_result(self): calc = Calculator() result = calc.add(2,2) self.assertEqual(4, result) 写这个测试十分简单: 1.首先需要从python的标准库中导入unittest模块; 2.接着,我们需要写一个包含不同测试用例的类; 3.最后,需要编写一个测试的函数,唯一的要求是函数名是以“test_”开头,这样能够被nosetest识别以及执行,我们将在后面详细介绍。 我们可以编写具体的测试代码在函数中的位置。我们初始化我们的计算器,这样我们就可以执行它的方法。在此之后,我们就可以调用add的方法,这是我们希望测试,其输出值存储在变量result中。一旦完成,我们就可以进行单元测试的assertEqual方法的使用,以确保我们的计算器的add方法的行为如预期一样。 现在如果我们运行测试代码,将会看到下面的失败信息: 从nosetest输出的结果来看,我们的代码中没有导入Calculator。这是因为我们根本还没有创建它!因此我们定义Calculator以及导入它: class Calculator(object): def add(self, x, y): pass import unittest from calculator import Calculator class TddInPythonExample(unittest.TestCase): def test_calculator_add_method_returns_correct_result(self): calc = Calculator() result = calc.add(2,2) self.assertEqual(4, result) 现在我们已经有了Calculator的定义了,让我们看看nosetest的提示: 所以,很显然,我们的添加的Calculator返回错误值,因为它不会做任何事情的时刻。很巧的是,nosetest能给出问题所在的那一行,这样我们可以确认,我们需要改变什么。让我们来修正Calculator方法,以保证我们的测试通过: class Calculator(object): def add(self, x, y): return x+y 结果: 成功了!我们已经定义了add方法,它按预期工作。但是,我们还有更多的工作要做,以确保我们已经测试正确。 如果有人要添加任何数字以外,会发生什么情况?实际上,Python会允许添加字符串和其他类型,但在我们的例子中,我们的计算器,它仅是允许添加数字。让我们添加另一个失败的测试,这种情况下,利用的assertRaises方法来测试: import unittest from calculator import Calculator class TddInPythonExample(unittest.TestCase): def test_calculator_add_method_returns_correct_result(self): calc = Calculator() result = calc.add(2,2) self.assertEqual(4, result) def test_ca ...

阅读全文 »

给Python初学者的技巧

原文地址:http://maxburstein.com/blog/python-shortcuts-for-the-python-beginner/ 下面这些内容是我多年使用python总结出来一些有用的提示和工具,希望对读者们有些帮助。 变量交换 x = 6 y = 5 x, y = y, x print x >>> 5 print y >>> 6 单行的if声明 print "Hello" if True else "World" >>> Hello 字符串连接 最后一个是一个很酷的连接了两种不同类型的对象的方式。 nfc = ["Packers", "49ers"] afc = ["Ravens", "Patriots"] print nfc + afc >>> ['Packers', '49ers', 'Ravens', 'Patriots'] print str(1) + " world" >>> 1 world print `1` + " world" >>> 1 world print 1, "world" >>> 1 world print nfc, 1 >>> ['Packers', '49ers'] 1 数字诀窍 #Floor Division (rounds down) print 5.0//2 >>> 2 #2 raised to the 5th power print 2**5 >> 32 小心除法和浮点数! print .3/.1 >>> 2.9999999999999996 print .3//.1 >>> 2.0 数值比较 x = 2 if 3 > x > 1: print x >>> 2 if 1 < x > 0: print x >>> 2 同时遍历两个列表 nfc = ["Packers", "49ers"] afc = ["Ravens", "Patriots"] for teama, teamb in zip(nfc, afc): print teama + " vs. " + teamb >>> Packers vs. Ravens >>> 49ers vs. Patriots 遍历列表同时得出序号和内容 teams = ["Packers", "49ers", "Ravens", "Patriots"] for index, team in enumerate(teams): print index, team >>> 0 Packers >>> 1 49ers >>> 2 Ravens >>> 3 Patriots 列表解析 numbers = [1,2,3,4,5,6] even = [number for number in numbers if number%2 == 0] 字典解析 teams = ["Packers", "49ers", "Ravens", "Patriots"] print {key: value for value, key in enumerate(teams)} >>> {'49ers': 1, 'Ravens': 2, 'Patriots': 3, 'Packers': 0} 初始列表值 items = [0]*3 print items >>> [0,0,0] 列表转换成字符串 teams = ["Packers", "49ers", "Ravens", "Patriots"] print ", ".join(teams) >>> 'Packers, 49ers, Ravens, Patriots' 从字典中获得值(key) data = {'user': 1, 'name': 'Max', 'three': 4} is_admin = data.get('admin', False) 列表的子集 x = [1,2,3,4,5,6] #First 3 print x[:3] >>> [1,2,3] #Middle 4 print x[1:5] >>> [2,3,4,5] #Last 3 print x[-3:] >>> [4,5,6] #Odd numbers print x[::2] >>> [1,3,5] #Even numbers print x[1::2] >>> [2,4,6] Collections模块 除了Python的内置数据类型,在collections模块中提供了一些额外的特殊用途。我发现计数器有时是非常有用的。你甚至可以找到一些有用的,如果你参与了今年Facebook的HackerCup。 from collections import Counter print Counter("hello") >>> Counter({'l': 2, 'h': 1, 'e': 1, 'o': 1}) Itertools模块 from itertools import combinations teams = ["Packers", "49ers", "Ravens", "Patriots"] for game in combinations(teams, 2): print game >>> ('Packers', '49ers') >>> ('Packers', 'Ravens') >>> ('Packers', 'Patriots') >>> ('49ers', 'Ravens') >>> ('49ers', 'Patriots') >>> ('Ravens', 'Patriots') False == True 这是一个有趣的一个比一个有用的技术。在Python中True和False是基本上是全局变量。因此: False = True if False: print "Hello" else: print "World" >>> Hello 如果你有任何其他很酷的提示/技巧,让他们在下面的意见。感谢您的阅读! ...

阅读全文 »