Python高级编程技巧
2016-11-02 14:27
267 查看
正文:
本文展示一些高级的Python设计结构和它们的使用方法。在日常工作中,你可以根据需要选择合适的数据结构,例如对快速查找性的要求、对数据一致性的要求或是对索引的要求等,同时也可以将各种数据结构合适地结合在一起,从而生成具有逻辑性并易于理解的数据模型。Python的数据结构从句法上来看非常直观,并且提供了大量的可选操作。这篇指南尝试将大部分常用的数据结构知识放到一起,并且提供对其最佳用法的探讨。
推导式(Comprehensions)
如果你已经使用了很长时间的Python,那么你至少应该听说过列表推导(list comprehensions)。这是一种将for循环、if表达式以及赋值语句放到单一语句中的一种方法。换句话说,你能够通过一个表达式对一个列表做映射或过滤操作。一个列表推导式包含以下几个部分:
一个输入序列
一个表示输入序列成员的变量
一个可选的断言表达式
一个将输入序列中满足断言表达式的成员变换成输出列表成员的输出表达式
举个例子,我们需要从一个输入列表中将所有大于0的整数平方生成一个新的序列,你也许会这么写:
num = [1, 4, -5, 10, -7, 2, 3, -1] filtered_and_squared = [] for number in num: if number > 0: filtered_and_squared.append(number ** 2) print filtered_and_squared
很简单是吧?但是这就会有4行代码,两层嵌套外加一个完全不必要的append操作。而如果使用filter、lambda和map函数,则能够将代码大大简化:
num=[1,4,-5,10,-7,2,3,-1] filtered_and_squared=map(lambdax:x**2,filter(lambdax:x>0,num)) printfiltered_and_squared # [1, 16, 100, 4, 9]
嗯,这么一来代码就会在水平方向上展开。那么是否能够继续简化代码呢?列表推导能够给我们答案:
Python
12345 | num = [1, 4, -5, 10, -7, 2, 3, -1]filtered_and_squared = [ x**2 for x in num if x > 0]print filtered_and_squared # [1, 16, 100, 4, 9] |
断言式判断每个成员是否大于零
如果成员大于零,则被交给输出表达式,平方之后成为输出列表的成员。
列表推导式被封装在一个列表中,所以很明显它能够立即生成一个新列表。这里只有一个type函数调用而没有隐式调用lambda函数,列表推导式正是使用了一个常规的迭代器、一个表达式和一个if表达式来控制可选的参数。另一方面,列表推导也可能会有一些负面效应,那就是整个列表必须一次性加载于内存之中,这对上面举的例子而言不是问题,甚至扩大若干倍之后也都不是问题。但是总会达到极限,内存总会被用完。针对上面的问题,生成器(Generator)能够很好的解决。生成器表达式不会一次将整个列表加载到内存之中,而是生成一个生成器对象(Generator objector),所以一次只加载一个列表元素。生成器表达式同列表推导式有着几乎相同的语法结构,区别在于生成器表达式是被圆括号包围,而不是方括号:Python
1 2 3 4 5 6 7 8 9 10 | num=[1,4,-5,10,-7,2,3,-1] filtered_and_squared=(x**2forxinnumifx>0) printfiltered_and_squared # <generator object <genexpr> at 0x00583E18> foriteminfiltered_and_squared: printitem # 1, 16, 100 4,9 |
Python
1234567891011121314151617 | num = [1, 4, -5, 10, -7, 2, 3, -1] def square_generator(optional_parameter): return (x ** 2 for x in num if x > optional_parameter) print square_generator(0)# <generator object <genexpr> at 0x004E6418> # Option Ifor k in square_generator(0): print k# 1, 16, 100, 4, 9 # Option IIg = list(square_generator(0))print g# [1, 16, 100, 4, 9] |
1 2 3 4 5 6 7 8 9 | alist=['a1','a2','a3'] blist=['1','2','3'] fora,binzip(alist,blist): printa,b # a1 1 # a2 2 # a3 3 |
Python
12345678 | import osdef tree(top): for path, names, fnames in os.walk(top): for fname in fnames: yield os.path.join(path, fname) for name in tree('C:\Users\XXX\Downloads\Test'): print name |
装饰器(Decorators)
装饰器为我们提供了一个增加已有函数或类的功能的有效方法。听起来是不是很像Java中的面向切面编程(Aspect-Oriented Programming)概念?两者都很简单,并且装饰器有着更为强大的功能。举个例子,假定你希望在一个函数的入口和退出点做一些特别的操作(比如一些安全、追踪以及锁定等操作)就可以使用装饰器。装饰器是一个包装了另一个函数的特殊函数:主函数被调用,并且其返回值将会被传给装饰器,接下来装饰器将返回一个包装了主函数的替代函数,程序的其他部分看到的将是这个包装函数。Python1 2 3 4 5 6 7 8 9 10 | deftimethis(func): ''' Decorator that reports the execution time. ''' pass @timethis defcountdown(n): whilen>0: n-=1 |
好了,让我们回到刚才的例子。我们将用装饰器做一些更典型的操作:
Python
123456789101112131415161718192021222324 | import timefrom functools import wraps def timethis(func): ''' Decorator that reports the execution time. ''' @wraps(func) def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(func.__name__, end-start) return result return wrapper @timethisdef countdown(n): while n > 0: n -= 1 countdown(100000) # ('countdown', 0.006999969482421875) |
1 2 | @timethis defcountdown(n): |
Python
123 | def countdown(n):...countdown = timethis(countdown) |
1 2 3 | @decorator deffunction(): print("inside function") |
装饰器代码是什么样的?大部分的例子都是将装饰器定义为函数,而我发觉将装饰器定义成类更容易理解其功能,并且这样更能发挥装饰器机制的威力。
对装饰器的类实现唯一要求是它必须能如函数一般使用,也就是说它必须是可调用的。所以,如果想这么做这个类必须实现__call__方法。
这样的装饰器应该用来做些什么?它可以做任何事,但通常它用在当你想在一些特殊的地方使用原函数时,但这不是必须的,例如:
Python
123456789101112131415161718192021 | class decorator(object): def __init__(self, f): print("inside decorator.__init__()") f() # Prove that function definition has completed def __call__(self): print("inside decorator.__call__()") @decoratordef function(): print("inside function()") print("Finished decorating function()") function() # inside decorator.__init__()# inside function()# Finished decorating function()# 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__()”。一个更实际的例子:Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | defdecorator(func): defmodify(*args,**kwargs): variable=kwargs.pop('variable',None) printvariable x,y=func(*args,**kwargs) returnx,y returnmodify @decorator deffunc(a,b): printa**2,b**2 returna**2,b**2 func(a=4,b=5,variable="hi") func(a=4,b=5) # hi # 16 25 # None # 16 25 |
上下文管理库(ContextLib)
contextlib模块包含了与上下文管理器和with声明相关的工具。通常如果你想写一个上下文管理器,则你需要定义一个类包含__enter__方法以及__exit__方法,例如:Python
1234567891011 | import timeclass demo: def __init__(self, label): self.label = label def __enter__(self): self.start = time.time() def __exit__(self, exc_ty, exc_val, exc_tb): end = time.time() print('{}: {}'.format(self.label, end - self.start)) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | importtime classdemo: def__init__(self,label): self.label=label def__enter__(self): self.start=time.time() def__exit__(self,exc_ty,exc_val,exc_tb): end=time.time() print('{}: {}'.format(self.label,end-self.start)) withdemo('counting'): n=10000000 whilen>0: n-=1 # counting: 1.36000013351 |
1. __enter__方法,当执行流进入with代码块时,__enter__方法将执行。并且它将返回一个可供上下文使用的对象。
2. 当执行流离开with代码块时,__exit__方法被调用,它将清理被使用的资源。
利用@contextmanager装饰器改写上面那个例子:
Python
123456789101112131415161718 | from contextlib import contextmanagerimport time @contextmanagerdef demo(label): start = time.time() try: yield finally: end = time.time() print('{}: {}'.format(label, end - start)) with demo('counting'): n = 10000000 while n > 0: n -= 1 # counting: 1.32399988174 |
描述器(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的理解,考虑以下代码:Python
1 2 3 4 5 6 7 8 9 10 11 12 13 | classCelsius(object): def__init__(self,value=0.0): self.value=float(value) def__get__(self,instance,owner): returnself.value def__set__(self,instance,value): self.value=float(value) classTemperature(object): celsius=Celsius() temp=Temperature() temp.celsius#calls Celsius.__get__ |
LazyLoading Properties例子:
Python
12345678910111213141516171819202122232425 | import weakref class lazyattribute(object): def __init__(self, f): self.data = weakref.WeakKeyDictionary() self.f = f def __get__(self, obj, cls): if obj not in self.data: self.data[obj] = self.f(obj) return self.data[obj] class Foo(object): @lazyattribute def bar(self): print "Being lazy" return 42 f = Foo() print f.bar# Being lazy# 42 print f.bar# 42 |
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类行为的有效方式。元类的定义是“一个类的类”。任何实例是它自己的类都是元类。Python1 2 3 4 5 6 7 8 9 10 | classdemo(object): pass obj=demo() print"Class of obj is {0}".format(obj.__class__) print"Class of obj is {0}".format(demo.__class__) # Class of obj is <class '__main__.demo'> # Class of obj is <type '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高级编程技巧
- 介绍Python中的一些高级编程技巧
- Python 高级编程技巧
- python 从入门到实践 第五章习题 (高级编程技巧 week3-1)
- Python高级编程技巧
- Python高级编程技巧
- Python高级编程技巧
- Python高级编程技巧实战 基于Python项目与面试题讲解
- python中若干高级编程技巧
- Python高级编程技巧
- Python高级编程技巧(转)
- Python 高级编程技巧
- Python高级编程技巧
- Python高级编程技巧
- Python高级编程技巧
- Python高级编程技巧
- python 的一些高级编程技巧
- Python 高效编程技巧
- C++Builder 的一些高级编程技巧
- DOS批处理高级教程:第七章 DOS批处理编程高级技巧(转)