Python高级编程技巧_第1页
Python高级编程技巧_第2页
Python高级编程技巧_第3页
Python高级编程技巧_第4页
Python高级编程技巧_第5页
已阅读5页,还剩6页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、Python高级编程技巧本文展示一些高级的Python设计结构和它们的使用方法。在日常工作中,你可以根据需要选择合适的数据结构,例如对快速查找性的要求、对数据一致性的要求或是对索引的要求等,同时也可以将各种数据结构合适地结合在一起,从而生成具有逻辑性并易于理解的数据模型。Python的数据结构从句法上来看非常直观,并且提供了大量的可选操作。这篇指南尝试将大部分常用的数据结构知识放到一起,并且提供对其最佳用法的探讨。推导式(Comprehensions)如果你已经使用了很长时间的Python,那么你至少应该听说过列表推导(list comprehensions)。这是一种将for循环、if表达式

2、以及赋值语句放到单一语句中的一种方法。换句话说,你能够通过一个表达式对一个列表做映射或过滤操作。一个列表推导式包含以下几个部分:一个输入序列一个表示输入序列成员的变量一个可选的断言表达式一个将输入序列中满足断言表达式的成员变换成输出列表成员的输出表达式举个例子,我们需要从一个输入列表num = 1, 4, -5, 10, -7, 2, 3, -1filtered_and_squared = for number in num: if number 0: filtered_and_squared.append(number * 2)print filtered_and_squared# 1, 1

3、6, 100, 4, 9很简单是吧?但是这就会有4行代码,两层嵌套外加一个完全不必要的append操作。而如果使用filter、lambda和map函数,则能够将代码大大简化:num = 1, 4, -5, 10, -7, 2, 3, -1filtered_and_squared = map(lambda x: x * 2, filter(lambda x: x 0, num)print filtered_and_squared# 1, 16, 100, 4, 9嗯,这么一来代码就会在水平方向上展开。那么是否能够继续简化代码呢?列表推导能够给我们答案:num = 1, 4, -5, 10, -

4、7, 2, 3, -1filtered_and_squared = x*2 for x in num if x 0print filtered_and_squared# 1, 16, 100, 4, 9 Python高级编程技巧迭代器(iterator)遍历输入序列num的每个成员x断言式判断每个成员是否大于零如果成员大于零,则被交给输出表达式,平方之后成为输出列表的成员。列表推导式被封装在一个列表中,所以很明显它能够立即生成一个新列表。这里只有一个type函数调用而没有隐式调用lambda函数,列表推导式正是使用了一个常规的迭代器、一个表达式和一个if表达式来控制可选的参数。另一方面,列表推

5、导也可能会有一些负面效应,那就是整个列表必须一次性加载于内存之中,这对上面举的例子而言不是问题,甚至扩大若干倍之后也都不是问题。但是总会达到极限,内存总会被用完。针对上面的问题,生成器(Generator)能够很好的解决。生成器表达式不会一次将整个列表加载到内存之中,而是生成一个生成器对象(Generator objector),所以一次只加载一个列表元素。生成器表达式同列表推导式有着几乎相同的语法结构,区别在于生成器表达式是被圆括号包围,而不是方括号:num = 1, 4, -5, 10, -7, 2, 3, -1filtered_and_squared = ( x*2 for x in n

6、um if x 0 )print filtered_and_squared# generator object at 0x00583E18for item in filtered_and_squared: print item# 1, 16, 100 4,9这比列表推导效率稍微提高一些,让我们再一次改造一下代码:num = 1, 4, -5, 10, -7, 2, 3, -1def square_generator(optional_parameter): return (x * 2 for x in num if x optional_parameter)pr

7、int square_generator(0)# generator object 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除非特殊的原因,应该经常在代码中使用生成器表达式。但除非是面对非常大的列表,否则是不会看出明显区别的。下例使用zip()函数一次处理两个或多个列表中的元素:alist = a1, a2, a3blist = 1, 2, 3for a

8、, b in zip(alist, blist): print a, b# a1 1# a2 2# a3 3再来看一个通过两阶列表推导式遍历目录的例子: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:UsersXXXDownloadsTest): print name装饰器(Decorators)装饰器为我们提供了一个增加已有函数或类的功能的有效方法。听起来是不是很像Jav

9、a中的面向切面编程(Aspect-Oriented Programming)概念?两者都很简单,并且装饰器有着更为强大的功能。举个例子,假定你希望在一个函数的入口和退出点做一些特别的操作(比如一些安全、追踪以及锁定等操作)就可以使用装饰器。装饰器是一个包装了另一个函数的特殊函数:主函数被调用,并且其返回值将会被传给装饰器,接下来装饰器将返回一个包装了主函数的替代函数,程序的其他部分看到的将是这个包装函数。def timethis(func): Decorator that reports the execution time. passtimethisdef countdown(n): whi

10、le n 0: n -= 1语法糖标识了装饰器。好了,让我们回到刚才的例子。我们将用装饰器做一些更典型的操作:import timefrom functools import wrapsdef 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) retur

11、n result return wrappertimethisdef countdown(n): while n 0: n -= 1countdown()# (countdown, 0.)当你写下如下代码时:timethisdef countdown(n):意味着你分开执行了以下步骤:def countdown(n):.countdown = timethis(countdown)装饰器函数中的代码创建了一个新的函数(正如此例中的wrapper函数),它用 *args 和 *kwargs 接收任意的输入参数,并且在此函数内调用原函数并且返回其结果。你可以根据自己的需要放置任何额外的代码(例如本

