译文:http://blog.jobbole.com/61171/

本文展示一些高级的Python设计结构和它们的使用方法。在日常工作中,你可以根据需要选择合适的数据结构,例如对快速查找性的要求、对数据一致

性的要求或是对索引的要求等,同时也可以将各种数据结构合适地结合在一起,从而生成具有逻辑性并易于理解的数据模型。Python的数据结构从句法

上来看 非常直观,并且提供了大量的可选操作。这篇指南尝试将大部分常用的数据结构知识放到一起,并且提供对其最佳用法的探讨。

推导式(Comprehensions)

如果你已经使用了很长时间的Python,那么你至少应该听说过列表推导(list comprehensions)。这是一种将for循环、if表达式以及赋值语句放到单一语

句中的一种方法。换句话说,你能够通过一个表达式对一个列表做映射或过滤操作。

一个列表推导式包含以下几个部分:

  • 一个输入序列
  • 一个表示输入序列成员的变量
  • 一个可选的断言表达式
  • 一个将输入序列中满足断言表达式的成员变换成输出列表成员的输出表达式

举个例子,我们需要从一个输入列表中将所有大于0的整数平方生成一个新的序列,你也许会这么写:

  1. num = [1, 4, -5, 10, -7, 2, 3, -1]
  2. filtered_and_squared = []
  3.  
  4. for number in num:
  5. if number > 0:
  6. filtered_and_squared.append(number ** 2)
  7. print filtered_and_squared
  8.  
  9. # [1, 16, 100, 4, 9]

很简单是吧?但是这就会有4行代码,两层嵌套外加一个完全不必要的append操作。而如果使用filter、lambda和map函数,则能够将代码大大简化:

  1. num = [1, 4, -5, 10, -7, 2, 3, -1]
  2. filtered_and_squared = map(lambda x: x ** 2, filter(lambda x: x > 0, num))
  3. print filtered_and_squared
  4.  
  5. # [1, 16, 100, 4, 9]

嗯,这么一来代码就会在水平方向上展开。那么是否能够继续简化代码呢?列表推导能够给我们答案:

  1. num = [1, 4, -5, 10, -7, 2, 3, -1]
  2. filtered_and_squared = [ x**2 for x in num if x > 0]
  3. print filtered_and_squared
  4.  
  5. # [1, 16, 100, 4, 9]

  • 迭代器(iterator)遍历输入序列num的每个成员x
  • 断言式判断每个成员是否大于零
  • 如果成员大于零,则被交给输出表达式,平方之后成为输出列表的成员。

列表推导式被封装在一个列表中,所以很明显它能够立即生成一个新列表。这里只有一个type函数调用而没有隐式调用lambda函数,列表推导式正是

使用了一个常规的迭代器、一个表达式和一个if表达式来控制可选的参数。

另一方面,列表推导也可能会有一些负面效应,那就是整个列表必须一次性加载于内存之中,这对上面举的例子而言不是问题,甚至扩大若干倍之后也

都不是问题。但是总会达到极限,内存总会被用完。

针对上面的问题,生成器(Generator)能够很好的解决。生成器表达式不会一次将整个列表加载到内存之中,而是生成一个生成器对象

(Generator objector),所以一次只加载一个列表元素。

生成器表达式同列表推导式有着几乎相同的语法结构,区别在于生成器表达式是被圆括号包围,而不是方括号:

  1. num = [1, 4, -5, 10, -7, 2, 3, -1]
  2. filtered_and_squared = ( x**2 for x in num if x > 0 )
  3. print filtered_and_squared
  4.  
  5. # <generator object <genexpr> at 0x00583E18>
  6.  
  7. for item in filtered_and_squared:
  8. print item
  9.  
  10. # 1, 16, 100 4,9

这比列表推导效率稍微提高一些,让我们再一次改造一下代码:

  1. num = [1, 4, -5, 10, -7, 2, 3, -1]
  2.  
  3. def square_generator(optional_parameter):
  4. return (x ** 2 for x in num if x > optional_parameter)
  5.  
  6. print square_generator(0)
  7. # <generator object <genexpr> at 0x004E6418>
  8.  
  9. # Option I
  10. for k in square_generator(0):
  11. print k
  12. # 1, 16, 100, 4, 9
  13.  
  14. # Option II
  15. g = list(square_generator(0))
  16. print g
  17. # [1, 16, 100, 4, 9]

