Python高级编程技术.docx
- 文档编号:11936307
- 上传时间:2023-04-16
- 格式:DOCX
- 页数:14
- 大小:20.42KB
Python高级编程技术.docx
《Python高级编程技术.docx》由会员分享,可在线阅读,更多相关《Python高级编程技术.docx(14页珍藏版)》请在冰豆网上搜索。
Python高级编程技术
Python高级编程技术
本文展现一些高级的Python设计结构和它们的利用方式。
在日常工作中,你能够依照需要选择适合的数据结构,例如对快速查找性的要求、对数据一致性的要求或是对索引的要求等,同时也能够将各类数据结构适合地结合在一路,从而生成具有逻辑性并易于明白得的数据模型。
Python的数据结构从句法上来看超级直观,而且提供了大量的可选操作。
这篇指南尝试将大部份经常使用的数据结构知识放到一路,而且提供对其最正确用法的探讨。
推导式(Comprehensions)
若是你已经利用了很长时刻的Python,那么你至少应该听说过列表推导(listcomprehensions)。
这是一种将for循环、if表达式和赋值语句放到单一语句中的一种方式。
换句话说,你能够通过一个表达式对一个列表做映射或过滤操作。
一个列表推导式包括以下几个部份:
一个输入序列
一个表示输入序列成员的变量
一个可选的断言表达式
一个将输入序列中知足断言表达式的成员变换成输出列表成员的输出表达式
举个例子,咱们需要从一个输入列表
num=[1,4,-5,10,-7,2,3,-1]
filtered_and_squared=[]
fornumberinnum:
ifnumber>0:
(number**2)
printfiltered_and_squared
#[1,16,100,4,9]
很简单是吧?
可是这就会有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]
嗯,这么一来代码就会在水平方向上展开。
那么是不是能够继续简化代码呢?
列表推导能够给咱们答案:
num=[1,4,-5,10,-7,2,3,-1]
filtered_and_squared=[x**2forxinnumifx>0]
printfiltered_and_squared
#[1,16,100,4,9]
Python高级编程技术
迭代器(iterator)遍历输入序列num的每一个成员x
断言式判定每一个成员是不是大于零
若是成员大于零,那么被交给输出表达式,平方以后成为输出列表的成员。
列表推导式被封装在一个列表中,因此很明显它能够当即生成一个新列表。
那个地址只有一个type函数挪用而没有隐式挪用lambda函数,列表推导式正是利用了一个常规的迭代器、一个表达式和一个if表达式来操纵可选的参数。
另一方面,列表推导也可能会有一些负面效应,那确实是整个列表必需一次性加载于内存当中,这对上面举的例子而言不是问题,乃至扩大假设干倍以后也都不是问题。
可是总会达到极限,内存总会被用完。
针对上面的问题,生成器(Generator)能够专门好的解决。
生成器表达式可不能一次将整个列表加载到内存当中,而是生成一个生成器对象(Generatorobjector),因此一次只加载一个列表元素。
生成器表达式同列表推导式有着几乎相同的语法结构,区别在于生成器表达式是被圆括号包围,而不是方括号:
num=[1,4,-5,10,-7,2,3,-1]
filtered_and_squared=(x**2forxinnumifx>0)
printfiltered_and_squared
#
foriteminfiltered_and_squared:
printitem
#1,16,1004,9
这比列表推导效率略微提高一些,让咱们再一次改造一下代码:
num=[1,4,-5,10,-7,2,3,-1]
defsquare_generator(optional_parameter):
return(x**2forxinnumifx>optional_parameter)
printsquare_generator(0)
#
#OptionI
forkinsquare_generator(0):
printk
#1,16,100,4,9
#OptionII
g=list(square_generator(0))
printg
#[1,16,100,4,9]
除非特殊的缘故,应该常常在代码中利用生成器表达式。
但除非是面对超级大的列表,不然是可不能看出明显区别的。
下例利用zip()函数一次处置两个或多个列表中的元素:
alist=['a1','a2','a3']
blist=['1','2','3']
fora,binzip(alist,blist):
printa,b
#a11
#a22
#a33
再来看一个通过两阶列表推导式遍历目录的例子:
importos
deftree(top):
forpath,names,fnamesin(top):
forfnameinfnames:
yieldfname)
fornameintree('C:
\Users\XXX\Downloads\Test'):
printname
装饰器(Decorators)
装饰器为咱们提供了一个增加已有函数或类的功能的有效方式。
听起来是不是很像Java中的面向切面编程(Aspect-OrientedProgramming)概念?
二者都很简单,而且装饰器有着更为壮大的功能。
举个例子,假定你希望在一个函数的入口和退出点做一些专门的操作(比如一些平安、追踪和锁定等操作)就能够够利用装饰器。
装饰器是一个包装了另一个函数的特殊函数:
主函数被挪用,而且其返回值将会被传给装饰器,接下来装饰器将返回一个包装了主函数的替代函数,程序的其他部份看到的将是那个包装函数。
deftimethis(func):
'''
Decoratorthatreportstheexecutiontime.
'''
pass
@timethis
defcountdown(n):
whilen>0:
n-=1
语法糖@标识了装饰器。
好了,让咱们回到适才的例子。
咱们将用装饰器做一些更典型的操作:
importtime
fromfunctoolsimportwraps
deftimethis(func):
'''
Decoratorthatreportstheexecutiontime.
'''
@wraps(func)
defwrapper(*args,**kwargs):
start=()
result=func(*args,**kwargs)
end=()
print,end-start)
returnresult
returnwrapper
@timethis
defcountdown(n):
whilen>0:
n-=1
countdown(100000)
#('countdown',
当你写下如下代码时:
@timethis
defcountdown(n):
意味着你分开执行了以下步骤:
defcountdown(n):
...
countdown=timethis(countdown)
装饰器函数中的代码创建了一个新的函数(正如此例中的wrapper函数),它用*args和**kwargs接收任意的输入参数,而且在此函数内挪用原函数而且返回其结果。
你能够依照自己的需要放置任何额外的代码(例如本例中的计时操作),新创建的包装函数将作为结果返回并取代原函数。
@decorator
deffunction():
print("insidefunction")
当编译器查看以上代码时,function()函数将会被编译,而且函数返回对象将会被传给装饰器代码,装饰器将会在做完相关操作以后用一个新的函数对象代替原函数。
装饰器代码是什么样的?
大部份的例子都是将装饰器概念为函数,而我觉察将装饰器概念成类更易明白得其功能,而且如此更能发挥装饰器机制的威力。
对装饰器的类实现唯一要求是它必需能如函数一样利用,也确实是说它必需是可挪用的。
因此,若是想这么做那个类必需实现__call__方式。
如此的装饰器应该用来做些什么?
它能够做任何事,但通常它用在当你想在一些特殊的地址利用原函数时,但这不是必需的,例如:
classdecorator(object):
def__init__(self,f):
print("inside()")
f()#Provethatfunctiondefinitionhascompleted
def__call__(self):
print("inside()")
@decorator
deffunction():
print("insidefunction()")
print("Finisheddecoratingfunction()")
function()
#inside()
#insidefunction()
#Finisheddecoratingfunction()
#inside()
译者注:
1.语法糖@decorator相当于function=decorator(function),在此挪用decorator的__init__打印“inside()”
2.随后执行f()打印“insideutdrgirtkfunction()”
3.随后执行“print(“Finisheddecoratingfunction()”)”
4.最后在挪用function函数时,由于利用装饰器包装,因此执行decorator的__call__打印“inside()”。
一个更实际的例子:
defdecorator(func):
defmodify(*args,**kwargs):
variable=('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
#1625
#None
#1625
上下文治理库(ContextLib)
contextlib模块包括了与上下文治理器和with声明相关的工具。
通常若是你想写一个上下文治理器,那么你需要概念一个类包括__enter__方式和__exit__方式,例如:
importtime
classdemo:
def__init__(self,label):
=label
def__enter__(self):
=()
def__exit__(self,exc_ty,exc_val,exc_tb):
end=()
print('{}:
{}'.format,end-)
完整的例子在此:
importtime
classdemo:
def__init__(self,label):
=label
def__enter__(self):
=()
def__exit__(self,exc_ty,exc_val,exc_tb):
end=()
print('{}:
{}'.format,end-)
withdemo('counting'):
n=
whilen>0:
n-=1
#counting:
上下文治理器被with声明所激活,那个API涉及到两个方式。
1.__enter__方式,当执行流进入with代码块时,__enter__
方式将执行。
而且它将返回一个可供上下文利用的对象。
2.当执行流离开with代码块时,__exit__方式被挪用,它将清理被利用的资源。
利用@contextmanager装饰器改写上面那个例子:
fromcontextlibimportcontextmanager
importtime
@contextmanager
defdemo(label):
start=()
try:
yield
finally:
end=()
print('{}:
{}'.format(label,end-start))
withdemo('counting'):
n=
whilen>0:
n-=1
#counting:
看上面那个例子,函数中yield之前的所有代码都类似于上下文治理器中__enter__方式的内容。
而yield以后的所有代码都如__exit__方式的内容。
若是执行进程中发生了异样,那么会在yield语句触发。
描述器(Descriptors)
描述器决定了对象属性是如何被访问的。
描述器的作用是定制当你想引用一个属性时所发生的操作。
构建描述器的方式是至少概念以下三个方式中的一个。
需要注意,下文中的instance是包括被访问属性的对象实例,而owner那么是被描述器修辞的类。
__get__(self,instance,owner)–那个方式是当属性被通过(value=的方式获取时挪用,那个方式的返回值将被赋给请求此属性值的代码部份。
__set__(self,instance,value)–那个方式是当希望设置属性的值=‘value’)时被挪用,该方式可不能返回任何值。
__delete__(self,instance)–当从一个对象中删除一个属性时(del,挪用此方式。
译者注:
关于instance和owner的明白得,考虑以下代码:
classCelsius(object):
def__init__(self,value=:
=float(value)
def__get__(self,instance,owner):
return
def__set__(self,instance,value):
=float(value)
classTemperature(object):
celsius=Celsius()
temp=Temperature()
#calls
上例中,instance指的是temp,而owner那么是Temperature。
LazyLoadingProperties例子:
importweakref
classlazyattribute(object):
def__init__(self,f):
=()
=f
def__get__(self,obj,cls):
ifobjnotin:
[obj]=(obj)
return[obj]
classFoo(object):
@lazyattribute
defbar(self):
print"Beinglazy"
return42
f=Foo()
#Beinglazy
#42
#42
描述器专门好的总结了Python中的绑定方式(boundmethod)那个概念,绑定方式是经典类
(classicclasses)的实现核心。
在经典类中,当在一个对象实例的字典中没有找到某个属性时,会继续到类的字典中查找,然后再到基类的字典中,就这么一直递归的查找下去。
若是在类字典中找到那个属性,说明器会检查找到的对象是不是一个Python函数对象。
若是是,那么返回的并非是那个对象本身,而是返回一个柯里化(curryingfunction)的包装器对象。
当挪用那个包装器时,它会第一在参数列表之前插入实例,然后再挪用原函数。
译者注:
1.柯里化–function,method,boundmethod及unboundmethod的区别。
第一,函数(function)是由def或lambda创建的。
当一个函数在class语句块中概念或是由type来创建时,它会转成一个非绑定方式(unboundmethod),而当通过类实例(instance)来访问此方式的时候,它将转成绑定方式(boundmethod),绑定方式会自动将实例作为第一个参数传入方式。
综上所述,方式是出此刻类中的函数,绑定方式是一个绑定了具体实例的方式,反之那么是非绑定方式。
综上,描述器被赋值给类,而这些特殊的方式就在属性被访问的时候依照具体的访问类型自动地挪用。
元类(MetaClasses)
元类提供了一个改变Python类行为的有效方式。
元类的概念是“一个类的类”。
任何实例是它自己的类都是元类。
classdemo(object):
pass
obj=demo()
print"Classofobjis{0}".format
print"Classofobjis{0}".format
#Classofobjis
#Classofobjis
在上例中,咱们概念了一个类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的测试。
观看者
观看者模式许诺多个对象访问同一份数据。
构造函数
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Python 高级 编程 技术