在开始本节之前,首先介绍下偏函数partial。首先借助help来看下partial的定义

首先来说下第一行解释的意思:

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来实现,对比一下二者的区别

(一)装饰器 decorator 实现

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__)

sum最后返回的值应该是10+20+15 = 45,这样一来,我们的decorator就实现了我们想要的扩展功能,最后,发现,原函数sum的name属性,仍然是sum,说明,这种装饰扩展功能,不影响我们的原函数:

(二)偏函数 partial function 实现

A:普通函数可变参数顺序执行

  1. def sum(*args):
  2. s = 0
  3. for n in args:
  4. s = s + n
  5. return s
  6. print(sum(10,20)+sum(1,2,3,4,5))

我们如果想实现+10+20的效果,必须写两遍sum,这样写,显然是最易懂的,但是,却显得很邋遢

B:普通函数可变参数加关键字参数组合

def sum(*args,**others):

s = 0

for n in args:

s = s + n

s1 = 0

for k in others:

s1 = s1 + others[k] #我们还要算一下,关键字参数里蕴藏的求和结果,k是dict中的关键字key

return s+s1 #最终,我们实现扩展功能,顺序参数和关键字参数结果相加

D= {'value1':10,'value2':20}

print(sum(1,2,3,4,5,**D))

代码看起来,是显得专业了,但是感觉冗余,没必要

C:偏函数可变参数顺序填充一步到位

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

可以看出,我们针对sum函数的求和结果,再加上10,或者加10加20,甚至加更多,都是可以通过偏函数来实现的,注意偏函数的第二部分,参数是可变的,是按顺序走的,因此,偏函数产生的新函数,sum_add_10 实际上等同于sum(10,*args):

下面来看可自定义属性的装饰器

from functools import wraps,partial

import logging

def attach_wrapper(obj,func=None):

if func is None:

return partial(attach_wrapper,obj)

setattr(obj,func.__name__,func)

return func

def logged(level,name=None,message=None):

def decorate(func):

logname=name if name else func.__module__

log=logging.getLogger(logname)

logmsg=message if message else func.__name__

@wraps(func)

def wrapper(*args,**kwargs):

log.log(level,logmsg)

return func(*args,**kwargs)

@attach_wrapper(wrapper)

def set_level(newlevel):

nonlocal level

level=newlevel

@attach_wrapper(wrapper)

def set_message(newmsg):

nonlocal logmsg

logmsg=newmsg

return wrapper

return decorate

@logged(logging.DEBUG)

def add(x,y):

return x+y

@logged(logging.CRITICAL,'example')

def spam():

print("Spam!")

首先用attach_wrapper来装饰set_level以及set_message。在调用的时候首先是attach_wrapper(wrapper)(set_level)。初次调用的时候由于func为空,因此调用partial(attach_wrapper,obj),将obj也就是wrapper函数作为第一个参数,并返回一个新的attach_wrapper函数,第一个参数是wrapper函数实例,再次调用的时候就变成attach_wrapper(wrapper,set_level). 在这次调用中set_level被设置为wrapper的属性。setattr(obj,func.__name__,func)也就等于wrapper.set_level=set_level

通过这种方式将set_message和set_level设置成了wrapper的方法。

在set_message和set_level中引用了nonlocal来修改内部变量。这样就可以控制logging的等级以及输出信息。

if __name__=="__main__":

logging.basicConfig(level=logging.DEBUG)

add(2,3)

add.set_message('Add called')

add(2,3)

add.set_level(logging.WARNING)

add(2,3)

运行结果如下:

DEBUG:__main__:add

DEBUG:__main__:Add called

WARNING:__main__:Add called