12、例中的计时操作),新创建的包装函数将作为结果返回并取代原函数。decoratordef function(): print(inside function)当编译器查看以上代码时,function()函数将会被编译,并且函数返回对象将会被传给装饰器代码,装饰器将会在做完相关操作之后用一个新的函数对象代替原函数。装饰器代码是什么样的?大部分的例子都是将装饰器定义为函数,而我发觉将装饰器定义成类更容易理解其功能,并且这样更能发挥装饰器机制的威力。对装饰器的类实现唯一要求是它必须能如函数一般使用,也就是说它必须是可调用的。所以,如果想这么做这个类必须实现_call_方法。这样的装饰器应该用来做些什么

13、?它可以做任何事,但通常它用在当你想在一些特殊的地方使用原函数时,但这不是必须的,例如: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 func

14、tion()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 utdrgirtk function()”3. 随后执行“print(“Finishe

15、d decorating function()”)”4. 最后在调用function函数时,由于使用装饰器包装,因此执行decorator的_call_打印 “inside decorator._call_()”。一个更实际的例子:def decorator(func): def modify(*args, *kwargs): variable = kwargs.pop(variable, None) print variable x,y=func(*args, *kwargs) return x,y return modifydecoratordef func(a,b): print a*2

16、,b*2 return a*2,b*2func(a=4, b=5, variable=hi)func(a=4, b=5)# hi# 16 25# None# 16 25上下文管理库(ContextLib)contextlib模块包含了与上下文管理器和with声明相关的工具。通常如果你想写一个上下文管理器,则你需要定义一个类包含_enter_方法以及_exit_方法,例如:import timeclass demo: def _init_(self, label): self.label = label def _enter_(self): self.start = time.time() de

17、f _exit_(self, exc_ty, exc_val, exc_tb): end = time.time() print(: .format(self.label, end - self.start)完整的例子在此: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(

18、self.label, end - self.start)with demo(counting): n = while n 0: n -= 1# counting: 1.上下文管理器被with声明所激活,这个API涉及到两个方法。1. _enter_方法,当执行流进入with代码块时,_enter_方法将执行。并且它将返回一个可供上下文使用的对象。2. 当执行流离开with代码块时,_exit_方法被调用,它将清理被使用的资源。利用contextmanager装饰器改写上面那个例子:from contextlib import contextmanagerimport timecontextm

19、anagerdef demo(label): start = time.time() try: yield finally: end = time.time() print(: .format(label, end - start)with demo(counting): n = while n 0: n -= 1# counting: 1.看上面这个例子,函数中yield之前的所有代码都类似于上下文管理器中_enter_方法的内容。而yield之后的所有代码都如_exit_方法的内容。如果执行过程中发生了异常,则会在yield语句触发。描述器(Descriptors)描述器决定了对象属性是如

20、何被访问的。描述器的作用是定制当你想引用一个属性时所发生的操作。构建描述器的方法是至少定义以下三个方法中的一个。需要注意,下文中的instance是包含被访问属性的对象实例,而owner则是被描述器修辞的类。_get_(self, instance, owner) 这个方法是当属性被通过(value = obj.attr)的方式获取时调用,这个方法的返回值将被赋给请求此属性值的代码部分。_set_(self, instance, value) 这个方法是当希望设置属性的值(obj.attr = value)时被调用,该方法不会返回任何值。_delete_(self, instance) 当从一

21、个对象中删除一个属性时(del obj.attr),调用此方法。译者注:对于instance和owner的理解,考虑以下代码:class Celsius(object): 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 Temperature(object): celsius = Celsius()te

22、mp=Temperature()temp.celsius #calls Celsius._get_上例中,instance指的是temp,而owner则是Temperature。LazyLoading Properties例子:import weakrefclass 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.dataobj = self

23、.f(obj) return self.dataobjclass Foo(object): lazyattribute def bar(self): print Being lazy return 42f = Foo()print f.bar# Being lazy# 42print f.bar# 42描述器很好的总结了Python中的绑定方法(bound method)这个概念,绑定方法是经典类(classic classes)的实现核心。在经典类中,当在一个对象实例的字典中没有找到某个属性时,会继续到类的字典中查找,然后再到基类的字典中,就这么一直递归的查找下去。如果在类字典中找到这个属性

24、,解释器会检查找到的对象是不是一个Python函数对象。如果是,则返回的并不是这个对象本身,而是返回一个柯里化(currying function)的包装器对象。当调用这个包装器时,它会首先在参数列表之前插入实例,然后再调用原函数。译者注:1. 柯里化 /wiki/%E6%9F%AF%E9%87%8C%E5%8C%962. function,method,bound method及unbound method的区别。首先,函数(function)是由def或lambda创建的。当一个函数在class语句块中定义或是由type来创建时,它会转成一个非绑

25、定方法(unbound method),而当通过类实例(instance)来访问此方法的时候,它将转成绑定方法(bound method),绑定方法会自动将实例作为第一个参数传入方法。综上所述,方法是出现在类中的函数,绑定方法是一个绑定了具体实例的方法,反之则是非绑定方法。综上,描述器被赋值给类,而这些特殊的方法就在属性被访问的时候根据具体的访问类型自动地调用。元类(MetaClasses)元类提供了一个改变Python类行为的有效方式。元类的定义是“一个类的类”。任何实例是它自己的类都是元类。class demo(object): passobj = demo()print Class of obj is 0.format(obj._class_)print Class of obj is 0.format(demo._class_)# Class of obj is # Class of obj is 在上例中,我们定义了一个类demo,并且生成了一个该类的对象obj。首先,可以看到

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论