但除非是面对非常大的列表,否则是不会看出明显区别的。

下例使用zip()函数一次处理两个或多个列表中的元素:

  1. alist = ['a1', 'a2', 'a3']
  2. blist = ['', '', '']
  3.  
  4. for a, b in zip(alist, blist):
  5. print a, b
  6.  
  7. # a1 1
  8. # a2 2
  9. # a3 3

再来看一个通过两阶列表推导式遍历目录的例子:

  1. import os
  2. def tree(top):
  3. for path, names, fnames in os.walk(top):
  4. for fname in fnames:
  5. yield os.path.join(path, fname)
  6.  
  7. for name in tree('C:\Users\XXX\Downloads\Test'):
  8. print name

装饰器(Decorators)

装饰器为我们提供了一个增加已有函数或类的功能的有效方法。听起来是不是很像Java中的面向切面编程(Aspect-Oriented Programming)概念?

两者都很简单,并且装饰器有着更为强大的功能。举个例子,假定你希望在一个函数的入口和退出点做一些特别的操作(比如一些 安全、追踪以及锁

定等操作)就可以使用装饰器。

装饰器是一个包装了另一个函数的特殊函数:主函数被调用,并且其返回值将会被传给装饰器,接下来装饰器将返回一个包装了主函数的替代函数,

程序的其他部分看到的将是这个包装函数。语法糖@标识了装饰器。好了,让我们回到刚才的例子。我们将用装饰器做一些更典型的操作:

  1. import time
  2. from functools import wraps
  3.  
  4. def timethis(func):
  5. '''
  6. Decorator that reports the execution time.
  7. '''
  8. @wraps(func)
  9. def wrapper(*args, **kwargs):
  10. start = time.time()
  11. result = func(*args, **kwargs)
  12. end = time.time()
  13. print(func.__name__, end-start)
  14. return result
  15. return wrapper
  16.  
  17. @timethis
  18. def countdown(n):
  19. while n > 0:
  20. n -= 1
  21.  
  22. countdown(100000)
  23.  
  24. # ('countdown', 0.006999969482421875

当你写下如下代码时:

  1. @timethis
  2. def countdown(n):
 

意味着你分开执行了以下步骤:

  1. def countdown(n):
  2. ...
  3. countdown = timethis(countdown)
 

装饰器函数中的代码创建了一个新的函数(正如此例中的wrapper函数),它用 *args 和 **kwargs 接收任意的输入参数,并且在此函数内调用原

函数并且返回其结果。你可以根据自己的需要放置任何额外的代码(例如本例中的计时操作),新创建的包装函数将作 为结果返回并取代原函数。

  1. @decorator
  2. def function():
  3. print("inside function")
 

当编译器查看以上代码时,function()函数将会被编译,并且函数返回对象将会被传给装饰器代码,装饰器将会在做完相关操作之后用一个新的函数

对象代替原函数。装饰器代码是什么样的?大部分的例子都是将装饰器定义为函数,而我发觉将装饰器定义成类更容易理解其功能,并且这样更能发

挥装饰器机制的威力。对装饰器的类实现唯一要求是它必须能如函数一般使用,也就是说它必须是可调用的。所以,如果想这么做这个类必须实现

__call__方法。这样的装饰器应该用来做些什么?它可以做任何事,但通常它用在当你想在一些特殊的地方使用原函数时,但这不是必须的,例如:

  1. class decorator(object):
  2.  
  3. def __init__(self, f):
  4. print("inside decorator.__init__()")
  5. f() # Prove that function definition has completed
  6.  
  7. def __call__(self):
  8. print("inside decorator.__call__()")
  9.  
  10. @decorator
  11. def function():
  12. print("inside function()")
  13.  
  14. print("Finished decorating function()")
  15.  
  16. function()
  17.  
  18. # inside decorator.__init__()
  19. # inside function()
  20. # Finished decorating function()
  21. # inside decorator.__call__()
 

译者注:
1. 语法糖@decorator相当于function=decorator(function),在此调用decorator的__init__打印“inside decorator.__init__()”
2. 随后执行f()打印“inside function()”
3. 随后执行“print(“Finished decorating function()”)”
4. 最后在调用function函数时,由于使用装饰器包装,因此执行decorator的__call__打印 “inside decorator.__call__()”。

