掌握这些知识,你的Python水平能更上一层楼

发表于:2018-3-14 09:38

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:卿哥聊技术    来源:博客园

  今天讲一些python中的高级用法,有助于大家更好的使用python这门语言。今天讲的这些知识是层层递进的关系,前面是后面的铺垫。
  函数可变参数*args和**kwargs
  python支持固定参数,默认参数,也和很多其他语言一样支持可变参数,只不过python支持的可变参数分为两种,*args是tuple,里面可以有任意多个element(包括0个)。**kwargs则是当你需要指定keyword时需要用到的参数类型。
  先考虑*args的情况,先看函数定义:
>>>deftake_any_args(*args):
...print("typeofargs:"+str(type(args)))
...print("valueofargs:"+str(args))
...
>>>take_any_args(1)
typeofargs:<type'tuple'>
valueofargs:(1,)
>>>take_any_args("a","b","c")
typeofargs:<type'tuple'>
valueofargs:('a','b','c')
>>>take_any_args()
typeofargs:<type'tuple'>
valueofargs:()
>>>take_any_args(['1','2'],['3','4'])
typeofargs:<type'tuple'>
valueofargs:(['1','2'],['3','4'])
  再看参数提取:
defprint_args(*args):
forarginargs:
print(arg)
print_args("red","blue","green")
defprint_all(*args,**kwargs):
forarginargs:
print(arg)
forkey,valueinkwargs.items():
print("{}->{}".format(key,value))
print_all(1,2)
print_all(a="red",b="blue",c="green")
red
blue
green
1
2
a->red
c->green
b->blue
  下面看看为什么需要**kwargs,对于上面的print_args,下面这种添加了keyword的调用方式会出错,所以就有了**kwargs的用武之地:
>>>defprint_args(*args):
...forarginargs:
...printarg
...
>>>print_args(a=1,b=2)
Traceback(mostrecentcalllast):
File"<stdin>",line1,in<module>
TypeError:print_args()gotanunexpectedkeywordargument'a'
  **kwargs的本质其实是dict,如下所示:
>>>defprint_kwargs(**kwargs):
...forkey,valueinkwargs.items():
...print("{}->{}".format(key,value))
...
>>>print_kwargs(a="lalala",b="papapa")
a->lalala
b->papapa
  通常再使用的时候都是二者合起来使用,如下所示:
defprint_all(*args,**kwargs):
forarginargs:
print(arg)
forkey,valueinkwargs.items():
print("{}->{}".format(key,value))
print_all(1,2)
print_all(a="red",b="blue",c="green")
1
2
a->red
c->green
b->blue
  上面的知识大家差不多应该都知道,下面这种Unpacking的用法很多人都不太了解:
>>>defsample_function(a,b,c):
...print("{},{},{}").format(a,b,c)
...
>>>input=(1,2,3)
>>>sample_function(1,2,3)
1,2,3
#和上面的方法等效
>>>sample_function(*input)
1,2,3
  unpack使用kwargs,记住keyword要和函数声明时的变量名一致才行,否则会报错
>>>defsample_function(a,b,c):
...print("a->{},b->{},c->{}".format(a,b,c))
...
>>>input={"a":1,"b":2,"c":3}
>>>sample_function(**input)
a->1,b->2,c->3
#与上面方法等效
>>>sample_function(a=input['a'],b=input['b'],c=input['c'])
a->1,b->2,c->3
  lambdafunction
  在python中所有的东西都是object,不管是int也好,list也好都是object。函数也是object。这个概念很重要。
>>>deff(n):
...returnn+1
...
>>>id(f)
4374076184
>>>g=f
>>>printg(2)
3
>>>id(g)
4374076184
  上面的g和f所指向的object是同一个object
  下面思考这样一个问题,如果numbers=["10","3","40","14","5"],让你找出最大值怎么找?
  >>>max(numbers)
  '5'
  这显然不对,因为max默认按照字母顺序排序了,所以需要额外提供排序信息:
>>>max(numbers,key=int)
'40'
int就是一个function,然后看看如果用lambdafunction表示就是:
>>>max(numbers,key=lambdax:int(x))
'40'
  再举一个例子,下面是几个人的年龄,性别,地址,请找出年纪最大的人:
