七、学习py:函数 人嘛了,这篇写到一半关机忘记保存了。
就简略写啦o(╥﹏╥)o
1.初识函数 1 2 3 4 def dhk (): print ("joker!" ) dhk()
1.1扩充功能,发送邮件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import smtplibfrom email.mime.text import MIMETextfrom email.utils import formataddrdef send_email (): msg = MIMEText("越江才学会用py发送邮件。" , "html" , "utf-8" ) msg['From' ] = formataddr(["越江" , "自己的邮箱" ]) msg['Subject' ] = "猪哥好" server = smtplib.SMTP_SSL("smtp.qq.com#发信服务器" ) server.login("#自己的邮箱" , "#授权码" ) server.sendmail("#自己的邮箱" , "#对面的邮箱" , msg.as_string()) server.quit send_email()
2.参数 在上述发邮件功能中,如果要对多人进行发送操作,一个一个发送略显复杂,所以可以将对方的邮件设置为参数,这样就能极大的提高代码的利用率。
1 2 3 def dhk (a ): print ("a" ) dhk(joker)
2.1动态参数 2.1.1*(只能通过地址传递) 1 2 3 4 def func (*args ): print (args) func(1 ,12 ,123 ,123 ,321 )
2.1.2**(只能通过名称传递) 1 2 3 4 def func (**kwargs ): print (kwargs) func(n1 = "dhk" ,n2 = 'joker' )
2.1.3*&** 1 2 3 4 def func (*args,**kwargs ): print (args,kwargs) func(123 ,n1="nihao" ) func(123 )
2.1.4知识补充 在定义函数时可以用**和 *,其实在执行函数时,也可是使用。
形参固定,实参用**和 *
1 2 3 4 5 6 7 def func (a1,a2 ): print (a1,a2) func(11 ,22 ) func(*[11 ,22 ]) func(a1=1 ,a2=2 ) func(**{"a1" :1 ,"a2" :2 })
形参中用了* 和 ,实参也用了 和 *
2.2默认参数 1 2 def func (a,b=123 ): print (a,b)
2.2.1参数的默认值【面试题】有坑 这个知识点在面试题中出现的概率比较高,但真正实际开发中用的比较少。
1 2 def func (a1,a2=18 ): print (a1,a2)
原理:python在创建函数(未执行)时,如果发现函数的参数中有默认值,则在函数内部会创建一块区域并维护这个默认值。
执行函数未传值时,则让a2指向函数维护的那个值的地址。
执行函数传值时,则让a2指向新传入的值的地址
在特定情况【默认参数的值是可变类型(列表,字典,集合)】&【函数内部会修改这个值】下,参数的默认值 有坑!超级大坑!!!!
小坑
1 2 3 4 5 6 7 8 9 def func (a1,a2=[] ): a2.append(666 ) print (a1,a2,id (a2)) func(123 ) func(321 ) func(33 ,[77 ,66 ]) func(300 )
大坑
1 2 3 4 5 6 7 8 9 10 11 12 def func (a1,a2=[1 ,2 ] ): a2.append(a1) return (a2) v1 = func(10 ) print (v1)v2 = func(20 ) print (v2)v3 = func(30 ,[11 ,22 ]) print (v3)v4 = func(40 ) print (v4)
深坑
1 2 3 4 5 6 7 8 9 10 11 12 13 def func (a1,a2=[1 ,2 ] ): a2.append(a1) return (a2) v1 = func(10 ) v2 = func(20 ) v3 = func(30 ,[11 ,22 ]) v4 = func(40 ) print (v1)print (v2)print (v3)print (v4)
2.5知识补充 1.**必须放在 *的后面
2.参数和动态参数混合时,动态参数只能放在最后。
3.默认值参数和动态参数同时存在。
2.6参数的补充! 2.6.1参数内存地址相关【面试题】 在开始将参数的内存地址相关之前,我们来学习一个技能:
查看某个值在内存中的地址。
1 2 3 v1 = "dhk" addr = id (v1) print (addr)
使用案例,如果两个东西(有些东西是两个东西但是内容是一样的)指向的是同一个地址,那他们就是一个东西。(听君一席话,如听一席话)
注意:函数执行传参时,传递的是内存地址。
1 2 3 v1 = "dhk" addr = id (v1) print (addr,id (addr))
2.6.2内存地址的利用 1 2 3 4 5 6 7 def func (data ): data.append(666 ) data_list = [11 ,22 ,33 ] func(data_list) print (data_list)
3.函数返回值 1 2 3 4 def func (a,b ): return (a+b) print (func(a=1 ,b=123 ))
注意:函数在碰到return的时候就会立即停止
3.1函数的返回值是内存地址 1 2 3 4 5 6 def func (): data = [11 ,22 ,33 ] print (id (data)) return data v1 = func() print (id (v1))
但是在第二遍执行这个函数的时候,内存地址是会变的,因为在函数执行完之后,其中参数是会被抹去的,重新执行函数又会重新创建一个新的内存地址。
1 2 3 4 5 6 7 def func (): data = [11 ,22 ,33 ] return data v1 = func() print (id (v1))v2 = func() print (id (v2))
4.函数和函数名 函数名其实就是一个变量,这个变量只不过指代的函数而已。
注意:函数必须先定义才能被调用。(解释型语言)(如果是编译型语言就不用。)
4.1函数做元素 既然函数就相当于是一个变量,那么在列表等元素中是否可以把函数当做元素呢?
1 2 3 4 5 6 7 8 9 def func (): return (123 ) data_list = ["dhk" ,"func" ,func,func()] print (data_list[0 ])print (data_list[1 ])print (data_list[2 ])print (data_list[3 ])
注意:函数同时也可被哈希,所以函数名同志也可以当做集合的元素、字典的键。
掌握这个只是之后,对后续的开发有很大的帮助。
案例1:要开发一个类似微信的功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 def send_message (): '''发送消息''' pass def send_image (): '''发送图片''' pass def send_file (): '''发送文件''' pass func_dict = { "1" :send_message, "2" :send_image, "3" :send_file} print ("欢迎使用越江系统" )print ("请选择:1.发送消息2.发送图片3.发送文件" )choice = input ("请输入选择的序号" ) func = func_dict.get(choice) if not func: print ("输入错误" ) else : func()
案例2:某个特定情况需要连续执行发消息,发图片,发文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def send_message (): '''发送消息''' pass def send_image (): '''发送图片''' pass def send_file (): '''发送文件''' pass func_List = [send_message,send_image,send_file] for func in func_List: func()
4.2函数名赋值 如果我们将函数名重新赋值,那么函数名不在指代函数,而是指代你赋值的那个东西。
1 2 3 4 5 def func (): print (123 ) func() func = 1 print (func)
注意:由于函数名被重新定义之后,就会变成新定义的值,所以大家在自定义函数的时候,不要与python内置的函数重合。
4.3函数名做参数和返回值 1.参数
1 2 3 4 5 6 7 8 def plus (num ): return num+100 def handler (func ): res = func(10 ) msg = f"执行func,并且获取到的结果为:{res} " print (msg) headler(plus)
2.返回值
1 2 3 4 5 6 7 8 9 10 def plus (num ): return num+100 def handler (): print ("执行handler" ) return plus result = handler() data = result(20 ) print (data)
5.作用域 作用域,可以理解为一块空间,这块空间的数据是可以共享的。通俗点来说,作用域就类似于一个房子,房子中的东西归里面所有人共享,其他房子的人无法获取。
5.1函数为作用域 python以函数为作用域,所以在函数内创建的所有数据,可以在次函数中被使用,无法在其他函数中被使用。
1 2 3 4 5 6 7 8 9 10 def func (): age = "dhk" print (name) def func2 (): age = 123 print (age) func() func2()
5.2全局和局部 一般来说全局变量的变量名都是大写。
在外面定义的变量为全局变量,一般在函数里面定义的变量为局部变量。
在局部作用域调用变量的时候,首先会在自己的作用域寻找变量,如果找不到才会在上一级作用域中寻找变量。
实例1:在局部作用于中读取全局作用域的变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 NAME = "dhk" AGE = 123 def func (): asd = "dsa" AGE = "nihao" print (asd) print (AGE) print (NAME) func() print (AGE)''' 输出 dsa nihao dhk 123 '''
5.3global关键字 在局部作用于默认对全局变量只能进行:读取和修改内部元素(可变类型),无法对全局变量进行重新赋值。
但是如果想要在局部作用域中对全局变量重新赋值,则可以基于global关键字实现:
1 2 3 4 5 6 7 dhk = "joker" def func (): global dhk dhk = "动肝" func() print (dhk)
6.函数的嵌套 python中以函数为作用域,在作用域中定义的相关数据只能被当前作用于或子作用域使用。
6.1函数在作用域中 其实,函数也是定义在作用域中的数据,在执行函数的时候,也同样遵循:优先在自己的作用域中寻找,没有则向上寻找
6.2函数定义的位置 1 2 3 4 5 6 7 8 9 def func (): print ("外面" ) def inside (): def func (): print ("里面" ) func() func() inside()
其实,大多数情况下我们都会将函数定义在全局,不会嵌套定义函数。不过,当我们定义一个函数去实现某功能,想要将内部功能拆分成N个函数,又担心这n个函数放在全局会与其他函数冲突时(尤其是多人协同开发),可以选择使用函数的嵌套。
6.3嵌套引发的作用域问题 基于内存和执行过程分析作用域
1 2 3 4 5 6 7 8 name = "dhk" def run (): name = "joker" def inner (): print (name) inner() run()
7.闭包 简而言之就是讲数据封装在一个包中,使用时再去里面取。(本质上,闭包是基于函数嵌套搞出来一个中特殊嵌套)
闭包应用场景1:封装数据防止污染全局
1 2 3 4 5 6 7 name = "dhk" def func (age ): print (name,age) def func2 (): print (name,age) def func3 : print (name,age)
闭包应用场景2:封装数据到一个包里,使用时再取
1 2 3 4 5 6 7 8 9 def task (arg ): def inner (): print (arg) return inner v1 = task(11 ) v2 = task(22 ) v1() v2()
1 2 3 4 5 6 7 8 9 10 11 12 13 def task (arg ): def inner (): print (arg) return inner inner_list = [] for val in [11 ,22 ,33 ]: inner_list.append(task(val)) inner_list[0 ]() inner_list[1 ]() inner_list[2 ]()
8.装饰器 现在给你一个函数,在不修改函数源码的前提下,实现在函数执行前和执行后分别输入before和after
1 2 3 4 5 6 def func (): print ("我是一个func函数" ) value = (11 ,22 ,33 ,44 ) return value result = func() print (result)
8.1第一回合 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 def func (): print ("我是一个func函数" ) value = (11 ,22 ,33 ,44 ) return value def outer (origin ): def inner (): print ("before" ) res = origin() print ("after" ) return res return inner func = outer(func) result = func() print (result)''' 输出 before 我是一个func函数 after (11, 22, 33, 44) '''
8.2第二回合 python中支持特殊语法,在某个函数上方使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 def outer (origin ): def inner (): print ("before" ) res = origin() print ("after" ) return res return inner @outer def func (): print ("我是一个func函数" ) value = (11 ,22 ,33 ,44 ) return value reslut = func() print (reslut)''' before 我是一个func函数 after (11, 22, 33, 44) '''
8.3第三回合 更改需求,现在有三个fanc函数,func1,func2,func3,都在前面和后面分别输出before 和after
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 def outer (origin ): def inner (): print ("before" ) res = origin() print ("after" ) return res return inner @outer def func1 (): print ("我是一个func1函数" ) value = (11 ,22 ,33 ,44 ) return value @outer def func2 (): print ("我是一个func2函数" ) value = (11 ,22 ,33 ,44 ) return value @outer def func3 (): print ("我是一个func3函数" ) value = (11 ,22 ,33 ,44 ) return value
8.4支持优化n个参数 如果函数存在参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 def outer (origin ): def inner (*args,**kwargs ): print ("before" ) res = origin(*args,**kwargs) print ("after" ) return res return inner @outer def func (a1 ): print ("我是一个func函数" ) print (a1) value = (11 ,22 ,33 ,44 ) return value reslut = func(123 ) print (reslut)''' before 我是一个func函数 123 after (11, 22, 33, 44) '''
总结 装饰器实例
1 2 3 4 5 6 7 8 9 10 11 12 def outer (origin ): def inner (*args,**kwargs ): res = origin(*args,**kwargs) return res return inner @outer def func (a1 ): pass func()
在不修改原函数内部和调用方式的情况下,使用装饰器。
9.匿名函数 匿名函数,是基于lambda表达式实现定义一个可以没有名字的函数,例如:
1 2 data_list = [lambda x:x+100 ,lambda x:x+110 ,lambda ] print (data_list[0 ])
9.1定义 基于lambda定义的函数格式为:lambda 参数 :函数体
参数,支持任意参数
1 2 3 lambda x:函数体lambda x1,x2:函数体lambda *args,**kwargs:函数体
函数体,只能支持单行代码
返回值,默认将函数体单行代码执行的结果返回给执行函数的地方。
1 2 3 func = lambda x:x+100 v1 = func() print (v1)
10.生成器 生成器是由函数+yield关键字创造出来的写法,在特定情况下,用它可以帮助我们节省内存。
1 2 3 def func (): print (111 ) yield 1
执行生成器函数的时候,函数体默认不会被执行;返回的是一个生成器对象。
下面结合案例说明生成器函数的作用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 def func (): print ("1" ) yield 123 print ("2" ) yield 234 print ("3" ) yiled 345 v1 = func() print (v1)n1 = next (v1) print (n1)n1 = next (v1) print (n1)n1 = next (v1) print (n1)''' <generator object func at 0x000001F71903CD60> 1 执行第一次的结果 123返回第一个关键词后面的内容 2 执行第二次的结果 234 返回第二个关键词的内容 3 执行第三次的结果 345 返回第三个关键词的内容 '''
10.1应用场景 假设要让你生成300w个随即的四位数,并打印出来。
1.在内存中一次创建300w个。
2.动态创建,用一个创建一个。
1 2 3 import randomval = random.range (1000 ,9999 ) print (val)
1 2 3 4 5 6 import randomdata_list = [] for item in range (3000000 ): val = random.randint(1000 ,9999 ) data_list.append(val)
而使用生成器的话
1 2 3 4 5 6 7 8 9 10 import randomdef num (max_count ): counter = 0 while counter < max_count: yield random.randint(1000 ,9999 ) counter += 1 data_list = num(3000000 ) n1 = next (data_list) n2 = next (data_list)
所以在以后遇到,先生成再用,一边生成再用的时候,要想到生成器。
10.2基于for循环 1 2 3 data = func() for item in data: print (item)
11.推导式 推导式,是python中提供的一个非常方便的功能,可以让我们通过一行代码实现创建,list,dict,tuple,set的同时初始化一些值。
列表
1 2 3 num_list = [i for i in range (10 )] print (num_list)
字典
1 2 num_list = {i:i for in range (10 )} num_list2 = {i:(i,11 ) for i in range (10 )}
推导式的一个拓展(除去末尾的mp4)