python学习笔记第二模块 第二部分 : 函数(重点)
一、函数的作用、定义 以及语法
1、函数的作用
2、函数的语法和定义
函数:来源于数学,但是在编程中,函数这个概念与数学中大不相同,在不同的编程语言里,有不同的叫法。
在 python 里叫函数,是指将一个语句的集合,通过一个名字(函数名)封装起来,执行时,调用函数名即可
特点:
(1)减少重复代码(简洁)
(2)使程序变得可扩展(更广泛)
(3)让程序变得易维护(好维护)
3、函数的基本语法(pycharm实现)
例: #实现函数语法: def sayhi(): #注意,冒号和小括号一定要有不能忘记 print("hello world!") print("python好简单") print("学会挣大钱") print("hello my name is Guoyilong age :25") print(sayhi) #不带括号,会出现一个内存地址,也就是函数名的内存地址 sayhi() #直接写名字,执行的时候就会出现函数内容 执行后效果: <function sayhi at 0x00000286F8393EA0> #打印不带括号的函数名会出现函数的内存地址 hello world! python好简单 学会挣大钱 hello my name is Guoyilong age :25
4、函数内的参数(pycharm实现)
函数名的括号内,可以写进去很多参数,实现不同的功能。下面代码实现下简单参数的作用
例: #函数内参数 def sayhi(name): #括号里面的“name”就是参数,在这里设置什么,执行前就要穿进去对应的值 print("hello",name) print("hello my name is Lin Da age :25") sayhi("GuoYilong") #上边设置了参数,这边就要写进对应的值,否则会报错提示没有对应的值 #执行后: hello GuoYilong #填进去参数会自动生成 hello my name is Lin Da age :25
5、函数内简单的计算参数:
数学计算,也可以用函数来实现,而且利用函数,还可以实现自定制(输入不同参数,计算不同结果),
下面代码实现:
例: def calc(x,y): #定义两个参数 res = x**y print(res) calc(5,7) #需输入两个参数,输入参数不同,产生结果不同 calc(3,6) #参数作用就是产生一定程度的自定制 #执行后输出: 78125 729
以上就是函数的基本方法和函数内简单的参数实现方法
下面分析下它的几个特性
1、减少代码重复:这个一看就很明显,直接调用函数名,比之前挨个编写节省很多空间,很简洁。
2、使用程序变得可扩展:不用函数,要写很多重复的,而且容易出错,现在只需要写一个就可以全部实现,修改也方便
3、使程序变的易维护:方便维护,检索一个地方,不繁杂,好维护。
二、函数内的参数
定义:参数可以让函数更灵活啊,不只能做默认参数的事情,还可以根据调用时传入参数的不同,来决定函数内部的执行流程
参数 包括 形参变量 和 实参变量
1、形参变量:
形参,参数内部占位,只能在函数内部使用,函数调用结束,返回主函数后,就不能再使用该形参变量,
在被调用时才分配内存单元,调用结束后,即刻释放内存。(就像一个数学公式)
2、实参变量:
实参,就是实际的参数,真真正正存在的值,可以是常量、变量、表达式、函数、列表、字典等,无论实参是何种变量,
在函数调用时,都必须有确定的值,以便把这些值传给形参。因此应预先赋值或现实输入等办法使参数获得确定的值。(就像公式中的值)
下面代码验证下 :
形参
例: def calc(x,y): #定义两个参数,这就是形参(占位) res = x**y #就像是一个数学公式 print(res) #调用时分配内存单元,调用结束后即刻释放
实参
例: a = 4 #变量可以赋数字,也可以是列表,字典,字符串,函数等等很多类型 b = 7 calc(a,b) #括号内的 a 和 b 这就是实参 calc(b,a) #就像是公式中的具体值
3、默认参数
默认参数,就是有一个固定的值,如果不修改,他也会填进去,如果修改,就按修改后的填
下面代码举例说明:
例: def stu_register(name,age,country,course): #括号内为形参 print("---------注册学生信息——————") print("姓名",name) print("年龄",age) print("国籍",country) print("课程",course) stu_register("卿秀安",22,"CN","python") #括号内为实参 stu_register("灵千少",24,"CN","java") stu_register("张旭东",20,"CN","c+")
发现大部分学生信息国籍都是中国(CN),大部分都是国内的学生,那能不能把国籍默认写成中国,其他国家的也可另外修改。
这个就用到了默认参数的作用
下面我们代码实现:
例:
def stu_register(name,age,course,country = "CN"): #注意,默认参数赋值后,必须放在最后面,不能影响位置参数的排序 print("---------注册学生信息——————") print("姓名",name) print("年龄",age) #打印顺序就是位置参数,如果下面填写错误,电脑是分辨不出来的,只能按照填写的运行 print("国籍",country) print("课程",course) stu_register("卿秀安",22,"python") #一旦把默认参数放在没赋值的参数前面,系统会报错,而且会影响位置参数的显示 stu_register("灵少",24,"java") stu_register("张旭东",20,"c+")
以上两种代码写出来的效果都是一样的
---------注册学生信息—————— 姓名 卿秀安 年龄 22 国籍 CN 课程 python ---------注册学生信息—————— 姓名 灵少 年龄 24 国籍 CN 课程 java ---------注册学生信息—————— 姓名 张旭东 年龄 20 国籍 CN 课程 c+
4、关键参数
定义:正常情况下,给函数传参需要按照位置顺序,不想按照位置顺序就可以用关键参数。只需指定参数名即可(指定了参数名的参数就叫关键参数)
但是要求是,关键参数要放在位置参数(以位置顺序确定确定对应关系的参数)之后。
代码实现:
例: def stu_register(name,age,course,country = "CN"): print("---------注册学生信息——————") print("姓名",name) print("年龄",age) print("国籍",country) #位置参数是这样的 print("课程",course) stu_register("卿秀安",course="python",age=18,country=“JP”) #改变了位置参数的默认顺序,但是我都指定赋值,这就是主要参数
执行结果:
---------注册学生信息—————— 姓名 卿秀安 年龄 18 国籍 JP #发现位置没有改变,而且都对应 课程 python
5、函数--非固定参数
没有特殊的定义,举例说明下:
就拿刚讲的发报警的事情来说,之前给一个运维人员发报警,没什么问题,但是现在我的需求是给10个人甚至之后的更多个人发,
怎么实现这个需求呢?
代码来看:
例: #非固定参数1(元祖型) #发报警,1个人,是这样写的 def send_alert(msg,users): pass send_alert("赶紧招呼系统","Guo Yilong") #警报只发给一个人 #报警发给10个人,怎么办呢? def send_alert(msg,*args): #只需要在原有的基础上,给users前加个 * 符号(相当于给参数内定义一个元祖),
#就能实现把多个名字打包一起赋给users(前面加个 * 这个时候,users就属于元祖类型) for u in args: print("报警发送给",u) #用 for 循环,把元祖循环打印,发给不同的人,这样就可以实现给10个甚至更多个人发消息 #总结,如果参数里面出现了 *users传递的参数就可以不再是固定参数,而是传过来的所有参数都会打包成元祖发送给 *usres
#方法一: send_alert("赶紧回来,系统出问题了","Alex","Guo Yilong","Wangle","Lining","Msck","Tige") #有多少个人,就可以加多少个人 #方法二: send_alert("赶紧回来,系统出问题了",*["Alex","Guo Yilong","Wangle","Lining","Msck","Tige"]) #这样就相当于把 * 符号后面的列表自己打包,传给 *args #需要注意了:带 * 符号的参数放在所有参数最后,除了用主要参数去实现,否则会报错。
上面讲了用元祖类型的参数实现非固定应用
下面我们用字典类型的参数实现非固定参数的应用:
例: #非固定参数2(字典型) def func(name,*args,**kwargs): #参数前加一个 * 符号,属于元祖类型,加两个 ** 符号,就属于字典类型 print(name,args,kwargs) #我们先证明数据类型 func("Guoyilong",22,"Alex","peiqi", "shanshan") #传进去多个实参
#执行效果 Guoyilong (22, 'Alex', 'peiqi', 'shanshan') {} #发现除了name有对应的值,其他值都赋给了元祖,字典是空的,
发现字典是空的,是因为传进去的实参,既没有字典的特征,也没有定义字典
**kwaegs 这个参数接收的是未定义的关键字参数
下面我们改变方法实现:
#有字典特征,实现字典内写入实参: 例: def func(name,*args,**kwargs): print(name,args,kwargs) func("Guoyilong",22,"Alex",adds = "peiqi",tesla = "shanshan",mck = "Wanghua") #给实参内写入字典格式,通过判断 key ,来实现如果过有 去a , 没有 去b。
执行代码结果: Guoyilong (22, 'Alex') {'adds': 'peiqi', 'tesla': 'shanshan', 'mck': 'Wanghua'} #发现字典内已有写入的实参
或者直接定义字典(调用时,直接定义声明,这个就是传给字典结构的)
定义型: 例: def func(name,*args,**kwargs): print(name,args,kwargs) c = {"im":"Liuxin","se":"Qiqiang"} #把字典赋给变量 c func("qiqi","lingsen",**c) #在变量前加 **
执行代码结果: qiqi ('lingsen',) {'im': 'Liuxin', 'se': 'Qiqiang'} #成功实现了功能
三、函数 -- 返回值
函数在外部的代码想要获取函数的执行结果,就可以在函数里用 return 语句把函数内的执行结果返回。
需要注意的是:
1、函数在执行过程中,只要遇到 return 语句,就会停止并返回结果,也可以理解为,函数里只要遇见 return 语句,代表着函数的结束。(如果在函数缩进内的 retutrn 下面再写任何代码,都不会被执行)
2、如果未在函数中定义 feturn ,这个函数会返回值 None
注册信息的代码举例说明:
返回值的作用 例: def stu_register(name,age,course,country = "CN"): print("---------注册学生信息——————") print("姓名",name) print("年龄",age) print("国籍",country) print("课程",course) if age > 22: return False #返回值,一定要返回有用的东西 else : return True registriation = stu_register("灵少",24,"java") if registriation: print("注册成功") #返回值调用在函数外的使用 else : print("你太老了")
执行代码结果:
---------注册学生信息—————— 姓名 灵少 年龄 24 国籍 CN 课程 java 你太老了 #返回值得作用
不止能返回 False 和 True 这两个值,还可以返回列表,元祖等类型
返回元祖: 例: def stu_register(name, age, course, country="CN"): print("---------注册学生信息——————") print("姓名", name) print("年龄", age) print("国籍", country) print("课程", course) return name,age #如果要返回多个值,在句尾加逗号 registriation = stu_register("灵少",24,"java") print(registriation) #打印变量
执行代码结果: ---------注册学生信息—————— 姓名 灵少 年龄 24 国籍 CN 课程 java ('灵少', 24) #它会把输入的值变成元祖返回
返回列表类型: 例: def stu_register(name, age, course, country="CN"): print("---------注册学生信息——————") print("姓名", name) print("年龄", age) print("国籍", country) print("课程", course) return [name,age] #返回列表,只需要把返回值填进列表内,(多个值单词用逗号隔开) registriation = stu_register("灵少",24,"java") print(registriation) #打印变量
执行代码结果: 例: ---------注册学生信息—————— 姓名 灵少 年龄 24 国籍 CN 课程 java ['灵少', 24] #已返回列表
四、函数 -- 局部变量
函数内定义的变量,就叫局部变量,在函数内定义的变量,与函数外没任何关系
下面我们代码验证下:
局部变量: 例: name = "Guo Yilong","hello world!" #在函数外,把文字写成英文 def choice_name(): name = "郭毅龙","你好" #(局部变量)在函数内,把名字写成中文 print("内",name) #打印函数内名字 choice_name() print("外",name) #这函数内和函数外虽然变量名是相同的,但是他们是完全独立的,没有任何关系
执行代码结果: 内 ('郭毅龙', '你好') #函数内的值 外 ('Guo Yilong', 'hello world!') #函数外的值
下面更详细的验证下相同变量名的内存地址是否相同
例:
name = "Guo Yilong","hello world!" def choice_name(): name = "郭毅龙","你好" print("内",name,id(name)) choice_name() print("外",name,id(name))
执行代码结果: 内 ('郭毅龙', '你好') 2993864636857 外 ('Guo Yilong', 'hello world!') 2993864700936 #发现两个内存地址完全不同
还有一种情况就是,如果函数缩进内没有变量会调用外部变量(只能调用不可修改)
下面代码验证下:
优先级别:(由内而外) 例: name = "Guo Yilong","hello world!" def choice_name(): print(name,id(name)) #如果内部没有变量,会自动调用函数外变量 choice_name() #分优先级别,先调用函数缩进内变量,如果没有 ,会调用函数外变量 print("外",name,id(name))
执行代码结果: ('Guo Yilong', 'hello world!') 2176036502664 外 ('Guo Yilong', 'hello world!') 2176036502664 #这就叫由内而外,发现调用了外部变量
小结:
- 全局变量:定义在函数外部的一级代码变量,按照顺序全局都可调用。
- 局部变量:是定义在函数缩进内的变量,只能在函数内生效,函数执行结束,局部变量全部无效。
- 如果全局和局部都有同名变量,函数会查找变量顺序(由内而外)优先使用函数内部变量,如果相同级别有两个函数的同名变量,它们也是独立的,函数只会优先调用函数缩进内变量,如果没有回调用全局变量,与另外一个同级别函数无关
- 函数内部调用全局变量,不可修改,如需修改,需重新定义变量或创建
- 函数内可以引用全局变量,但是全局不可以调用局部变量
五、在函数内修改全局变量
在实际应用中,不建议在函数内修改全局变量,但如果非要修改,也还是有方法的( global ),所以还是不建议在函数内修改全局变量,因为如果养成恶习,在以后涉及 代码量大的 时候,函数内修改全局变量会导致不好调试和维护,带来很多麻烦
代码讲一下方法:
修改全局变量: 例; name = "Guo Yilong","hello world!" def choice_name(): global name #新语法(意思是告诉python程序要修改全局变量、列表还有其他类型的变量) name = "郭毅龙","你好" #把变量重新修改成汉语(也可是列表、元祖、字典等等其他类型)(注意顺序,先声明,再修改) age = 52 print("内",name) choice_name() print("外",name)
执行代码效果: 内 ('郭毅龙', '你好') 外 ('郭毅龙', '你好') #发现都改掉了
在函数内,不只能修改字符串变量,也可修改列表、函数等等很多变量
下面我们看修改列表变量的方法
先尝试普通方法能否修改:
普通方法: 例: names = ["Alex","peiqi","liudehua","meishu"] #先设定变量 def change_names(): names = ["Guoyilong","Meiliang","Alex"] #尝试直接改是否可行 print(names) change_names() print(names) #打印各层
执行代码结果: ['Guoyilong', 'Meiliang', 'Alex'] ['Alex', 'peiqi', 'liudehua', 'meishu'] #发现并没有修改
因为之前讲过,列表、其他类型套列表都是不可更改整个列表的内存地址的,(当然除了 global 这个方法 )但列表内的值是可以修改的,
下面代码实现下:
修改列表内元素: 例: names = ["Alex","peiqi","liudehua","meishu"] def change_names(): del names[3] #修改和删除列表内的元素 names[2] = "Guoyilong" print(names) change_names() print(names)
执行代码结果: ['Alex', 'peiqi', 'Guoyilong'] ['Alex', 'peiqi', 'Guoyilong'] #发现已经修改成功,验证了上边的话
注意:所有包含很多字符串元素的内部值,都是可以通过这样方法实现修改,唯一不能修改的是:字符串和数字(因为本事就是个内存地址,没办法修改)
六、函数--嵌套函数
元祖、列表、字典等类型可以嵌套,函数类型也可嵌套(利用缩进)
下面代码执行下:
嵌套函数: 例: age = 22 #设定全局变量 def funt1(): age = 35 #设定各层级局部变量 print(age) def funt2(): #内部函数可以再次定义函数 age = 48 print(age) #每一层都互不干扰 def funt3(): age = 77 print(age) funt3() funt2() #注意缩进 funt1() #执行时,需被调用,否则,不会执行
执行代码结果: 35 #发现嵌套内代码 全部执行 48 77
下面分析几个情况,把变量放在不同位置,看执行哪里变量
#换位置 age = 22 def funt1(): print(age) def funt2(): age = 35 print(age) def funt3(): print(age) #这种情况会执行那个变量 funt3() funt2() funt1()
代码输出结果: 22 #发现外边函数调用的是全局变量 35 #发现内2套函数调用的是局部变量 35 #这就总结了局部变量的调用方法,它会由最内部逐层、逐级依次向外查找变量,找到为止
六、函数 -- 作用域
定义:在 python 中,一个函数就是一个作用域,所有的局部变量,就是放置在局部变量内,代码定义完成后,作用域就已生成,调用时,就会根据作用域嵌 套关系(也可称为作用域链)由内而外层层往上找。
下面我们代码验证下:
作用域 例: 第一
age = 18 def funt1(): age = 22 def funt2(): print(age) return 666 #设置返回值 val = funt1() print(val)
666 #被执行会会是 return 返回值,而不是其他
第二 age = 18 def funt1(): age = 22 def funt2(): print(age) return funt2 #设置不带括号的函数名 val = funt1() print(val)
执行代码结果; <function funt1.<locals>.funt2 at 0x00000259AE603F28> #返回 funt2 的内存地址,
#理下思路:val = funt1(),
而 funt1()设置的返回值 return 返回的是funt2 的内存地址 (函数名不加括号返回函数本身的内存地址)
这就证明,其实 val = funt2 (加了括号会返回funt2 函数代码的执行结果)
第三
age = 18 def funt1(): age = 22 def funt2(): print(age) return funt2 val = funt1() val() #现在把val 加上括号看是否与 val = funt2()相同
执行代码结果: 22 #和 funt2() 效果相同
七、函数--匿名函数
匿名函数,顾名思义就是没起名字的函数,定义一个函数不用名字,下面先和正常函数对比下
匿名函数: 例: #先写一个正常的函数 def calc(x,y): return x*y #再看匿名函数 lambda x,y:x*y #这句话叫声明一个匿名函数, #这个公式就和上边的正常函数一个效果,但是如果使用的话还是要定义一个变量 func = lambda x,y:x*y 下面验证下执行结果 print(calc(7,8) ) #执行正常函数 print(func(7,8) ) #执行匿名函数
执行代码结果: 56 56 #发现执行结果相同,说明作用相同
那么,匿名函数还能做什么,下下面我们再来看:
例: #下面写一个判断 def calc(x,y): if x < y: return x*y else : return x/y #正常函数判断是没错的,但是匿名函数内是不可以写这样复杂的判断的 func = lambda x,y:x*y if x<y else x/y #但是匿名函数可以用三元运算 print(calc(16,4)) print(func(16,4))
执行代码结果: 4.0 4.0 #执行结果也相同,其实只看上面觉得匿名函数还是要赋变量名,看着还很模糊,还不如用正常的函数
#这只是在普通场景下,在很多场景下,匿名函数不是单独使用而是搭配其他语法使用
匿名函数的搭配使用:
例: #匿名函数最常用的是搭配其他语句的使用 #下面有一个列表,需求就是把列表的每一个值自乘(1*1,2*2,3*3....循环之) data = list(range(10))
1、用普通的方法(for 循环)
for index,i in enumerate(data): data[index] = i*i #比较麻烦
map()
#新语法 :括号内可以放2个值(函数,数据集合)相当于会把数据结合内的每一个值当做一个参数,交给函数执行一遍
2、用普通的函数实现map的功能 data = list(range(10)) print(data) #先预设一个普通函数: def func(n): return n*n map(func,data)#简单的这样就是可以取到值 f = list(map(func,data))# 但是传进去的是列表所以要打印列表格式 print(f)
3、用 lambda 函数(匿名函数)实现 data = list(range(10)) print(data) print(list(map(lambda x :x*x,data)))
****以上代码执行结果都是一样的***** #lambda 后面就是匿名函数,我再这里使用之后就不再调用,所以只在这一个地方起作用就可以了
*** 匿名函数小结:1、节省代码量 2、看着高级
八、函数--高阶函数
定义:变量可以指向函数,函数的参数可以接收变量,那么一个函数就可以接收另一个函数作为参数,而这种函数就称为高阶函数。
变量可以指向函数:
例: f = lambda x: x*x #变量可以指向函数 print(f(8)) def calc(x,y): #变量可以指向函数 return x/y s = calc print(s(8,4))
函数的参数可以接收变量:
例 : def calc(x,y): #设置正常函数 return x+y def func(x): return x*x n = calc #函数可设置成变量 a = 7 b = 13 print(n(a,b)) #函数的参数可以设置成变量
这就相当于一个函数的参数可以设置成另外一个函数(一个函数就可以接收另一个函数作为参数):
例: def calc(x,y): #设置一个函数 return x+y def func(x): #设置另一个参数 return x f = func(calc) #相当于第一个函数被第二个函数当做参数执行, print(f(7,5)) #因为第一个函数被当作参数,所以要传进去2个值
以上这种函数结构就叫高阶函数
还有另一种方法:(把函数当作一个参数返回)
例: def calc(x,y): #设置一个函数 return x+y def func(): return calc(7,5) #把上一个函数当做函数的返回值 print(func())
绝对值函数:
例: def func2(x,y): #设置一个函数 return abs(x+y) # abs() 新语法:绝对值,就是负数绝对值后成正数返回(abs()是参数) print(func2(-5,-8))
执行代码结果: 13 #已经当正数返回
例: def func2(x,y): #再设置一个参数 return abs,x,y # abs() 新语法:绝对值,就是不管正负数,都会返回正数 res = func2(7,-11) #只要函数内包含另一个函数,就被称为高阶函数 print(res)
执行代码结果: (<built-in function abs>, 7, -11) #只要执行的函数包含另一个函数就叫高阶函数
高阶函数查看执行结果:
例: def func2(x,y): return abs,x,y res = func2(-3,-11) print(res[0](res[1]+res[2])) #函数也是以索引为导向提取对应的值,注意括号外的是函数名,括号内的值为函数的参数
执行代码结果: 14 #这就是告诫函数,和绝对值的作用
小结:只需满足下面任意条件即是高阶函数
1、接收一个或者多个函数作为输入(参数)
2、return 返回另一个函数
———————————————————结束线———————————————————