>>>person_zhangsan={'age':40,'gender':'male','home':'beijing'}
>>>person_lisi={'age':35,'gender':'male','home':'hangzhou'}
>>>person_wangwu={'age':21,'gender':'female','home':'chongqing'}
>>>people=[person_zhangsan,person_lisi,person_wangwu]
>>>max(people,key=lambdax:x['age'])
{'gender':'male','age':40,'home':'beijing'}
python在operator中提供了itemgetter这个函数,它起到的作用和lambdafunction一样,比如:
>>>fromoperatorimportitemgetter
>>>max(people,key=itemgetter("age"))
{'gender':'male','age':40,'home':'beijing'}
  对比一下我还是更喜欢lambdafunction的定义,简洁明了
  Decorator装饰器
  最长见的decorator的usercase是什么?答:retry。比如网络restfulrequest碰到不稳定的server或者说server给你返回了5XX,你要不要retry。
  一开始可能你的code长这样:
  importrequests
  URL="https://example.com/api"
  defget_items():
  returnrequests.get(URL+"/items")
  当然你还会有很多getfunction,比如get_apple,get_banana,get_orange,...
  实际部署之后发现server不稳定,不定期返回500,你就要加retry
  如果只有一个get_items,你可能会这么写:
#第二版,加入retry
defget_items():
NUM_RETRY=3
current_retry=0
resp=None
whileTrue:
resp=requests.get(URL+"/items")
ifrest.status_code/100==5andcurrent_retry<NUM_RETRY:
current_retry+=1
continue
break
returnresp
  可是每一个fucntion都要改,是不是很累。。。
  下面decorator隆重登场,decorator的本质是一个function。这个function的parameter有且仅有一个就是一个functionobject,返回值则是另一个不同的function
#比如已经有了一个普通function
defsome_function(arg1,arg2,arg3):
#此处省略20行
some_function=some_decorator(some_function)
等效于
@some_decorator
defsome_function(arg1,arg2,arg3):
#......
  下面举一个decorator的例子,loggingdecorator
deflogfuncname(func):
defwrapper(*args,**kwargs):
print("functionname:"+func.__name__)
returnfunc(*args,**kwargs)
returnwrapper
>>>@logfuncname
...defsome_func(n):
...returnn+1
...
>>>printsome_func(3)
functionname:some_func
4
  如上所示,logfuncname就是一个decorator,它的input是func,return了一个wrapperfunction。
  下面我们回到一开始retry那个例子:
#第三版,定义decorator
defretry(func):
defwrapper(*args,**kwargs):
NUM_RETRY=3
current_retry=0
resp=None
whileTrue:
resp=func(*args,**kwargs)
ifrest.status_code/100==5andcurrent_retry<NUM_RETRY:
current_retry+=1
continue
break
returnresp
returnwrapper
@retry
defget_items():
returnrequests.get(URL+"/items")
  然后get_apple,get_banana,get_orange什么的上面加上@retry就可以了
  接下来问题来了,如果有另一个decorator也想用上怎么办?
  decorator是可以叠加的,比如下面的例子,注意上下顺序就是decorator从左到右的顺序
@add2
@multi3
deffoo(n):
returnn+1
#相当于foo=add2(multi3(foo))
#那么foo(3)就是14
@multi3
@add2
deffoo(n):
returnn+1
#相当于foo=multi3(add2(foo))
#那么foo(3)就是18
  如果想要改变retry的次数怎么办,比如get_apple想要retry3次,但是get_banana想要retry5次怎么办?
#第四版,定义带参数的decorator
defretry(num_retry):
defdecorator(func):
defwrapper(*args,**kwargs):
current_retry=0
resp=None
whileTrue:
resp=func(*args,**kwargs)
ifrest.status_code/100==5andcurrent_retry<num_retry:
current_retry+=1
continue
break
returnresp
returnwrapper
returndecorator
@retry(3)
defget_items():
returnrequests.get(URL+"/items")
  这里其实用到了一个closure的概念,就是外层函数的参数在里层函数里是可见的,而里层函数的参数在外层不可见(当然这里也不需要)。
  Decorator在flask中的实现原理
  下面我们来看看flask中decorator是怎么实现的,简而言之:
classWebApp:
def__init__(self):
#初始化routes
self.routes={}
defroute(self,param):
defdecorator(func):
#定义decorator时为routes赋值key/value
self.routes[param]=func
defwrapper(*args,**kwargs):
returnfunc(*args,**kwargs)
returnwrapper
returndecorator
defget(self,param):
try:
#get时根据key返回value
returnself.routes[param]()
exceptKeyError:
return"ERROR-nosuchpage"
>>>app=WebApp()
>>>@app.route("/")
...defindex():
...return'IndexPage'
...
>>>@app.route("/contact/")
...defcontact():
...return'ContactPage'
...
>>>app.get("/")
'IndexPage'
>>>app.get("/contact/")
'ContactPage'
>>>app.get("/no-such-page/")
'ERROR-nosuchpage'
上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号