最近项目忙成狗,都没时间好好看看开发的东西了,正好最近开始看rest api的东西,真是个好东西啊。可以前后端分离,但是在学习的过程中,遇到各种问题。还是基础不够扎实。本次rest api的会一边遇坑一边补习之前的东西。
一 functools库
真是一个神奇的python库,以前一直没注意。
1 偏函数 partial
先找文档学习一下,最后看源码,源码能看懂就看,看不懂那就是真的看不懂了。
文档参考 https://blog.csdn.net/appleyk/article/details/77609114
partial 一共有三个部分:
(1)第一部分也就是第一个参数,是一个函数,这个函数可以是你定义的,也可以是Python内置函数
(2)第二部分是一个可变参数,*args,比如内置函数max的参数就是一个可变参数,max(1,2,3,4,5)=5
(3)第三部分是一个关键字参数,比如内置函数int的第二个参数就是命名关键字参数,默认base=10,表示int转换时默认是10进制的:
partial函数的作用就是:将所作用的函数作为partial()函数的第一个参数,原函数的各个参数依次作为partial()函数的后续参数,原函数有关键字参数的一定要带上关键字,没有的话,按原有参数顺序进行补充。
使用一下吧
A、偏函数的第二个部分(可变参数),按原有函数的参数顺序进行补充,参数将作用在原函数上,最后偏函数返回一个新函数(类似于,装饰器decorator,对于函数进行二次包装,产生特殊效果;但又不同于装饰器,偏函数产生了一个新函数,而装饰器,可改变被装饰函数的函数入口地址也可以不影响原函数)
这里我的理解是:首先偏函数能做的事情,装饰器也能做,其次偏函数可以操作源函数的参数,可是装饰器可以操作函数的入口。那为啥还有偏函数,估计是因为比装饰器简单吧。用起来应该很爽。
案例:我们定义一个sum函数,参数为*args可变,计算这些可变参数的和。
扩展:我们想要对sum函数求和后的结果,再加上10加上20甚至加更多,得到一个新的结果。
实现:我们分别用decorator和partial来实现,对比一下二者的区别。
好激动,因为可以紧跟作者脚步可以复习一下装饰器的东西了。
(1)装饰器实现
基础:http://www.cnblogs.com/wanstack/p/9009932.html
# /usr/bin/env Python3
# -*- encoding:UTF-8 -*- from functools import wraps def sum_add(*args1): #我们要给我们的装饰器decorator,带上参数
def decorator(func):
@wraps(func) #加上这句,原函数func被decorator作用后,函数性质不变
def my_sum(*args2): #注意,参数要和原函数保持一致,真正实行扩展功能的是外层的装饰器
my_s = 0
for n in args1:
my_s = my_s +n #这个是我们新加的求和结果
return func(*args2) + my_s #这个,我们在原求和函数的结果上再加上s,并返回这个值
return my_sum #返回my_sum函数,该函数扩展原函数的功能
return decorator #返回我们的装饰器 @sum_add(10,20) #启用装饰器 对sum函数进行功能扩展
def sum(*args):
s = 0
for n in args:
s = s+n
return s
print(sum(1,2,3,4,5))
print(sum.__name__)
这里以前一直有一个疑问,今天突然间理解了,以前一直觉得为啥装饰器带上参数就需要写三层嵌套函数,因为@decorator没有()所以是不需要执行的,而加上参数@decorator(*args)是已经开始执行了啊。尼玛就是这么简单,当时困扰了好久。归根结底还是不理解。有时候想想做技术真的是理解了就是理解了,不理解死记硬背也不理解。
看看偏函数的实现:
# /usr/bin/env Python3
# -*- encoding:UTF-8 -*-
from functools import partial def sum(*args):
s = 0
for n in args:
s = s + n
return s sum_add_10 = partial(sum,10) #10 作用在sum第一个参数的位置
sum_add_10_20 = partial(sum,10,20) #10 20 分别作用在sum第一个和第二个参数的位置
print('A____________我们看下原函数sum的函数地址入口:')
print(sum)
print('B______我们看下partial函数返回函数的地址入口:')
print(partial(sum,10))
print(sum_add_10(1,2,3,4,5)) # --> 10 + 1 + 2 + 3 + 4 + 5 = 25
print(sum_add_10_20(1,2,3,4,5)) # --> 10 + 20 + 1 + 2 + 3 + 4 + 5 = 45
一步到位,当然还有其他方式一步到位的写法。这里的partial(函数,函数的第一个参数位置) 这部就是可以对已经存在的函数的参数中,继续传递参数吗。
B、偏函数的第三个部分(关键字参数),按原有函数的关键字参数进行填补,参数将作用在原函数上,最后偏函数返回一个新函数
案例:我们定义一个mod求余函数,两个参数,一个是被除数,一个是除数,除数我们这里用命名关键字参数表示,默认值2
扩展:我们的除数不固定,可以是对2就行求余,也可以对3,对4,总之我们需要指定除数的值
返回结果: True 或 False
实现:原函数实现和partial函数实现
# /usr/bin/env Python3
# -*- encoding:UTF-8 -*-
import functools
def mod(m,*,key=2):
return m % key == 0
mod_to_2 = functools.partial(mod,key=2)
print('A__3___使用原函数的默认关键字参数对2进行求余:')
print(mod(3)) #对2进行求余-- 原函数 使用默认参数
print('B__3___使用偏函数对2进行求余:')
print(mod_to_2(3)) #对2进行求余-- 新函数 --偏函数产生
mod_to_5 = functools.partial(mod,key=5)
print('C__25___使用原函数的关键字参数对5进行求余:')
print(mod(25,key=5)) #对5进行求余 -- 原函数
print('D__25___使用偏函数对5进行求余:')
print(mod_to_5(25)) #对5进行求余 -- 新函数--偏函数产生
2 wraps
其实上面已经用到了wraps,一般在使用装饰器的时候会使用这个,他也是一个装饰器,至于为什么会用他,因为我们在使用装饰器的时候,会损失一些基本信息,比如fun.__name__等等,用上他,就不会了。
啥也不说了,直接上代码。
# coding:utf-8
from functools import partial WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__',
'__annotations__')
WRAPPER_UPDATES = ('__dict__',)
def update_wrapper(wrapper,
wrapped,
assigned = WRAPPER_ASSIGNMENTS,
updated = WRAPPER_UPDATES):
"""Update a wrapper function to look like the wrapped function wrapper is the function to be updated
wrapped is the original function
assigned is a tuple naming the attributes assigned directly
from the wrapped function to the wrapper function (defaults to
functools.WRAPPER_ASSIGNMENTS)
updated is a tuple naming the attributes of the wrapper that
are updated with the corresponding attribute from the wrapped
function (defaults to functools.WRAPPER_UPDATES)
"""
"""
try:
<语句>
except <name>:
<语句> #如果在try部份引发了名为'name'的异常,则执行这段代码
else:
<语句> #如果没有异常发生,则执行这段代码
finally:
print("不管有没有异常,我总是被执行了")
"""
print("wrapper==========" ,wrapper) # 这里的wrapper是一个inner函数
for attr in assigned:
try:
# 这里的wrapped应该是被装饰的函数,从wrapped中获取assigned的属性
value = getattr(wrapped, attr)
print("value============",value)
except AttributeError:
pass
else:
# 这里try执行了,这里就把value的值赋值给wrapper对象
setattr(wrapper, attr, value)
v = getattr(wrapper,attr)
print('v------',v)
for attr in updated:
print("getattr(wrapper, attr)",getattr(wrapper, attr)) # 这是个字典
getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
# Issue #17482: set __wrapped__ last so we don't inadvertently copy it
# from the wrapped function when updating __dict__
wrapper.__wrapped__ = wrapped
# Return the wrapper so this can be used as a decorator via partial()
return wrapper # 这个wrapper是一个inner def wraps(wrapped,
assigned = WRAPPER_ASSIGNMENTS,
updated = WRAPPER_UPDATES):
"""Decorator factory to apply update_wrapper() to a wrapper function Returns a decorator that invokes update_wrapper() with the decorated
function as the wrapper argument and the arguments to wraps() as the
remaining arguments. Default arguments are as for update_wrapper().
This is a convenience function to simplify applying partial() to
update_wrapper().
"""
# 其中update_wrapper是一个函数,wtrapped,assigned,updated是这个函数的关键字参数
# 这里执行update_wrapper函数,返回一个wrapper
return partial(update_wrapper, wrapped=wrapped,
assigned=assigned, updated=updated) def decorator(func):
"""
1. value = getattr(wrapped, attr) 从func中拿到一些属性信息
2. setattr(wrapper, attr, value) 把这些属性信息赋值给inner
3. return wrapper 返回inner
:param func:
:return:
"""
@wraps(func) # func = wraps(func)
def inner():
print('起床先睁眼')
func()
return inner @decorator # func = decorator(func)
def func():
print('早上起床') func() # 执行inner()
print(func.__name__)
print(func.__dict__)
可能看着有点乱,但是只要就是
1. value = getattr(wrapped, attr) 从func中拿到一些属性信息
2. setattr(wrapper, attr, value) 把这些属性信息赋值给inner
3. return wrapper 返回inner
做了这三件事情,当然了__dict__中的我没写,但是大概就是这么个流程,现在知道了即使,我们返回的是inner函数,也会对应的是func的信息。中间通过反射进行取值和赋值的操作。