锅炉信息网 > 锅炉知识 > 锅炉百科

工厂函数

发布时间:

普通工厂函数的实现def recoder(strname, age):n    print('name:', strname, 'age:', age)nndef cuifun(age):

普通工厂函数的实现

def recoder(strname, age):n print('name:', strname, 'age:', age)nndef cuifun(age): # 工厂函数n strname='cui'n return recoder(strname,age)nncuifun(24) # name: cui age: 24

工厂函数cuifun封装了recoder,但是这样写很麻烦,每换一个strname都要写不同的函数

闭合函数(closure)

def wrapperfun(strname): # 闭合函数,strname为自由变量n def recoder(age):n print("name:", strname, 'age:', age)n return recodernnfun = wrapperfun('cui') # fun是recoder的闭合函数nfun(24) # name: cui age: 24nnfun2 = wrapperfun('ma')nfun2(25) # name: ma age: 25nn# __closure__属性记录自由变量的参数对象地址(tuple),当闭合函数被调用时,会根据该地址找到自由变量nprint(fun.__closure__) # (<cell at 0x00000153E6C59A98: str object at 0x00000153E6D7C8F0>,)

实现装饰器

在wrapperfun函数上增加参数校验的功能:

def checkParam(fn): # 装饰器函数n def wrapper(strname):n if isinstance(strname, (str)):n return fn(strname)n print("variable strname is not a string type")n returnnn return wrappernnndef wrapperfun(strname):n def recoder(age):n print('name:', strname, 'age:', age)nn return recodernnnwrapperfun2 = checkParam(wrapperfun)nfun = wrapperfun2('cui')nfun(24) # name: cui age: 24nfun = wrapperfun2(37) # variable strname is not a string type

checkParam是一个装饰器函数,返回值是内部定义的wrapper函数。为了实现对原有函数wrapperfun的参数检查,将wrapper函数的参数和wrapperfun的参数保持一致。若被检查的参数合法,再调用原有函数,并将参数传进去;若不合法,则打印警告。

在使用时,直接将wrapperfun传入checkParam中,来完成对原函数wrapperfun的装饰,并得到带有参数检查的闭合函数wrapperfun2。

装饰器本质是一个闭合函数,该闭合函数的自由变量是一个函数。

@修饰符

def checkParam(fn):n def wrapper(strname):n if isinstance(strname, (str)):n return fn(strname)n print("variable strname is not a string type")n returnnn return wrappernn@checkParamndef wrapperfun(strname):n def recoder(age):n print('name:', strname, 'age:', age)nn return recodernnfun = wrapperfun('cui')nfun(24) # name: cui age: 24nnfun = wrapperfun(37) # variable strname is not a string type

能够接收任何参数的通用参数修饰器

之前的装饰器checkParam内部实现了一个wrapper函数,wrapper函数的参数与被装饰函数参数一样,这样才能实现对被装饰函数的参数检查,并将输入参数传给装饰函数。

为了在定义装饰器时解耦内部wrapper的参数对被装饰函数的强依赖关系,可以使用能够适应任何参数的通用参数装饰器

def checkParam(fn): # 装饰器n def wrapper(*args, **kwargs):n if isinstance(args[0], (str)): # 对fn的第一个参数的类型进行检查n return fn(*args, **kwargs)n print("variable strname is not a string type")n returnnn return wrapper

可接收参数的通用修饰器

def isadmin(userid):n def checkParam(fn):n def wrapper(*args, **kwargs):n if userid != 'admin':n print('Operation is prohibited as you are not admin!')n returnn if isinstance(args[0], (str)):n return fn(*args, **kwargs)n print("variable strname is not a string type")n returnnn return wrappern return checkParamnn@isadmin(userid='admin')ndef wrapperfun(strname):n def recoder(age):n print('name:', strname, 'age:', age)nn return recodernn@isadmin(userid='user')ndef wrapperfun2(strname):n def recoder(age):n print('name:', strname, 'age:', age)nn return recodernnnfun = wrapperfun('cui') # 此时的fun是strname初始化后的recoder nfun(24) # name: cui age: 24nnfun2 = wrapperfun(37) # variable strname is not a string typenfun3 = wrapperfun2(37) # Operation is prohibited as you are not admin!nnn# 下面不用@写一个普通装饰器ndef wrapperfun3(strname):n def recoder(age):n print('name:', strname, 'age:', age)nn return recodernn# 执行isadmin(userid='user')返回一个装饰器checkParam,n# 再调用checkParam,传入参数wrapperfun3,返回wrapper函数nwrapperfun3 = isadmin(userid='admin')(wrapperfun3)nprint(wrapperfun3.__name__) # wrappernfun4=wrapperfun3('cui')nfun4(24)

注意:使用@修饰符使调用时节省了不少代码,可以直接调用被修饰函数了。

装饰器返回函数的名称修复

