函数
一、函数基础
1、为什么要用函数
# 增强程序的组织结构,可读性高 # 减少代码冗余 # 扩展性强
2、什么是函数
# 函数时组织好的,可以重复使用的的,用来实现单一,或相关联功能的代码段 # 函数能提高应用的模块性,和代码的重复利用率
# 在程序中,函数必须遵守:先定义,再调用
3、函数的分类
# 内置函数 为了 方便程序开发,针对一些简单的功能,Python解释器已经定义好了的函数即内置函数,对于内置函数我们可以拿来就用而无需事先定义,如len() sum() max() # 自定义函数 很明显内置函数所能提供的功能有限,这就需要我们自己根据需求,事先定义好我们自己的函数来实现某个功能,以后在遇到应用场景时,调用自定义函数即可
4、定义函数
# 函数代码块以def关键字开头,后接函数名和小括号 # 任何传入的参数和自身变量必须放在小括号里面,参数名称可以多个 # 函数的第一行需要写函数说明 # 函数内容以冒号起始,并且缩进 # return [表达式]结束函数,选择性的返回一个值给调用方,不带表达式的return ,返回值为None
5、有参函数、无参函数、空函数
# 有参函数 def userinfo(name,age,): # 定义时有参 ''' :param name: 姓名 :param age: 年龄 :return: ''' print('姓名:%s 年龄:%s' %(name,age)) userinfo('fred','18') # 调用时必须传参 # 无参函数 def func(): # 定义时无参 ''' :return: ''' print('this is No Reference function') func() # 调用时不需要传参 # 空函数 def pay(): pass # 空函数的作用:在项目立项阶段,使用空函数把此项目的大体功能干先列出来,后续只需要一个个实现小功能就可以
6、函数的返回值
return 是函数结束的标志, 函数内可以有多个return,但只要执行一个,函数就立即结束,并且把return后的值当做本次调用的结果返回 注意: 1、返回值没有类型限制 2、返回值没有个数限制,可以用逗号分开多个值一次返回 3、可以没有return,默认返回None
什么时候该有返回值?
调用函数,经过一系列的操作,最后拿到一个明确的结果,则必须要有返回值
通常有参数需要返回值,输入参数,经过计算,得到一个最终结果
什么时候不需要返回值?
调用函数,仅仅只是执行一系列的操作,最后不需要得到什么结果,则无需有返回值
通常无参函数不需要有返回值
7、函数调用:
# 语句形式:userinfo()
# 表达式形式:3*len('userinfo')
# 当做其他函数的参数:range(len('userinfo'))
8、函数基础阶段总结:
1、函数的使用必须遵守:先定义后调用的原则 2、在定义阶段,只检测函数命名语法以及函数体的语法 3、在调用阶段,通过函数名找到函数的内存地址,然后执行函数体代码 4、函数名;是用来访问到函数的内存地址的,拿到函数的内存地址加括号就可以触发函数体代码的执行 5、函数参数:是外界调用者为函数传值的媒介 6、函数体代码:是函数功能的具体实现 7、return是函数体执行的成果
二、函数的参数
1、实参与形参
函数的参数可以分为两大类:形参与实参
形参:函数定义阶段,括号内指定的参数,可以理解为变量名
实参:函数调用阶段,括号内指定的参数,可以理解为变量名对应的值
只有在调用函数时才会在函数体内发生实参(值)与形参(变量名)的绑定关系
该绑定关系仅在调用函数时临时生效,调用结束后就解除绑定
2、位置参数
''' 位置形参:函数定义阶段,按照从左到右的顺序依次定义的形参,称之为位置形参 位置实参:函数调用阶段,按照从左到右的顺序依次传入的值,称之为位置实参 ''' def userinfo(name,age): print('姓名:%s 年龄:%s' %(name,age)) userinfo('fred',18) # 此时就相当于格式化输出中的%s 姓名:fred 年龄:18 # 从左到右 name=fred age=18 # userinfo('fred',18,'sa')typeError: userinfo() takes 2 positional arguments but 3 were give
# 值传多了 userinfo('fred') userinfo() missing1 required positional argument:
'age' # 没有给age传值 userinfo(20,'fred') 姓名:20 年龄:fred # 由于严格按照位置传值,在不了解参数具体功能情况下,就会出现这种结果 # 总结: # 但凡是按照位置定义的形参,在调用时必须为其传值,多一个不行,少一个也不行 # 在传值时,按顺序与形参一一对应
3、关键字实参
''' 关键字实参:在函数调用阶段,按照key=value的形式定义的实参,称之为关键字实参 注意: 1、关键字实参可以完全打乱顺序,但仍然可以指名道姓的为指定形参传值 2、可以在调用函数时,关键字实参和位置实参混合使用 3、关键字实参要跟在位置实参的后边,并且不能为一个形参传多个值 ''' def userinfo(name,age,company): print('姓名:{name} 年龄:{age} 公司:{company}'.format(name=name,age=age,company=company)) userinfo('fred',company='9you',age=20) # 姓名:fred 年龄:20 公司:9you userinfo(age=20,company='9you','fred') SyntaxError: positional argument follows keyword argument # 位置参数不能跟在关键字参数右边
4、默认形参
''' 默认参数:在定义函数时,就已经为某些参数绑定值,称之为默认形参 注意: 1、在定义阶段就已经有值,意味着在调用阶段可以不用为其传值 2、默认形参必须放到位置形参后面 3、默认形参的值只在定义阶段生效一次,在函数定义之后发生的改动无效 4、默认形参的值通常应该为不可变类型 什么时候应该定义默认形参: 大多数情况下都一样的,比如民族 什么时候应该定义位置形参 大多数情况是不一样的,比如name ''' def userinfo(name,age,company,sex='male'): print('姓名:{name} 年龄:{age} 公司:{company} 性别:{sex}'.format(name=name, age=age, company=company,sex=sex)) userinfo('fred',age=18,company='9you') # 可以不用为默认形参传值 姓名:fred 年龄:18 公司:9you 性别:male userinfo('jerry',age=18,company='9you',sex='female') 姓名:jerry 年龄:18 公司:9you 性别:female # 也可为默认形参传值,以传的值为准
def userinfo(name,age,sex='male',company):SyntaxError: non-default argument follows default argument # 默认形参不能在位置形参左边
print('姓名:{name} 年龄:{age} 公司:{company} 性别:{sex}'.format(name=name, age=age, company=company,sex=sex))
5、可变长参数
''' 可变长参数是在调用函数时,函数的参数个数是不固定的 然而实参终究要为形参传值的,针对两种形式的参数不固定,对应着形参也必须有两种解决方案,来分别处理溢出的位置实参与关键字实参 python中约定俗成: *args # 位置形参 *kwargs # 关键字形参
* 会把溢出的实参存成元组,然后赋值给紧跟其后的变量名
** 会把溢出的关键字实参存成字典,然后赋值给紧跟其后的变量名 '''
1)形参中带*
def userinfo(username,passwd,*args): print('用户名:%s' %username) print('密码:%s' %passwd) print('其他:',args) userinfo('root','x', '0', '0', 'root', '/root', '/bin/bash')
用户名:root 密码:x 其他: ('0', '0', 'root', '/root', '/bin/bash')
2)形参中带* && 实参中带*
''' 实参中* 小窍门: 但凡实参中*,都先将其打散成位置实参,然后考虑传值 ''' def userinfo(username,passwd,*args): print('用户名:%s' %username) print('密码:%s' %passwd) print('其他:',args) userinfo('root',*('x', '0', '0', 'root', '/root', '/bin/bash')) #此处,只传一个位置实参,然后*后面跟了元组 用户名:root 密码:x 其他: ('0', '0', 'root', '/root', '/bin/bash') #处理过程:先将元组打散成位置实参,然后将x传给passwd,剩下的整体传给*,并绑定给args
3)、实参中带*
''' 实参中* 小窍门: 但凡实参中*,都先将其打散成位置实参,然后考虑传值 ''' def userinfo(username,passwd,desc): print('用户名:%s' %username) print('密码:%s' %passwd) print('描述:%s' %desc) l=['fred','1234','this is fred'] userinfo(*l) #实参中带*,形参中没带*的情况,要严格遵守位置形参的规则,多一个不行,少一个也不行
4)、形参中带**
''' 形参中带** 溢出的关键字实参会存成字典,然后赋值给紧跟其后的变量名 ''' def userinfo(username,passwd,**kwargs): print('用户名:%s' %username) print('密码:%s' %passwd) print('其他:',kwargs) userinfo(username='root',passwd='x',uid='0',gid='0',desc='root') # 溢出关键字实参被**存成字典,赋值给kwargs
用户名:root
密码:x
其他: {'uid': '0', 'gid': '0', 'desc': 'root'}
5)、形参带**,实参中带**
''' 形参中带** 溢出的关键字实参会存成字典,然后赋值给紧跟其后的变量名 实参中带** 但凡实参中带** ,先将其打散成关键字实参,然后再考虑传值 ''' def userinfo(username,passwd,**kwargs): print('用户名:%s' %username) print('密码:%s' %passwd) print('其他:',kwargs) userinfo('root',**{'passwd':'x','uid':'0','gid':'0','desc':'root'}) # 实参中带** 先将打散成关键字实参,passwd=x 其他给了kwargs 用户名:root 密码:x 其他: {'uid': '0', 'gid': '0', 'desc': 'root'}
6)、实参中带**
''' 实参中带** 但凡实参中带** ,先将其打散成关键字实参,然后再考虑传值 ''' def userinfo(username,passwd): print('用户名:%s' %username) print('密码:%s' %passwd) userinfo(**{'username':'fred','passwd':'1234','desc':'this is fred'}) TypeError: userinfo() got an unexpected keyword argument 'desc' # 不能传多也不能传少 userinfo(**{'username':'fred','passwd':'1234'}) 用户名:fred密码:1234
7、练习
''' 1、写函数,,用户传入修改的文件名,与要修改的内容,执行函数,完文件修改操作 ''' def replance_file(file_name,old,new): ''' 修改文件内容 :param file_name:要修改的文件 :param old: 修改前的的内容 :param new: 修改后的内容 :return: ''' import os with open(file_name, mode='r', encoding='utf8') as f_r, \ open('.%s.swap' % file_name, mode='w', encoding='utf8') as f_w: for line in f_r: if line.startswith(old): line = line.replace(old,new) f_w.write(line) else: f_w.write(line) os.remove(file_name) os.rename('.%s.swap' %file_name,file_name) return old,new res=replance_file('src.txt','name','姓名') print(res)
''' 2、写函数,计算传入字符串中【数字】、【字母】、【空格] 以及 【其他】的个数 ''' def result(s): counts={ 'alpha':0, 'digit':0, 'space':0, 'other':0, } for i in s: if i.isalpha(): # 判断是否为字母 counts['alpha']+=1 elif i.isdigit(): # 判断是否为数字 counts['digit']+=1 elif i.isspace(): # 判断是否为空格 counts['space']+=1 else: counts['other']+=1 return counts res=result('hello 18 age name %%%%&&&') print(res)
''' 3、写函数,判断用户传入的对象(字符串、列表、元组)长度是否大于5。 ''' def judge(*args): result=[] for i in args: if len(i) > 5: result.append(i) return result res=judge('fred_li',[18,'9you','sa'],('read ','play')) print(res)
''' 4、写函数,检查传入列表的长度,如果大于2,那么仅保留前两个长度的内容,并将新内容返回给调用者。 ''' def new_list(l): if len(l) > 2: return l[0],l[1] else: return l res=new_list(['fred','18','read','play']) print(res)
''' 5、写函数,检查获取传入列表或元组对象的所有奇数位索引对应的元素,并将其作为新列表返回给调用者。 ''' def fun(l): return l[::2] res=fun(['root','x','/home','this is root','/sbin/nologin']) print(res)
''' 6、写函数,检查字典的每一个value的长度,如果大于2,那么仅保留前两个长度的内容,并将新内容返回给调用者。 dic = {"k1": "v1v1", "k2": [11,22,33,44]} PS:字典中的value只能是字符串或列表 ''' def func(dic): d={} for k,v in dic.items(): if len(v) > 2: d[k]=v[0:2] else: d[k]=v return d print(func({'username':'fred','dept':('sa','ceo','cto','coo'),'hobby':['read','music','play','game'],'desc':('friend','father')}))
三、函数对象
''' 函数是第一类对象,意味着函数可以当做数据去使用 ''' # 函数身为一个对象,拥有对象的三个通用属性:id type value def userinfo(): print('from userinfo') print(userinfo) <function userinfo at 0x0387AA98> print(type(userinfo)) <class 'function'> print(id(userinfo)) 59222680
''' 可以被引用,即函数可以赋值给一个变量 ''' def userinfo(): print('from userinfo') func=userinfo print(func,userinfo) <function userinfo at 0x051DBA98> <function userinfo at 0x051DBA98>
''' 可以当做参数传给另外一个函数 ''' def f1(): print('from f1') def f2(func): print(func) f1() f2(f1) # f1是当做参数传给f2 ,f2内部调用了f1 <function f1 at 0x036CBA98> from f1
''' 可以当做函数的返回值 ''' def f1(): print('from f1') def f2(): return f1 res=f2() print(res) <function f1 at 0x0369BA98> res() from f1
''' 可以当做容器类型(可以存多个值)的元素 容器对象(list、dict、set等)中可以存放任何对象,包括整数、字符串,函数也可以作存放到容器对象中 ''' def auth(): print('from auth') def pay(): print('from pay') func_dic={ '1':auth, '2':pay, } func_dic['1']() from auth func_dic['2']() from pay
四、函数嵌套
1、函数的嵌套调用
''' 函数的嵌套调用:在调用一个函数时,其内部的代码又调用了其他的函数 ''' def sum(x,y): sum=x+y return sum def sums(a,b,c,d): res=sum(a,b) res1=sum(c,d) res2=res+res1 return res2 print(sums(10,20,30,40))
2、函数的嵌套定义
''' 函数的嵌套定义:在一个函数的内部又定义了另外一个函数 ''' def f2(): x=1 def f1(): print('from f1') print(x) f1() f2()
五、名称空间与作用域
1、名称空间
''' 名称空间:是名称与对象之间的关系,可以将命名空间看做是字典,其中的键是名称,值是对象 ''' ''' 内置名称空间:存放Python解释器自带的名称,如len max sum 生命周期:在解释器启动时产生,关闭时回收 ''' print(len) <built-in function len> ''' 全局名称空间:除了内置的与局部的名字之外的都属于全局名称空间 生命周期:在程序文件执行时,立刻产生,程序执行完毕后就回收 ''' name='root' ''' 局部名称空间:存放函数内部定义的名字 生命周期:在调用函数时临时生效,函数调用结束后立即回收 ''' def user(): age=18 return age print(user()) print(age) name 'age' is not defined # 函数调用已结束,所以age和18的绑定关系也被回收了 ''' 加载顺序:内置名称空间———全局名称空间————局部名称空间 查找顺序:基于当前名称空间查找 当前————上一级————上上级 假设目前在局部名称空间:查找顺序为:局部名称空间————全局名称空间————内置名称空间 强调::::: 函数的形参属于局部名称空间 '''
def foo():
x=4
print(locals()) # locals()方法是打印出当前局部名称空间中的名字与value的对应关系
foo()
{'x': 4} # 以字典方式存储
2、作用域
''' 作用域:指的是名字的作用范围 分为:全局作用域和局部作用域 ''' ''' 全局作用域:包含内置名称空间与全局名称空间中的名字 特点:全局有效,全局存活 ''' ''' 局部作用域:只包含局部名称空间的名字 特点:局部有效,临时存活 ''' x=3333 def area(): x=222 def area1(): x=111 print('from area1 %s' %x) area1() print('from area %s' %x) area() # 次函数为嵌套函数 from area1 111 # 在area1函数内部,x与111绑定,area1被调用时,area1内部有x的名字与值对应的关系,所以x此时等于111 from area 222 # area1调用结束后,x=111的绑定关系也随着解除,在area内部,x与222绑定,area被调用,所以此时x=222 print(x) 3333 # area调用结束,x与222的绑定关系也随之解除,所以此时是在全局作用域里查找x对应的valve,所以此时x=3333 ''' 总结: 函数的作用域关系是在函数定义阶段就已经固定死的,与函数的调用位置无关 即在调用函数时一定要到定义函数的位置寻找作用域关系 '''
3、global和nonlocal
''' global:在局部名称空间声明其后的变量是作用于全局 ''' x=3333 def func(): x=2222 def func1(): global x x=4444 print('from func1 %s' %x) func1() print('from func %s' %x) func() from func1 4444 # 由于func1中声明了x是全局名称空间中的名字,所以它作用于本层局部作用域和全局作用域, from func 2222 # 但不影响上层局部作用域 print(x) 4444 # 此时就算全局名称空间中定义了x=3333,也以global所在的局部名称空间中定义的为准
''' nonlocal 在局部名称空间声明其后的变量作用于上层 ''' x=1111 def func(): x=4444 def func1(): nonlocal x x=3333 print('from func1 %s' %x) func1() print('from func %s' %x) func() from func1 3333 # 由于func1中nonlocal声明了x=3333 from func 3333 # 所以就算func中定义了x=4444,也不生效 print(x) 1111 # nonlocal并未影响全局名称空间
六、闭包函数
''' 大前提:函数的作用域关系是在函数定义时就已经固定死的,与调用位置无关 闭包函数: 1、闭指的是:定义在函数内部的函数 2、包指的是:该内部函数包含对其外层函数作用域名字的引用 闭包函数通常需要结合函数对象的概念,打破层级限制,将闭包函数返回到外部使用 ''' import requests # 要事先安装requests模块 def outter(url): def get(): response=requests.get(url) print(len(response.text)) return get # 返回get函数的内存地址 jd=outter('http://www.jd.com') # 调用outter函数,实际是调用内部的get jd() baidu=outter('https://www.baidu.com') baidu() ''' 总结: 1、闭包函数传一次值,多次调用 2、通过外部函数把值传给内部函数 3、闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得无论在何处调用,都优先使用自己外层包裹的作用域 '''
七、装饰器
''' 装饰器是闭包函数一种应用场景 装饰器: 装饰指的在不修改被装饰对象的前提下,为被装饰对象添加新功能 器指的是工具 装饰器本身可以为任意可调用的对象,被装饰的对象也可以是任意可调用的对象 原则: 写一个函数给另外一个函数添加新功能,需要遵守开放封闭的原则(对修改是封闭的,对扩展是开放的) 1、不修改被装饰对象的源代码 2、不修改被装饰对象的调用方式 '''
1、无参装饰器之装饰无参对象
''' 无参装饰器 ''' import requests import time def get(): response=requests.get('http://www.baidu.com') print('The url have %s' %(len(response.text))) # 为get函数加上统计时间的功能 def timmer(func): start_time=time.time() func() stop_time=time.time() print('run time is %s' %(stop_time - start_time)) # timmer(get) # 此方法虽然能实现需求,但是其改变了原对象的调用方式,pass掉 # 只能考虑使用闭包函数咯 def wrapper(func): # func为最原始的get def timmer(): # 此处一定不能传值,如果传值就失去了闭包的意义 start_time=time.time() func() #此处的的func是由包在外层的wrapper传进来的 stop_time=time.time() print('run time is %s' %(stop_time - start_time)) return timmer get=wrapper(get) # 此时(get)为最原始的get函数,传给了func,然后将wrapper(get)重新赋值get get() # 此处get被伪装了,站在用户的角度上,还以为调用的是最原始的get函数,其实get调用的是wrapper,在wrapper内部为get加了统计时间的功能
# 到此为止,新功能实现了,并且遵守了开放封闭原则(未修改被装饰对象的源代码和调用方式) ''' 装饰器语法 在被调用对象的正上方使用"@装饰器名" @装饰器名 == get=wrapper(get) ''' # 上面的功能可以改写为: def wrapper(func): # func为最原始的get def timmer(): # 此处一定不能传值,如果传值就失去了闭包的意义 start_time=time.time() func() #此处的的func是由包在外层的wrapper传进来的 stop_time=time.time() print('run time is %s' %(stop_time - start_time)) return timmer @wrapper #get=wrapper(get) def get(): response=requests.get('http://www.baidu.com') print('The url have %s' %(len(response.text))) get() The url have 2381 run time is 0.034737348556518555
2、无参装饰器之装饰有参对象
''' 有参装饰器 接上面的例子,只能统计http://www.baidu.com的时间统计,功能被写死了 此时就得给装饰器传参了 ''' import requests import time def wrapper(func): # 最原始的get def timmer(*args,**kwargs): #给被调用对象传参 'http://www.jd.com' start_time=time.time() func(*args,**kwargs) #get('http://www.jd.com') / home() stop_time=time.time() print('run time is %s' %(stop_time - start_time)) return timmer @wrapper def get(url): response=requests.get(url) print('The url have %s' %(len(response.text))) @wrapper(‘http://www.jd.com’) def home(): time.sleep(1) print('welcome access home page') # 过程分析: # get=wrapper(get) # 首先将get传给wrapper,也就间接传给timmer,拿到timmer的返回值,即wrapper(get) # print(get) # <function wrapper.<locals>.timmer at 0x051AFDB0> get() # 第二步,此时是给最原始的get传参,由timmer接收,并传给内部的get函数,完成新加功能并且未修改被装饰对象的源代码和调用方式 home() # 到此为止,可实现即可装饰有参对象和无参对象
''' 完整版装饰器语法 ''' import requests import time def wrapper(func): def timmer(*args,**kwargs): start_time=time.time() res=func(*args,**kwargs) stop_time=time.time() print('run time is %s' %(stop_time - start_time)) return res # 返回源对象的返回值 return timmer @wrapper def get(url): response=requests.get(url) # print return ('The url have %s lines' %(len(response.text))) # 源对象有返回值 print(get('https://www.baidu.com'))
示例:
'''
为被装饰对象加上认证功能''' import requests import time current_userinfo={'user':None} # 存储当前登录用户的信息 def auth(func): def wrapper(*args,**kwargs): if current_userinfo['user']: # 如果current_userinfo['user']的值非空,则证明当前用户已登录,不用重复登录 func(*args,**kwargs) return print(func(*args,**kwargs)) else: # 否则,从新登录 username=input('请输入账号:').strip() passwd=input('请输入密码:').strip() if username == 'fred' and passwd == '123': print('welcome to login') current_userinfo['user']=username func(*args,**kwargs) return print(func(*args,**kwargs)) else: print('用户或密码错误') return wrapper @auth def get(url): response=requests.get(url) return ('The url have %s' %(len(response.text))) @auth def home(): time.sleep(1) return ('welcome access home page') get('http://www.jd.com') home()
3、多个装饰器执行顺序
''' 为一个对象添加多个装饰器 ''' import requests import time current_userinfo={'user':None} # 存储当前登录用户的信息 def auth(func): def wrapper(*args,**kwargs): if current_userinfo['user']: func(*args,**kwargs) return print(func(*args,**kwargs)) else: username=input('请输入账号:').strip() passwd=input('请输入密码:').strip() if username == 'fred' and passwd == '123': print('welcome to login') current_userinfo['user']=username func(*args,**kwargs) return print(func(*args,**kwargs)) else: print('用户或密码错误') return wrapper def timmer(func): # 最原始的get def wrapper1(*args,**kwargs): start_time=time.time() res=func(*args,**kwargs) # wrapper(get) stop_time=time.time() print('run time is %s' %(stop_time - start_time)) return res return wrapper1 @timmer @auth def get(url): response=requests.get(url) return ('The url have %s' %(len(response.text))) get('http://www.jd.com') ''' 分析: 1、@auth === 最原始的get 》》》 wrapper(get) 即get=wrapper(get) 2、@timmer == 不是最原始get 》》 wrapper1(wrapper(get)) 即get=wrapper1(wrapper(get)) 执行顺序: 1、先执行timmer,就是wrapper1(wrapper(get)), 即执行auth 2、等auth执行完,再回到timmer,执行res=func(*args,**kwargs)之后的代码 3、整个装饰器执行完成,本次时间统计是从timmer开始到auth结束,查看结果 请输入账号:fred 请输入密码:1234 用户或密码错误 run time is 5.6903979778289795 结论:多个装饰器的情况下,自上而下一次执行 '''
4、有参装饰器
''' 有参装饰器:本质就是在无参装饰器外又包了一层 ''' import requests import time current_userinfo={'user':None} # 存储当前登录用户的信息 def multi_auth(engine): def outter(func): def wrapper(*args,**kwargs): # engine是关键字实参传进来的,所以file一定会传给engine if engine == 'file': if current_userinfo['user']: func(*args,**kwargs) return print(func(*args,**kwargs)) else: username=input('请输入账号:').strip() passwd=input('请输入密码:').strip() if username == 'fred' and passwd == '123': print('welcome to login') current_userinfo['user']=username func(*args,**kwargs) return print(func(*args,**kwargs)) else: print('用户或密码错误') elif engine == 'mysql': print('mysql认证机制') elif engine == 'ldap': print('ldap认证机制') else: print('不支持改认证机制') return wrapper return outter @multi_auth(engine='file') # outter(get) # @multi_auth(engine='mongdb') def get(url): response=requests.get(url) return ('The url have %s' %(len(response.text))) get('http://www.jd.com')
5、完全装饰被装饰对象(继承被装饰对象的属性)
''' wraps装饰器:获取被装饰对象的属性(函数名、帮助信息等) Help on function wraps in module functools: wraps(wrapped, assigned=('__module__', '__name__', '__qualname__', '__doc__', '__annotations__'), updated=('__dict__',)) ''' import requests import time from functools import wraps current_userinfo={'user':None} # 存储当前登录用户的信息 def multi_auth(engine): def outter(func): @wraps(func) # 将最原始的get对象的属性转嫁给wrapper def wrapper(*args,**kwargs): # engine是关键字实参传进来的,所以file一定会传给engine if engine == 'file': if current_userinfo['user']: func(*args,**kwargs) return print(func(*args,**kwargs)) else: username=input('请输入账号:').strip() passwd=input('请输入密码:').strip() if username == 'fred' and passwd == '123': print('welcome to login') current_userinfo['user']=username func(*args,**kwargs) return print(func(*args,**kwargs)) else: print('用户或密码错误') elif engine == 'mysql': print('mysql认证机制') elif engine == 'ldap': print('ldap认证机制') else: print('不支持改认证机制') return wrapper return outter @multi_auth(engine='file') def get(url): ''' 获取指定url的response.text的长度 :param url: :return: ''' response=requests.get(url) return ('The url have %s' %(len(response.text))) # get('http://www.jd.com') print(help(get)) # 获取函数的帮助信息(注释) Help on function get in module __main__: get(url) 获取指定url的response.text的长度 :param url: :return: None
八、三元表达式、列表生成式、字典生成式、二分法、函数与的递归调用
1、三元表达式
''' 三元表达式: 语法: 条件成立的返回值 if 条件 else 条件不成立的返回值 ''' x=10 y=20 # res=x if x > y else y res=True if x > y else False print(res)
2、列表生成式
''' 列表生成式:和for循环搭配 语法:l=[i for i in 序列 条件(可以没有条件)] 条件只能跟一个 ''' l=[i for i in range(5)] print(l) # 等同于 for_l=[] for i in range(5): for_l.append(i) print(for_l) l1=[i for i in range(5) if i > 2] print(l1) # 等同于 for_l1=[] for i in range(5): if i > 2: for_l1.append(i) print(for_l1) str='root:x:0:0:root:/root:/bin/bash' l2=str.split(':') l2=[i.upper() for i in l2 if i == 'root'] print(l2) # 等同于 l3=[] for i in str.split(':'): if i == 'root': l3.append(i.upper()) print(l3)
3、字典生成式
''' 字典生成式: 语法:d1={k.upper():v for k,v in d1.items() if } 可以不加条件 ''' d1={ 'name': 'fred', 'age': 18, 'commany': '9you', 'weight': 50.4 } print({k.upper():v for k,v in d1.items() if k == 'weight'}) # 等同于 d2={} for k,v in d1.items(): d2[k.upper()]=v print(d2)
4、函数的递归调用
''' 函数的递归调用: 在调用一个函数的过程中又直接或间接调用了自己,称之为函数的递归调用 本质就是一个重复的过程,必须有两个明确阶段: 1、回溯:一层一层的递归下去调用下去,每进入下一层问题的规模都应该有所减少 2、递推:往回一层一层的结束调用 递归必须要有一个明确的结束条件,在满足该条件的情况下会终止递归 递归调用的分类: 直接调用:自身调用 def foo(): foo() 间接调用:通过别的函数间接调用自身 def bar() foo() 递归 VS while循环 递归只需要把控结束或进入递归的条件即可,至于循环次数或者嵌套层数无须考虑 调用函数会产生局部的名称空间,占用内存,因为上述这种调用会无需调用本身, python解释器的内存管理机制为了防止其无限制占用内存,对函数的递归调用做了最大的层级限制 import sys sys.getrecursionlimit() 查看当期最大层级限制 sys.setrecursionlimit(2000) 修改最大层级限制 ''' # 示例: # age(5) = age(4) + 2 # age(4) = age(3) + 2 # age(3) = age(2) + 2 # age(2) = age(1) + 2 # age(1) = 18 #推算出age(5)的值 def age(n): # n从高到底,一步一步逼近1 if n == 1: return 18 return age(n-1)+2 print(age(2)) # 取出l中的每个元素 l=[1,[2,[3,]]] def foo(l): for item in l: if type(item) is list: foo(item) else: print(item) foo(l) 1 2 3
#总结递归的使用: 1. 必须有一个明确的结束条件 2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少 3. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)
5、二分法
''' 二分法是一种快速查找的方法,时间复杂度低,逻辑简单易懂,总的来说就是不断的除以2除以2... 分法查找非常快且非常常用,但是唯一要求是要求数组是有序的 ''' # 查找次序列中有没有43 nums=[3,11,13,15,23,27,43,51,72,81,93,101] def binary_search(num,nums): print(nums) if len(nums) == 0: print('%s is not found' %num) return # mid_index=len(nums) // 2 if num > nums[mid_index]: # in right nums=nums[mid_index+1:] # 从mid_index索引号加一位(如果不加1,则取到mid_index)开始-数组最后 binary_search(num,nums) #重复调用本身逻辑,传入新的nums elif num < nums[mid_index]: # in left nums=nums[:mid_index] #从数组索引0开始取-mid_index binary_search(num,nums) # 重复调用本身逻辑,传入新的nums else: print('find it') binary_search(94,nums) [3, 11, 13, 15, 23, 27, 43, 51, 72, 81, 93, 101] [51, 72, 81, 93, 101] [93, 101] [93] [] 94 is not found
九、匿名函数
''' 匿名函数:即没有名字的函数,与函数有相同的作用域,但是匿名函数意味着引用计数为0,使用一次就释放,除非让其有名字 定义匿名函数就是定义出了一个函数的内存 语法: lambda 实参 函数体代码 lambda 只是一个表达式,函数体比 def 简单很多。 lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。 lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。 ''' func=(lambda name,age:print('my name is %s my age is %s' %(name,age)))('name',20) print(func) ''' 示例 文件内容标题为name,age,work,commany fred|20|SA|haymarker lck|30|IT|u9 取出每一行,按如下格式存储 [{'name':'fred','age':'20','work':'SA','commany':'haymarker'}] ''' # 字典生成式的另一种应用场景 with open('info.txt','rt',encoding='utf8') as info: items=[line.strip().split('|') for line in info] # 列表生成式将文件内容按行转换成列表 # 字典生成式将items的值传给字典里的key userinfo=[{'name':name,'age':age,'salary':salary,'work':work,'commany':commany} for name,age,salary,work,commany in items] # print(userinfo) # 取出年龄最大的成员的信息 def maxage(dic): return dic['age'] # 返回age对应的value for dic in userinfo: res=maxage(dic) # 循环取出最大age的值 print(max(userinfo,key=maxage)) # 调用maxage函数,取出age最大用户信息,由于循环的是userinfo列表,所以返回最大age所在的列表 print(max(userinfo,key=lambda dic:dic['age'])) #dic是参数, {'name': 'lck', 'age': '30', 'salary': '1000', 'work': 'IT', 'commany': 'u9'} # 取出薪资最高的成员信息 print(max(userinfo,key=lambda dic:dic['salary'])) {'name': 'fred', 'age': '20', 'salary': '3000', 'work': 'SA', 'commany': 'haymarker'} # 将成员信息映射成首字母大写的形式 def capital(dic): return {'name':dic['name'].capitalize(), 'age':dic['age'], 'salary':dic['salary'], 'work':dic['work'], 'commany':dic['commany']} for dic in userinfo: res=capital(dic) user=map(capital,userinfo) print(user) <map object at 0x04CD1ED0> print(list(user)) [{'name': 'Fred', 'age': '20', 'salary': '3000', 'work': 'SA', 'commany': 'haymarker'}, {'name': 'Lck', 'age': '30', 'salary': '1000', 'work': 'IT', 'commany': 'u9'}] users=map(lambda dic:{ 'name':dic['name'].capitalize(), 'age':dic['age'], 'salary':dic['salary'], 'work':dic['work'], 'commany':dic['commany'] },userinfo) print(list(users))
十、迭代器、生成器、生成器表达式
1、迭代器
''' 什么是迭代器 迭代器就是一个迭代取值的工具 迭代是一个重复的过程,但是每一次重复都是基于上一次的结果而进行 为何要用迭代器 针对没有索引的数据类型,比如字典、集合、文件,要想迭代取出其中包含的一个个的值 Python解释器必须提供一种能够不依赖与索引的迭代取值工具 什么是可迭代对象 内置有__iter__()方法的都是可迭代对象 以下数据类型都是可迭代对象: <str_iterator object at 0x04AB24F0> <list_iterator object at 0x04AB2970> <dict_keyiterator object at 0x04AB38A0> <set_iterator object at 0x04AB85D0> <_io.TextIOWrapper name='info.txt' mode='r' encoding='utf8'> int和float不是可迭代对象 迭代器对象 1、内置有__iter__() 2、内置有__next__() 文件既是可迭代对象,又是迭代器对象 f.__iter__() f.__next__() 对于可迭代对象来说,调用__iter__()方法,得到就是迭代器对象
注意:
迭代器对象一定是可迭代对象
可迭代对象不一定是迭代器对象'''
''' 迭代器对象的使用 ''' info=['adm','/var/adm','/sbin/nologin'] obj=info.__iter__() print(obj.__next__()) # obj.__next__() 等同于next(obj) print(next(obj)) print(next(obj)) print(next(obj)) # next()方法抛出StopIteration异常时,标志着迭代器对象里的值已经被取完了 StopIteration # 使用while循环来取值 while True: try: print(next(obj),end=' ') except StopIteration: # except捕捉到StopIteration异常,则break掉 break ''' try...except: try的工作原理是,当开始一个try语句后,python就在当前程序的上下文中作标记 这样当异常出现时就可以回到这里,try子句先执行,接下来会发生什么依赖于执行时是否出现异常。 如果当try后的语句执行时发生异常,python就跳回到try并执行第一个匹配该异常的except子句,异常处理完毕,控制流就通过整个try语句(除非在处理异常时又引发新的异常)。 如果在try后的语句里发生了异常,却没有匹配的except子句,异常将被递交到上层的try,或者到程序的最上层(这样将结束程序,并打印缺省的出错信息)。 如果在try子句执行时没有发生异常,python将执行else语句后的语句(如果有else的话),然后控制流通过整个try语句。 '''
2、for循环的工作原理
''' 基于for循环就可以不依赖与索引取值 工作原理: 1.执行 in后对象的dic.__iter__()方法,得到此对象的迭代器对象 2.执行next(dic.__iter__()),将得到的值赋值给k 3.重复过程2,直到捕获StopIteration,结束循环 ''' dic={ 'name':'fred', 'age':19, 'commany':'haymarker', } for k in dic: print(k)
''' 迭代器的优缺点: 优点: 提供了一种不依赖与索引的、通用的取值方法 节省内存,同一时间在内存中只有一个值(对象元素过大,迭代器可以更节省内存) 缺点: 针对同一个迭代器对象只能取完一次,不能重复取,不如按照索引或者key取值灵活 无法预测迭代器对象所包含值的个数 '''
3、生成器
''' 什么是生成器 在函数内但凡出现yield关键字,再调用函数就不会触发函数体代码的执行了 generator() 直接调用不会触发函数体代码的执行 会得到一个返回值,该返回值就是一个生成器对象 def generator(): print('this is first') yield print(generator()) <generator object generator at 0x04F52870> 而生成器对象本身就是迭代器 g.__next__() g.__iter__() 为什么要用生成器 生成器是自定义的迭代器 ''' def generator(): print('this is first') yield g=generator() print(g.__next__()) # 既然g是迭代器,那么就可以通过迭代器规则通过next()方法取值 # 即触发g对应的函数体代码的执行,直到碰到一个yield就暂停住,该yield后的值就当做本次next返回值 # 如果yield后面没值,则返回None print(g.__next__()) # 依次取值,直到遇到抛出StopIteration,说明该对象中的值已经被取完了 ''' 总结yield功能: 1、提供了一种自定义迭代器的方式 2、可以用于返回值 yield与return的区别: 相同点:都可以用于返回值,个数及类型都没有限制 不同点:yield可以返回多次值,return只能返回一次,整个函数就结束了 3、函数暂停以及继续执行的状态是由yield保存的 ''' # 练习:自定义生成器,模拟range方法 def my_range(start,stop,step=1): while True: if start >= stop: break yield start start+=step for item in my_range(1, 5, 2): print(item)
十一、面向过程编程
'''' 面向过程编程 1、首先强调:面向过程编程绝对不是用函数编程这么简单,面向过程是一种编程思路、思想,而编程思想是不依赖于具体的语言或语法。言外之意即是我们不依赖于函数,也可以基于面向过程的思想编写程序 2、定义 面向过程的核心是过程二字,过程指的是解决问题的步骤,即先干什么再干什么 基于面向过程设计程序就好比在设计一条流水线,是一种机械式的思维方式 3、优点:复杂的问题流程化,进而简单化 4、缺点:可扩展性差,修改流水线的任意一个阶段,都会牵一发而动全身 5、应用:扩展性要求不高的场景,典型案例如Linux内核。git,httpd '''
转载请注明出处:http://www.cnblogs.com/lichunke/