python cookbook第三版学习笔记二十:可自定义属性的装饰器的更多相关文章

  1. python cookbook第三版学习笔记二十一:利用装饰器强制函数上的类型检查

    在演示实际代码前,先说明我们的目标:能对函数参数类型进行断言,类似下面这样: @typeassert(int, int) ... def add(x, y): ...     return x + y ...

  2. python cookbook第三版学习笔记二:字典

    一般来说字典中是一个键对应一个单值的映射,如果想一个键值映射多个值,那么就需要将这些值放到另外的容器中,比如列表或者集合. 比如d={'a':[1,2]} Collections中的defaultdi ...

  3. python cookbook第三版学习笔记十:类和对象(一)

    类和对象: 我们经常会对打印一个对象来得到对象的某些信息. class pair:     def __init__(self,x,y):         self.x=x         self. ...

  4. python cookbook第三版学习笔记九:函数

    接受任意数量参数的函数. 当传入函数的参数个数很多的时候,在函数定义的时候不需要为每一个参数定义一个变量,可以用*rest的方式来包含多余的参数. 如下面的代码,*rest包含了2,3,4这3个参数. ...

  5. python cookbook第三版学习笔记十一:类和对象(二)调用父类的方法

    在子类中调用父类的方法,可以下面的A.spam(self)的方法. class A(object):     def spam(self):         print 'A.spam' class ...

  6. python cookbook第三版学习笔记十二:类和对象(三)创建新的类或实例属性

    先介绍几个类中的应用__getattr__,__setattr__,__get__,__set__,__getattribute__,. __getattr__:当在类中找不到attribute的时候 ...

  7. python cookbook第三版学习笔记 一

    数据结构 假设有M个元素的列表,需要从中分解出N个对象,N<M,这会导致分解的值过多的异常.如下: record=['zhf','zhf@163.com','775-555-1212','847 ...

  8. python cookbook第三版学习笔记十三:类和对象(三)描述器

    __get__以及__set__:假设T是一个类,t是他的实例,d是它的一个描述器属性.读取属性的时候T.d返回的是d.__get__(None,T),t.d返回的是d.__get__(t,T).说法 ...

  9. python cookbook第三版学习笔记七:python解析csv,json,xml文件

    CSV文件读取: Csv文件格式如下:分别有2行三列. 访问代码如下: f=open(r'E:\py_prj\test.csv','rb') f_csv=csv.reader(f) for f in ...

随机推荐

  1. Centos RSA 登录

    创建密钥 ssh-keygen -t rsa 1.用户目录下新建.ssh/authorized_keys mkdir .ssh  #创建隐藏目录 #修改文件访问权限 chmod 700 .ssh cd ...

  2. rocketmq发送消息代码

    DefaultMQProducer defaultMQProducer = new DefaultMQProducer(); defaultMQProducer.setProducerGroup(Co ...

  3. Atitit.变量的定义 获取 储存 物理结构 基本类型简化 隐式转换 类型推导 与底层原理 attilaxDSL

    Atitit.变量的定义 获取 储存 物理结构 基本类型简化 隐式转换 类型推导 与底层原理 attilaxDSL 1.1. $ 美元字符, php 黑头1 1.2. 默认变量的范围和声明:1 1.3 ...

  4. nginx正则说明

    nginx正则说明 分类: nginx -- : 11758人阅读 评论() 收藏 举报 nginx正则表达式firefox ^~ 标识符后面跟一个字符串.Nginx将在这个字符串匹配后停止进行正则表 ...

  5. c++ [wrong]simple "Garbage Collector"

    In fact, Ptr alone can accomplish the task mentioned below. Implementation see Ptr.h, main2.cpp. In ...

  6. python基础-初识Python和不同语言之间的区别

    一.Python的创始人谁? Python之父:吉多·范罗苏姆GuidovanRossum 吉多·范罗苏姆是一名荷兰计算机程序员,他作为Python程序设计语言的作者而为人们熟知.在Python社区, ...

  7. Ordering Tasks UVA - 10305 图的拓扑排序

    John has n tasks to do. Unfortunately, the tasks are not independent and the execution of one task i ...

  8. linux USR1亦通常被用来告知应用程序重载配置文件

    linux kill 命令 以及 USR1 信号 解释 原创 2016年03月16日 16:48:27 标签:linux kill -USR1 5325 最近 在做 服务器搭建的一些工作,其中 用到了 ...

  9. C#通过修改注册表改变IE默认选项

    修改注册表,这个代码好实现,关键是怎么找到对应的注册表值,也就是说画一条线很容易,难的是找到要在哪里画,然后我百度了一圈,出来的都是画线的,没有指出或者指出的不全的注册表对应值,只能FQ谷歌了,也就有 ...

  10. linux 个人测试用例

    1. 我想在某个目录下, 找到某个文件中有某个字符(leon)的文件, 并列出来? (如果是在windows下, 可能需要一个文件一个文件的看, 但是在 linux 下可以实现) find . –ma ...