一个更实际的例子:

  1. def decorator(func):
  2. def modify(*args, **kwargs):
  3. variable = kwargs.pop('variable', None)
  4. print variable
  5. x,y=func(*args, **kwargs)
  6. return x,y
  7. return modify
  8.  
  9. @decorator
  10. def func(a,b):
  11. print a**2,b**2
  12. return a**2,b**2
  13.  
  14. func(a=4, b=5, variable="hi")
  15. func(a=4, b=5)
  16.  
  17. # hi
  18. # 16 25
  19. # None
  20. # 16 25
 

上下文管理库(ContextLib)

contextlib模块包含了与上下文管理器和with声明相关的工具。通常如果你想写一个上下文管理器,则你需要定义一个类包含__enter__

方法以及__exit__方法,例如:

  1. import time
  2. class demo:
  3. def __init__(self, label):
  4. self.label = label
  5.  
  6. def __enter__(self):
  7. self.start = time.time()
  8.  
  9. def __exit__(self, exc_ty, exc_val, exc_tb):
  10. end = time.time()
  11. print('{}: {}'.format(self.label, end - self.start))
 

完整的例子在此:

  1. import time
  2.  
  3. class demo:
  4. def __init__(self, label):
  5. self.label = label
  6.  
  7. def __enter__(self):
  8. self.start = time.time()
  9.  
  10. def __exit__(self, exc_ty, exc_val, exc_tb):
  11. end = time.time()
  12. print('{}: {}'.format(self.label, end - self.start))
  13.  
  14. with demo('counting'):
  15. n = 10000000
  16. while n > 0:
  17. n -= 1
  18.  
  19. # counting: 1.36000013351
 

上下文管理器被with声明所激活,这个API涉及到两个方法。
1. __enter__方法,当执行流进入with代码块时,__enter__方法将执行。并且它将返回一个可供上下文使用的对象。
2. 当执行流离开with代码块时,__exit__方法被调用,它将清理被使用的资源。

利用@contextmanager装饰器改写上面那个例子:

  1. from contextlib import contextmanager
  2. import time
  3.  
  4. @contextmanager
  5. def demo(label):
  6. start = time.time()
  7. try:
  8. yield
  9. finally:
  10. end = time.time()
  11. print('{}: {}'.format(label, end - start))
  12.  
  13. with demo('counting'):
  14. n = 10000000
  15. while n > 0:
  16. n -= 1
  17.  
  18. # counting: 1.32399988174
 

看上面这个例子,函数中yield之前的所有代码都类似于上下文管理器中__enter__方法的内容。而yield之后的所有代码都如__exit__

方法的内容。如果执行过程中发生了异常,则会在yield语句触发。

描述器(Descriptors)

描述器决定了对象属性是如何被访问的。描述器的作用是定制当你想引用一个属性时所发生的操作。

构建描述器的方法是至少定义以下三个方法中的一个。需要注意,下文中的instance是包含被访问属性的对象实例,而owner则是被描述器修辞的类。

  • __get__(self, instance, owner) – 这个方法是当属性被通过(value = obj.attr)的方式获取时调用
  • __set__(self, instance, value) – 这个方法是当希望设置属性的值(obj.attr = ‘value’)时被调用,该方法不会返回任何值。
  • __delete__(self, instance) – 当从一个对象中删除一个属性时(del obj.attr),调用此方法。

译者注:对于instance和owner的理解,考虑以下代码:

  1. class Celsius(object):
  2. def __init__(self, value=0.0):
  3. self.value = float(value)
  4. def __get__(self, instance, owner):
  5. return self.value
  6. def __set__(self, instance, value):
  7. self.value = float(value)
  8.  
  9. class Temperature(object):
  10. celsius = Celsius()
  11.  
  12. temp=Temperature()
  13. temp.celsius #calls Celsius.__get__
 

上例中,instance指的是temp,而owner则是Temperature。

LazyLoading Properties例子:

  1. import weakref
  2.  
  3. class lazyattribute(object):
  4. def __init__(self, f):
  5. self.data = weakref.WeakKeyDictionary()
  6. self.f = f
  7. def __get__(self, obj, cls):
  8. if obj not in self.data:
  9. self.data[obj] = self.f(obj)
  10. return self.data[obj]
  11.  
  12. class Foo(object):
  13. @lazyattribute
  14. def bar(self):
  15. print "Being lazy"
  16. return 42
  17.  
  18. f = Foo()
  19.  
  20. print f.bar
  21. # Being lazy
  22. #
  23.  
  24. print f.bar
  25. #