def isadmin(userid):n def checkParam(fn):n def wrapper(*args, **kwargs):n if userid != 'admin':n print('Operation is prohibited as you are not admin!')n returnn if isinstance(args[0], (str)):n return fn(*args, **kwargs)n print("variable strname is not a string type")n returnn wrapper.__name__ = fn.__name__ # 修改wrapper的namen return wrappern return checkParamnnnndef wrapperfun(strname):n def recoder(age):n print('name:', strname, 'age:', age)nn return recodernnwrapperfun = isadmin(userid='admin')(wrapperfun)nprint(wrapperfun.__name__) # wrapperfun

也可以通过内置装饰器functools.wraps来实现此功能

import functoolsndef isadmin(userid):n def checkParam(fn):n @functools.wraps(fn) # wrapper的函数名称会变成与传入的fn参数一样的函数名称n def wrapper(*args, **kwargs):n if userid != 'admin':n print('Operation is prohibited as you are not admin!')n returnn if isinstance(args[0], (str)):n return fn(*args, **kwargs)n print("variable strname is not a string type")n returnnn return wrappern return checkParamnnnndef wrapperfun(strname):n def recoder(age):n print('name:', strname, 'age:', age)nn return recodernnwrapperfun = isadmin(userid='admin')(wrapperfun)nprint(wrapperfun.__name__) # wrapperfun

组合装饰器

def checkParam(fn):n def wrapper(*args, **kwargs):n if isinstance(args[0], (str)):n return fn(*args, **kwargs)n print("variable strname is not a string type")n returnn return wrappernndef logging(userid):n def checkParam(fn):n def wrapper(*args, **kwargs):n print(userid,end=':')n return fn(*args, **kwargs)n return wrappern return checkParamnn@logging(userid='admin')n@checkParamndef wrapperfun(strname):n def recoder(age):n print('name:',strname,'age:',age)n return recodernnfun = wrapperfun('cui')nfun(24) # admin:name: cui age: 24

使用装饰器,可以将代码按照要实现功能的主次逐层分开,这就是面向切面的编程思想(Aspect Oriented Program AOP)

多装饰器的调用顺序

def logging(fn):n print("in logging")n def wrapper_logging(*args, **kwargs):n print("in wrapper_logging")n return fn(*args, **kwargs)n return wrapper_loggingnndef checkParam(fn):n print("in checkParam")n def wrapper_checkParam(*args, **kwargs):n print('in wrapper_checkParam')n return fn(*args, **kwargs)n return wrapper_checkParamnn@loggingn@checkParamndef wrapperfun(strname): # 输出:in checkParam n in loggingn print('name:',strname)nnwrapperfun('cui') # 输出:in wrapper_logging n in wrapper_checkParam n name:cui

如果不使用@修饰器,代码执行顺序可以看的很清楚:

def wrapperfun(strname):n print('name:',strname)nnnc = checkParam(wrapperfun) # in checkParamnprint(c.__name__) # wrapper_checkParamnc2 = logging(c) # in loggingnprint(c2.__name__) # wrapper_loggingnc2('cui') # in wrapper_logging n in wrapper_checkParam n name:cui

解决‘同作用域下默认参数被覆盖’的问题

def recoder(strname, age):n print('name:',strname,'age:',age)nndef makerecoders():n acts = []n for i in ["cui","ma"]:n acts.append(lambda age:recoder(i, age))n return actsnnfor a in (makerecoders()):n a(age=32) # name: ma age: 32 n name: ma age: 32

上面的例子中,通过for循环遍历列表来批量生成工厂函数,但是只有列表的最后一个元素起到了默认值作用,前面的元素均没有生效,应改为下述写法:

def recoder(strname, age):n print('name:', strname, 'age:', age)nnndef makerecoders():n acts = []n for i in ["cui", "ma"]:n acts.append(lambda age, i=i: recoder(i, age)) # 将循环值i作为参数传入匿名函数n return actsnnnfor a in (makerecoders()):n a(age=32) # name: cui age: 32 n name: ma age: 32

精选推荐

  • 711关东煮供应商
    711关东煮供应商

    今天给大家介绍三位,奶粉,全家、罗森这些便利店里关东煮的供应商。店里卖三四块钱一串的关东煮,在网上买不到,一块钱就搞定。首先关东

  • 健康日历|高压锅容易爆炸的4个原因
    健康日历|高压锅容易爆炸的4个原因

    来源:医药养生保健报设计:李雅琴医学审核:姜峰出品人:胡丽丽

  • 高炉
    高炉

    今天这活却是个白事,等到了时辰,那家人便准备火化,本来准备送普炉,我却心中一动,便对那家人说道:“这老人走也不要省,还是送高炉吧。”

  • 高压锅和电压力锅的区别,推荐几款点压力锅
    高压锅和电压力锅的区别,推荐几款点压

    记得之前有一次去朋友家玩,他正在用高压锅煮小米粥,是的,高压锅压小米粥,大概煮了半小时,高压锅突然爆炸了,现场惨不忍睹啊,幸好厨房里没

0