Python--5 函数
5.1 函数介绍
所谓函数, 就是把具有独立功能的代码块组织为一个小役块, 在需要的时候调用 .函数的使用包含两个步骤:
1. 定义函数--封装独立的功能
2. 调用函数--享受封装的成果
.函数的作用, 在开发程序时 , 使用函数可以提高编写的效率以及代码的重用
函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。
函数能提高应用的模块性,和代码的重复利用率。你已经知道python提供了许多内建函数,比如print()。但你也可以自己创建函数,这被叫做用户自定义函数。
5.2 函数定义调用
函数分为库函数和自定义函数。
1、定义函数
定义一个由自己想要功能的函数,以下是简单的规则:
- 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号()。
- 任何传入参数和自变量必须放在圆括号中间。圆括号之间可以用于定义参数。
- 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
- 函数内容以冒号起始,并且缩进。
- return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 none。
语法
def functionname( parameters ):
"函数_文档字符串"
function_suite
return [expression]
示例1:打印三角形
#1. 写出三角形的打印代码并测试 #print(" *") #print(" ***") #print("*****") #2. 定义一个函数,将测试完的打印代码屏蔽 def printsanjiaoxing(): print(" *") print(" ***") print("*****") #3. 调用函数 # 注意:定义一个函数默认不会执行,只能通过调用的方法执行 printsanjiaoxing() [root@localhost 04-day]# python 4-1.py * *** *****
2、函数调用
函数调用
定义一个函数只给了函数一个名称,指定了函数里包含的参数,和代码块结构。
这个函数的基本结构完成以后,你可以通过另一个函数调用执行,也可以直接从python提示符执行。
5.3 函数文档说明
pycharm的调试工具:
f8 step over可以单步执行代码,会把函数调用看作是一行代码直接执行
f7 step into可以单步执行代码,如果是函数会进入函数内部
在开发中 如果希望给函数添加注释,应该在定义函数的下方,使用 连续的三对引号在连续的三对引号之间编写对函数的说明文字
在函数调用位置,使用快捷键ctrl + q可以查看函数的说明信息
注意:因为函数体相对比较独立函数定义的上方应该和其他代码(包括注释)深留两个空行
>>> def printinfo(): ... "此函数就是打印hello world" ... print("hello world") ... >>> >>> printinfo() hello world >>> help(printinfo) help on function printinfo in module __main__: printinfo() 此函数就是打印hello world (end)
函数定义的首行字符串即为函数说明文档
5.4 函数参数
参数的作用:
函数, 把具有独立功能的代码块组织为一个小悛块, 在需要的时候调用
函数的参数增加函数的通用性.针对相同的数据处理逻辑, 能够适应更多的数据
1. 在函数内部, 把参数当做变量 使用, 进行需要的数据处理
2. 函数调用时, 按照函数定义的参数顺序, 把希望在函数内部处理的数据, 通过参数传递
.形参:定义函数时 ,小括号中的参数,是用来接收参数用的, 在函数内部作为变量使用
.实参:调用函数时 ,小括号中的参数,是用来把数据传递到函数内部用的
调用函数时可使用的正式参数类型:
必备参数
命名参数
缺省参数
不定长参数
1、必备参数
必备参数须以正确的顺序传入函数。调用时的数量必须和声明时的一样。
调用printstr()函数,你必须传入一个参数,不然会出现语法错误:
>>> def printstr(string): ... "打印任何传入的字符串" ... print(string) ... >>> >>> printstr() traceback (most recent call last): file "<stdin>", line 1, in <module> typeerror: printstr() missing 1 required positional argument: 'string' >>> printstr("hello world") hello world
2、命名参数
命名参数和函数调用关系紧密,调用方用参数的命名确定传入的参数值。你可以跳过不传的参数或者乱序传参,因为python解释器能够用参数名匹配参数值。
>>> def printstr(string): ... "打印任何传入的字符串" ... print(string) ... >>> >>> printstr(string="hello world") hello world >>>
命名参数的顺序不重要,如下例
>>> def printinfo(name,age): ... "打印任何传入的字符串" ... print("name: ",name) ... print("age: ",age) ... >>> >>> printinfo(age=60,name="成龙") name: 成龙 age: 60
3、缺省参数
调用函数时,缺省参数的值如果没有传入,则被认为是默认值。
>>> def printinfo(name,age = 25): ... "打印任何传入的字符串" ... print("name: ",name) ... print("age: ",age) ... >>> >>> printinfo(age=60,name="成龙") name: 成龙 age: 60 >>> printinfo(name="小张") name: 小张 age: 25
4、不定长参数
可能需要一个函数能处理比当初声明时更多的参数。这些参数叫做不定长参数,和上述2种参数不同,声明时不会命名。基本语法如下:
def functionname([formal_args,] *var_args_tuple ): "函数_文档字符串" function_suite return [expression]
加了星号(*)的变量名会存放所有未命名的变量参数。选择不传参数也可。如下实例:
>>> def printinfo(arg1,*vartuple): ... "打印任何传入的参数" ... print("输出:") ... print(arg1) ... for var in vartuple: ... print(var) ... >>> >>> printinfo(10) 输出: 10 >>> printinfo(20,30,40) 输出: 20 30 40
不定长参数也称为动态参数,一个*:传入的参数将以元组形式呈现,两个**:传入参数将以字典形式呈现,但传参数以key=value形式。
def func(*args): print args # 执行方式一 func(11,33,4,4454,5) # 执行方式二 li = [11,2,2,3,3,4,54] func(*li)
def func(**kwargs): print args # 执行方式一 func(name='tom',age=18) # 执行方式二 li = {'name':'tom', age:18, 'gender':'male'} func(**li)
import smtplib from email.header import header from email.mime.text import mimetext # 第三方 smtp 服务 mail_host = "smtp.163.com" # smtp服务器 mail_user = "esen_monitor@163.com" # 用户名 mail_pass = "授权密码" # 授权密码,非登录密码 sender = 'esen_monitor@163.com' # 发件人邮箱(最好写全, 不然会失败) receivers = ['esen_monitor@tom.com'] # 接收邮件,可设置为你的qq邮箱或者其他邮箱 content = '我用python' title = '人生苦短' # 邮件主题 def sendemail(): message = mimetext(content, 'plain', 'utf-8') # 内容, 格式, 编码 message['from'] = "{}".format(sender) message['to'] = ",".join(receivers) message['subject'] = title try: smtpobj = smtplib.smtp_ssl(mail_host, 465) # 启用ssl发信, 端口一般是465 smtpobj.login(mail_user, mail_pass) # 登录验证 smtpobj.sendmail(sender, receivers, message.as_string()) # 发送 print("mail has been send successfully.") except smtplib.smtpexception as e: print(e) if __name__ == '__main__': sendemail()
接收到邮件:
5.5 函数返回值
在程序开发中, 有时候, 会希望 一个函数执行结束后, 告诉调用者一个结果 , 以便调用者针对具体的结果做后续的处理
.返回值是函数完成工作后, 最后给调用者的一个结果
.在函数中使用return关键字可以返回结果
.调用函数一方, 可以使用变量来接收函数的 返回结果
python 函数返回值有两种形式:
1 返回一个值。
2 返回多个值。
return语句[表达式]退出函数,选择性地向调用方返回一个表达式。不带参数值的return语句返回none。
>>> def firstvalue(a,b): ... c = a + b ... return c ... >>> >>> print(firstvalue(1,2)) 3
>>> def secondvalue(a,b): ... c = a + b ... return (a,b,c) ... >>> x,y,z = secondvalue(1,2) >>> print("x: ",x,"y: ",y,"z: ",z) x: 1 y: 2 z: 3
5.6 函数类型
1.无参数,无返回值的函数
此类函数,不接受参数,也没有返回值,一般用于打印提示等类型。
>>> def printmenu(): ... print('--------------------') ... print(' xx涮涮锅 点菜系统') ... print('') ... print(' 羊肉涮涮锅 ') ... print(' 牛肉涮涮锅 ') ... print('--------------------') ... >>> printmenu() -------------------- xx涮涮锅 点菜系统 羊肉涮涮锅 牛肉涮涮锅 --------------------
2.没有参数,有返回值的函数
打印用户输入的选择
[root@localhost 04-day]# vim 4-4.py #!/usr/bin/env python def getinput(): name = input("请输入你的名字: ") return name choice = getinput() print("你的名字是:%s"%choice) [root@localhost 04-day]# python 4-4.py 请输入你的名字: tom 你的名字是:tom
3.有参数,没有返回值
计算1到指定数的累加和
#!/usr/bin/env python def sumnums(num): i=1;sum=0 while i<=num: sum +=i i+=1 print("1+2+...+num=%d"%sum) sumnums(100) [root@localhost 04-day]# python 4-2.py 1+2+...+num=5050
4.有参数,有返回值
[root@localhost 04-day]# vim 4-3.py #!/usr/bin/env python def sumnums(num): i=1;sum=0 while i<=num: sum +=i i+=1 return sum result = sumnums(100) print("-"*30) print("累加和为:%d"%result) print("-"*30) [root@localhost 04-day]# python 4-3.py ------------------------------ 累加和为:5050 ------------------------------
5.7 函数嵌套
嵌套函数,顾名思义就是函数里面套函数。
[root@localhost 04-day]# vim 4-5.py #!/usr/bin/env python def a(a): print("i am a") def b(b): print("a+b=",a+b) print("i am b") b(2) print("over!!!") a(3) [root@localhost 04-day]# python 4-5.py i am a a+b= 5 i am b over!!!
写一个函数打印一条横线
[root@localhost 04-day]# vim 4-6.py #!/usr/bin/env python def printline(): print("-"*30) printline() [root@localhost 04-day]# python 4-6.py ------------------------------
新的需求:根据用户需要打印n条线
#!/usr/bin/env python def printlinei_1(): print("-"*30) #1. 方法一:直接修改原函数 def printline(n): i=0 while i<n: print("-"*30) i+=1 #方法一虽然可以实现,但不可取,如果其他地方已经调用原函数,那么也将发生改变 #2. 方法二:重新写一个新函数 def printline2(n): i=0 while i<n: printlinei_1() i+=1 num = int(input("请输入要打印的行数:")) printline(num) printline2(num)
5.8 函数变量作用域
程序中的所有变量可能不会在该程序中的所有位置进行访问。这取决于所声明的变量。
变量的作用域确定了程序,可以访问一个特定的标识符的一部分。在python中的变量两个基本范畴:
全局变量
局部变量
局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。调用函数时,所有在函数内声明的变量名称都将被加入到作用域中。
局部变量
局部变量,就是在函数内部定义的变量
•不同的函数,可以定义相同的名字的局部变量,但是各用个的不会产生影响
•局部变量的作用,为了临时保存数据需要在函数中定义变量来进行存储,这就是它的作用
全局变量:如果一个变量,既能在一个函数中使用,也能在其他的函数中使用,这样的变量就是 全局变量
总结1:
•在函数外边定义的变量叫做 全局变量
•全局变量能够在所有的函数中进行访问
•如果在函数中修改全局变量,那么就需要使用 global 进行声明,否则出错
•如果全局变量的名字和局部变量的名字相同,那么使用的是局部变量的,小技巧 强龙不压地头
#可变类型的局部变量 >>> a = 1 >>> def f(): ... a += 1 ... print a ... >>> f() traceback (most recent call last): file "<stdin>", line 1, in <module> file "<stdin>", line 2, in f unboundlocalerror: local variable 'a' referenced before assignment >>> >>> >>> li = [1,] >>> def f2(): ... li.append(1) ... print li ... >>> f2() [1, 1] >>> li [1, 1]
总结2:
•在函数中不使用global声明全局变量时不能修改全局变量的本质是不能修改全局变量的指向,即不能将全局变量指向新的数据。
•对于不可变类型的全局变量来说,因其指向的数据不能修改,所以不使用global时无法修改全局变量。
•对于可变类型的全局变量来说,因其指向的数据可以修改,所以不使用global时也可修改全局变量。
5.9 递归函数
1.递归的定义
# 一个函数直接或者间接调用自己,那么这个函数就称为递归函数。 # 那这个函数如果一直自己调用自己,是不是会一直处于死循环状态,答案是不会,因为python为了杜绝此类现象,强制的将递归层数控制在了997层。 # 为了证实上面的说法,定义一个函数试试看
count = 0 def story(): global count count += 1 print(count) story() story() # 运行 c:\users\kongd>python e:\python\day4\digui.py 1 2 ... 997 traceback (most recent call last): file "e:\python\day4\digui.py", line 8, in <module> story() file "e:\python\day4\digui.py", line 6, in story story() file "e:\python\day4\digui.py", line 6, in story story() file "e:\python\day4\digui.py", line 6, in story story() [previous line repeated 992 more times] file "e:\python\day4\digui.py", line 5, in story print(count) recursionerror: maximum recursion depth exceeded while calling a python object
# 当前我也可以通过引用模块,打破这个限制,如下操作: import sys sys.setrecursionlimit(1000000) count = 0 def story(): global count count += 1 print(count) story() story() '''结果: 3220 我的笔记本性能比较渣,只能打印到 3220 '''
# 1. 必须有一个明确的结束条件
# 2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少
# 3. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,
# 每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)
3.递归的例子
# 举个简单的例子,我们来计算阶乘 n! = 1 * 2 * 3 * .....
def func(n):
if n == 1:
return n
elif n > 1:
return n * func(n - 1)
else:
return '请传递大于0的参数'
print(func(5))
5.10 匿名函数
没有名字的函数,不用写return
,返回值就是该表达式的结果。
语法:lambda 参数:方法(或三元运算)
lambda x:x*x等同于
def f(x): return x * x
如下实例:
sum = lambda arg1, arg2: arg1 + arg2
# 调用sum函数
print("value of total : ", sum(10, 20))
print("value of total : ", sum(20, 20))
以上实例输出结果:
value of total : 30
value of total : 40
lambda函数能接收任何数量的参数但只能返回一个表达式的值
匿名函数不能直接调用print,因为lambda需要一个表达式
应用场合
函数作为参数传递
- 自己定义函数
>>> def fun(a, b, opt): ... print("a =", a) ... print("b =", b) ... print("result =", opt(a, b)) ... >>> fun(1, 2, lambda x,y:x+y) a = 1 b = 2 result = 3
2.作为内置函数的参数
想一想,下面的数据如何指定按age或name排序?
stus = [ {"name":"zhangsan", "age":18}, {"name":"lisi", "age":19}, {"name":"wangwu", "age":17} ] 按name排序: >>> stus.sort(key = lambda x:x['name']) >>> stus [{'age': 19, 'name': 'lisi'}, {'age': 17, 'name': 'wangwu'}, {'age': 18, 'name': 'zhangsan'}] 按age排序: >>> stus.sort(key = lambda x:x['age']) >>> stus [{'age': 17, 'name': 'wangwu'}, {'age': 18, 'name': 'zhangsan'}, {'age': 19, 'name': 'lisi'}]
补充:
三元运算(三目运算),是对简单的条件语句的缩写。
# 书写格式
result = 值1 if 条件 else 值2
# 如果条件成立,那么将 “值1” 赋值给result变量,否则,将“值2”赋值给result变量
eg: 两个数比较
1、使用if双分支
>>> a=5
>>> b=8
>>> if a>b:
... max=a
... else:
... max=b
...
>>> max
8
2、使用三元运算
>>> max = a if a>b else b
>>> max
8
作业
1、简述普通参数、指定参数、默认参数、动态参数的区别
2、写函数,计算传入字符串中【数字】、【字母】、【空格] 以及 【其他】的个数
3、写函数,判断用户传入的对象(字符串、列表、元组)长度是否大于5。
4、写函数,检查用户传入的对象(字符串、列表、元组)的每一个元素是否含有空内容。
5、写函数,检查传入列表的长度,如果大于2,那么仅保留前两个长度的内容,并将新内容返回给调用者。
6、写函数,检查获取传入列表或元组对象的所有奇数位索引对应的元素,并将其作为新列表返回给调用者。
7、写函数,利用递归获取斐波那契数列中的第 10 个数,并将该值返回给调用者。
8、请用函数实现一个判断用户输入的年份是否是闰年的程序
提示:
1.能被400整除的年份
2.能被4整除,但是不能被100整除的年份
以上2种方法满足一种即为闰年