描述器很好的总结了Python中的绑定方法(bound method)这个概念,绑定方法是经典类(classic classes)的实现核心。在经典类中,当在

一个对象实例的字典中没有找到某个属性时,会继续到类的字典中查找,然后再到基类的字典中,就这么一直递归 的查找下去。如果在类字典

中找到这个属性,解释器会检查找到的对象是不是一个Python函数对象。如果是,则返回的并不是这个对象本身,而是返回一个柯 里化

(currying function)的包装器对象。当调用这个包装器时,它会首先在参数列表之前插入实例,然后再调用原函数。

译者注:
1. 柯里化 – http://zh.wikipedia.org/wiki/%E6%9F%AF%E9%87%8C%E5%8C%96
2. function,method,bound method及unbound
method的区别。首先,函数(function)是由def或lambda创建的。当一个函数在class

语句块中定义或是由type来创建时,它会
转成一个非绑定方法(unbound method),而当通过类实例(instance)来访问此方法的时候,它将转

成绑定方法(bound
method),绑定方法会自动将实例作为第一个参数传入方法。综上所述,方法是出现在类中的函数,绑定方法是一个绑定了

具体实例的方法,反之则是非绑定
方法。

综上,描述器被赋值给类,而这些特殊的方法就在属性被访问的时候根据具体的访问类型自动地调用。

元类(MetaClasses)

元类提供了一个改变Python类行为的有效方式。

元类的定义是“一个类的类”。任何实例是它自己的类都是元类。

  1. class demo(object):
  2. pass
  3.  
  4. obj = demo()
  5.  
  6. print "Class of obj is {0}".format(obj.__class__)
  7. print "Class of obj is {0}".format(demo.__class__)
  8.  
  9. # Class of obj is <class '__main__.demo'>
  10. # Class of obj is <type 'type'>
 

在上例中,我们定义了一个类demo,并且生成了一个该类的对象obj。首先,可以看到obj的__class__是demo。有意思的来了,那么demo

的class又是什么呢?可以看到demo的__class__是type。

所以说type是python类的类,换句话说,上例中的obj是一个demo的对象,而demo本身又是type的一个对象。

所以说type就是一个元类,而且是python中最常见的元类,因为它使python中所有类的默认元类。

因为元类是类的类,所以它被用来创建类(正如类是被用来创建对象的一样)。但是,难道我们不是通过一个标准的类定义来创建类的么?的确是这样,

但是python内部的运作机制如下:

    • 当看见一个类定义,python会收集所有属性到一个字典中。
    • 当类定义结束,python将决定类的元类,我们就称它为Meta吧。
    • 最后,python执行Meta(name, bases, dct),其中:

a. Meta是元类,所以这个调用是实例化它。
b. name是新建类的类名。
c. bases是新建类的基类元组
d. dct将属性名映射到对象,列出所有的类属性。

那么如何确定一个类(A)的元类呢?简单来说,如果一个类(A)自身或其基类(Base_A)之一有__metaclass__属性存在,则这个类(A/Base_A)就是

类(A)的元类。否则type就将是类(A)的元类。

模式(Patterns)

“请求宽恕比请求许可更容易(EFAP)”

这个Python设计原则是这么说的“请求宽恕比请求许可更容易(EFAP)”。不提倡深思熟虑的设计思路,这个原则是说应该尽量去尝试,如果遇到
错误,

则给予妥善的处理。Python有着强大的异常处理机制可以支持这种尝试,这些机制帮助程序员开发出更为稳定,容错性更高的程序。

单例

单例是指只能同时存在一个的实例对象。Python提供了很多方法来实现单例。

Null对象

Null对象能够用来代替None类型以避免对None的测试。

观察者

观察者模式允许多个对象访问同一份数据。

构造函数

构造函数的参数经常被赋值给实例的变量。这种模式能够用一行代码替代多个手动赋值语句。

总结

谢谢阅读,如有疑问,请留言讨论。

