前言:
函数在编程中有着至关重要的地位,它组织代码、使代码复用,下面我们将分类介绍一下Python函数的基本使用、闭包、装饰器等;
函数的使用阶段:
定义阶段:定义就是把一段代码,赋值给函数
调用阶段:一定要先定义,再调用;
函数的组成结构:
1、函数名
2、 函数体
3、返回值 (1-3属于函数定义阶段)
4、调用阶段
5、表达式形式调用函数可以保存函数返回值 / 语句形式调用函数执行后的返回值 不会被保存下来;
def my_sum(x,y): #def语句 定义的函数名 res=x if x > y else y #函数体 return res #函数的执行结果,人过留名,雁过留声;函数执行结束后,如果没有返回值,就是 none a=my_sum(7,8) # 函数调用阶段 把函数执行的结果,赋值给变量,保存起来 print(a) #打印函数的执行结果 print(my_sum(8,9)) #语句形式 调用函数,不会保存函数的返回值;
函数的参数
函数的可变参数: *args **args (args是变量名可以为任何字符)
在实参的角度
*arga:在按位置传值的情况下,多余的 组织成 元组 值赋值给*args 包含多个位置参数
1 def chen (*b): # *b传入形参的是元组,其长度可以无穷无尽。 b=(1,2,3,4,5,6,7) 2 sum=0 3 for i in b: 4 sum+=i 5 return sum 6 7 print(chen(1,2,3,4,5,6,7))
在形参的角度
形参混合使用优先级:位置传参、args传参、默认传参
def foo (a,*b,c=2): # *b 在形参的角度来讲属于 位置传参 所以一定要在 默认传参之前 print(a) print(*b) print(c) foo(1,2,4,5,6,c=7)
*(1,2,3,4)或者 *[1,2,3,4]就相当于 把元组、列表这些序列 分解、打散 分别给实参;
def foo (a,b,c,d): print(a) print(b) print(c) print(d) foo( *(1,2,3,4)) # *(1,2,3,4) 元组也可以在实参数上 传值 *(1,2,43,4)== *args
**arges
从实参角度
在关键字传值时,把多余的实参 组织成 字典 形式 赋值给形参
1 def foo (a,**b): #从形参的角度来说 在关键字传值得情况下,把多余的实参赋给 **b 2 print(a) 3 print(b) 4 5 6 foo( 1,b=2,c=3 ,d=4)
运行结果:
1
{'b': 2, 'c': 3, 'd': 4}
(*a,**b): #接受任意 实参 ,可以位置传参,可以是关键字传参
1 def foo (*a,**b): #形参可以接受任意实参 ,可以是位置传参,可以是关键字传参 2 print(a) 3 print(b) 4 5 6 foo( 1,2,3,4,5,b=2,c=3 ,d=4) 7 8 运行结果: 9 (1, 2, 3, 4, 5) 10 {'b': 2, 'c': 3, 'd': 4}
闭包
前戏
x=1 def f1(): x=2 def f2(): print(x) return f2 f3=f1() x=2222222222 f3() #函数的作用域已经在函数定义阶段就已经确立了,调用函数阶段不管函数在哪个位置执行,都要回到原来确定好作用域;
执行结果:2
个人理解闭包函数的作用 (实现装饰器):
闭包可以保存住外部函数的变量(状态),既然内部函数打包了外部作用域的状态,在这个基础上增加外部函数的功能,这不是正是函数装饰器吗?
x=1 def f1(): x=2 y=6 def f2(): print(x) print(y) return f2 f3=f1() #f3 =f1内部的f2 x=2222222222 f3() ''' 函数的作用域已经在函数定义阶段就已经确立了,调用函数阶段不管函数在哪个位置执行, 都要回到原来定义的位置寻找作用域关系! ''' print(f3.__closure__[0].cell_contents ) #获取内部函数引用外部作用域的值 print(f3.__closure__[1].cell_contents )
将以下joson数据解析成sql语句(使用闭包,包住relation变量)
{"filters": [
{"func": "contains", "field": "title", "args": ["CHECKPOINT"]},
{"func": "contains_one", "field": "tag", "args": [10]}
],
"relation": "and"
}
import pymysql import datetime import json from conf import mysql_conf #MySQL基础配置类 class MySQLHandler(object): def __init__(self,mysql_conf): self.mysql_conf=mysql_conf def query(self, sql): self.ret = [] mysql_conn = pymysql.connect(**self.mysql_conf) cursor = mysql_conn.cursor() cursor.execute(sql) columns = [x[0] for x in cursor.description] for row in cursor.fetchall(): d = dict(zip(columns, row)) self.ret.append(d) cursor.execute('commit') cursor.close() mysql_conn.close() return self.ret def update(self, sql): self.ret = [] mysql_conn = pymysql.connect(**self.mysql_conf) cursor = mysql_conn.cursor() cursor.execute(sql) cursor.execute('commit') cursor.close() mysql_conn.close() return self.ret @property def global_alarm_rules(self): ret = self.query(sql='select * from alarm_rules where in_use=-2') return ret #获取alarm_rules表的1条记录的json数据进行解析 + 拼sql class RuleHandler(MySQLHandler): def __init__(self,mysql_conf): super().__init__(mysql_conf) def parse_rules(self,conditions):#使用闭包,包住relation变量 filters = [self.parse_filters(**x) for x in conditions['filters']] relation=conditions['relation'] self.select_sql = '' def f(): for x in filters: self.select_sql+= ' %s ' %(relation)+x() return f def parse_actions(self,action): self.update_sql='' if action['type'] == 'mark': def do(): self.update_sql='process_status = "%s"'%(action['args']) return do def parse_filters(self, field, func, args): sql = False if func == 'equals': sql = '%s = %s' % (field, args) elif func == 'contains': sql = '{0} like "%%{1}%%"'.format(field,args) elif func == 'during': sql = 'send_time between "%s" and "%s"' % (self.during_this_period(*args)) return (lambda:sql) def during_this_period(self,s,e): current_time = datetime.datetime.now() start_str = '%s-%s-%d %s:00:00' % (current_time.year,current_time.month, current_time.day,s) end_str = '%s-%s-%s %d:00:00' % (current_time.year, current_time.month, current_time.day,e) start_time = datetime.datetime.strptime(start_str, "%Y-%m-%d %H:%M:%S") end_time = datetime.datetime.strptime(end_str, "%Y-%m-%d %H:%M:%S") return start_time, end_time class Judge(RuleHandler): def __init__(self,mysql_conf): super().__init__(mysql_conf) def execute(self): for roule in self.global_alarm_rules: conditions=json.loads(roule['conditions']) action= json.loads(roule['action']) self.parse_rules(conditions)() sql='select id,send_time,title,process_status from alarm_messages where process_status = "UNPROCESSED" %s'%(self.select_sql) #select id,send_time,title,process_status from alarm_messages where process_status = "UNPROCESSED" and send_time between "2019-12-21 00:00:00" and "2019-12-21 04:00:00" and title like "%%DETECT OOM%%" alarm_infos=self.query(sql=sql) print(sql) print(alarm_infos) if alarm_infos:#符合规则的再去parse_action进行update self.parse_actions(action)() id_str = ','.join([str(x['id']) for x in alarm_infos]) sql = "update alarm_messages set %s where id in (%s)" % (self.update_sql,id_str) # self.update(sql=sql) if __name__ == '__main__': judge=Judge(mysql_conf=mysql_conf) judge.execute()
函数装饰器(实现装饰器的原理就是闭包函数)
1、在不改变现有代码的情况下,增加函数的功能;
2、@函数名A = 把正下方的原始函数B,当A函数的参数传入A,并把A的return的结果,赋值给 函数B; B=return的结果 A(B)
函数B
无参数装饰器函数
def auth(fun): def wrapper(*args,**kwargs): print("开始认证") f=fun(*args,**kwargs) print("结束") return wrapper @auth #@语法糖的的操作:把正下方的函数,当做一个参数,传给auth,并把index函数指向auth的的执行结果; 无参装饰器auth不执行;
def index():
print("主页") index()
有参装饰器
def auth1(name): def auth(fun): def wrapper(*args,**kwargs): print("%s 开始认证"% name) f=fun(*args,**kwargs) print("结束") return wrapper return auth @auth1(name='alex') #1、 当Python解释器遇到函数名+括号auth1(name='alex'),先让auth1先执行,执行完毕,得出执行结果。 #2、@auth1(name='alex')的执行结果,也就是return的函数, auth函数. #3、所以有参数装饰器 @是auth函数, 也就是把正下方的函数,当做一个参数,传给auth()的执行结果;wrapper函数 有参装饰器 def index(): print("主页") index()
装饰器的缺陷
原始函数的帮助文档会被装饰器函数覆盖,解决之道
from functools import wraps def auth(func): @wraps(func) #使用内置函数wraps,装饰一下原始函数,会保存住原始函数的帮助信息
from functools import wraps def auth(func): @wraps(func) #使用内置函数wraps,装饰一下原始函数,会保存住原始函数的帮助信息 def wrapprt(*args,**kwargs): ''' 我是1个装饰器函数 ''' print('开始认证。。。') res=func() print('结束认证。。。') return res return wrapprt @auth def index(): ''' 我是1个需要被装饰的函数 ''' print('主页') index() print(index.__doc__)
递归函数:
递归经常和迭代一起被提到,(迭代就是从复做某事);递归:函数调用自身,每次调用自身一次,问题规模比上一次减少,最后有一个明确的结束条件;
递归就是函数执行时,调用了自己,然后把这种状态积累保存在栈内存里,遇到自己执行的结果了,一起返回出来;
吃1+吃2+吃3 函数有了执行结果 4 的时候 吐出来:吃进去的1+吃进去2+吃进去3+函数结果= 10
堆内存:吃1+吃2+吃3,然后吐出来顺序;1------->2------->3
栈内存:吃1+吃2+吃3,然后吐出来顺序;3------>2-------->1
队列:吃了就拉出来,不积累;吃一个拉一个 1---->
def age(n): #age()=10 print('=========',n) if n==1: return 10 else: return age(n-1)+2 print(age(5))
data = [1, 3, 6, 7, 9, 12, 14, 16, 17, 18, 20, 21, 22, 23, 30, 32, 33, 35]
def seach(number,data): print(data) mid_index = int(len(data)/2) mid_values = data[mid_index] # print(data) if number> mid_values: data=data[mid_index:] seach(number,data) elif number < mid_values: data=data[:mid_index] seach(number,data) else: print('find it') return seach(3,data)
执行结果:
[1, 3, 6, 7, 9, 12, 14, 16, 17, 18, 20, 21, 22, 23, 30, 32, 33, 35] [1, 3, 6, 7, 9, 12, 14, 16, 17] [1, 3, 6, 7] #本程序的设计核心是 递归和二分法则:每次取出有序序列中间的那个值和要查找的number比较,大于取序列的右边,小于取出序列的左边,循环递归,直到找出结果; [1, 3] find it [1, 3] None
生成器函数
yeild 把函数的执行步骤,以yield为分界线划分成一个单位 ,遇到next(a)方法返回一个单位的执行结果1次。
def rang2(): star=0 while star<100: yield star*4 star+=1 # for i in rang2(): # print(i) #生成器定义在函数里的迭代器有 yield字段 #生成器:可以节省内存,拿一个取一个 def rang(): yield 1 yield 2 #yeild 把函数的执行步骤,以yield为分界线划分成一个单位 ,遇到next(a)方法返回一个单位的执行结果1次。 yield 3 # a=rang() # print(next(a)) # print(next(a)) # print(next(a)) def test3(): i=0 while i<10: yield "母鸡下一个鸡蛋%s"%i i+=1 f=test3() f2=test3() # print(next(f2)) # print(next(f2)) # print(next(f)) #计算 1+1+2+3+5+8的和 def test4(): a=1 yield a b=1 yield b while True: c=a+b yield c a=b b=c sum1=test4() for i in sum1: print(i)
高阶函数
接收1个函数作为参数输入,return返回1个函数,满足以上2条件其中一个都叫高阶函数。