Python高级编程技巧(转)的更多相关文章

  1. python高级编程技巧

    由python高级编程处学习 http://blog.sina.com.cn/s/blog_a89e19440101fb28.html Python列表解析语法[]和生成 器()语法类似 [expr  ...

  2. Python的几个高级编程技巧

    Python有一些技巧对你来说是新知识,但是还有一些技巧会让你的代码效率大幅提升. 本文总结了一下自己用到的一些Python高级编程技巧,希望对大家有帮助. 列表生成器 a=[1,2,3] [x*x ...

  3. python高级编程:有用的设计模式3

    # -*- coding: utf-8 -*-__author__ = 'Administrator'#python高级编程:有用的设计模式#访问者:有助于将算法从数据结构中分离出来"&qu ...

  4. python高级编程:有用的设计模式2

    # -*- coding: utf-8 -*- __author__ = 'Administrator' #python高级编程:有用的设计模式 #代理 """ 代理对一 ...

  5. python高级编程:有用的设计模式1

    # -*- coding: utf-8 -*-__author__ = 'Administrator'#python高级编程:有用的设计模式#设计械是可复用的,某种程序上它对软件设计中觉问题提供的语言 ...

  6. python高级编程之选择好名称:完

    由于时间关系,python高级编程不在放在这边进行学习了,如果需要的朋友可以看下面的网盘进行下载 # # -*- coding: utf-8 -*- # # python:2.x # __author ...

  7. python高级编程读书笔记(一)

    python高级编程读书笔记(一) python 高级编程读书笔记,记录一下基础和高级用法 python2和python3兼容处理 使用sys模块使程序python2和python3兼容 import ...

  8. python高级编程之列表推导式

    1. 一个简单的例子 在Python中,如果我们想修改列表中所有元素的值,可以使用 for 循环语句来实现. 例如,将一个列表中的每个元素都替换为它的平方: >>> L = [1, ...

  9. Python高级编程之生成器(Generator)与coroutine(二):coroutine介绍

    原创作品,转载请注明出处:点我 上一篇文章Python高级编程之生成器(Generator)与coroutine(一):Generator中,我们介绍了什么是Generator,以及写了几个使用Gen ...

随机推荐

  1. 关于对afx_msg的解释-----来源百度百科

    1AFX前缀 Afx前缀是微软MFC一个小组的名称简写,并没有别的意义. MFC的很多代码,包括全局函数名.宏.头文件名都使用了"Afx". Afx*.h是一组MFC的核心头文件, ...

  2. 【Java面试题】40 你所知道的集合类都有哪些?主要方法?

    线性表,链表,哈希表是常用的数据结构,在进行Java开发时,JDK已经为我们提供了一系列相应的类来实现基本的数据结构.这些类均在java.util包中.本文试图通过简单的描述,向读者阐述各个类的作用以 ...

  3. sql 一些题目

    这道SQL笔试题你会怎么写(转) 最近面试了一些Senior BI的候选人,行业经验三年到七年不等,起初觉得这个Level的无需准备笔试题,碍于领导执念,就在真实项目中提取5道SQL题目,这里仅单说其 ...

  4. gradle教程 [原创](eclipse/ADT下 非插件 非Android Studio/AS)纯手打 第三篇:gradle完整的实战

    上两篇的地址 安装配置 http://www.cnblogs.com/uncle2000/p/4276833.html 简单实战 http://www.cnblogs.com/uncle2000/p/ ...

  5. mongodb 安装(windows mongodb 安装)

    MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的.他支持的数据结构非常松散,是类似json的bjson格式,因此可以存储比较复杂的数据类型.M ...

  6. Git 的BUG小结

    Git 的BUG小结 Git 在push的时候出现了: fatal: The remote end hung up unexpectedly 在网上找了非常多  发现出现了下面错误提示也可能是同样的问 ...

  7. Leetcode: mimimum depth of tree, path sum, path sum II

    思路: 简单搜索 总结: dfs 框架 1. 需要打印路径. 在 dfs 函数中假如 vector 变量, 不用 & 修饰的话就不需要 undo 2. 不需要打印路径, 可设置全局变量 ans ...

  8. Rollup 与 webpack的区别

    特性: webpack 拆分代码, 按需加载: Rollup 所有资源放在同一个地方,一次性加载,利用 tree-shake 特性来剔除项目中未使用的代码,减少冗余,但是webpack2已经逐渐支持t ...

  9. Python 数据类型:数值

    数值类型分为:整型 .长整型 .浮点型 .复数型 整型示例: In [1]: a = 100 # 整型也就是整数类型 In [2]: type(a) # 整型的英文缩写为int Out[2]: int ...

  10. CentOS 下使用yum 命令安装MySQL

    CentOS Linux下使用yum 命令安装MySQL过程记录. 1. 查看服务器中有没有安装过MySQL 1. 查看有没有安装包: yum list mysql* #移除已经安装的mysql yu ...