第三章 函数
def hello(): #1 print('Howdy!') print('Howdy!!!') print('hello there!') hello() #3 hello() hello()
#def语句定义了一个名为hello()函数。def语句之后的代码块是函数体,这段代码在函数调用时执行,3处的是函数的调用。
返回值和return语句
一般来说,函数调用求值得结果,称为函数的“返回值”。用def语句创建函数时,可以使用return语句指定应该返回什么值。
return语句组成
- return关键字
- 函数应该返回的值或表达式
如果在return语句中使用了表达式,返回值就是该表达式求值得结果.
None值
在Python中有一个值称为None,他表示没有值。None是NoneType数据类型的唯一值。N必须大写
print()函数在屏幕上显示文本,但它不需要返回任何值,返回值为None,这和len()或input()不同。
在幕后,对于所有没有return语句的函数定义,Python都会在末尾加上return None。这类似于while或for循环隐式的以continue语句结尾。而且,如果使用不带值的return语句(也就是只有关键字本身),那么就返回None
关键字参数和print()
大多数参数是由它们在函数调用中的位置来识别的。但是,"关键字参数"是由函数调用时加在它们前面的关键字来识别的。关键字参数通常用于可选变元。
print('hello') print('world') #输出 hello world #这2个字符串出现在独立的2行中,因为print()函数自动在传入的字符串末尾添加了换行符。但是,可以设置end关键字参数,将它变成另一个字符串。例如: print('hello',end='') print('world') #输出 hello world ##输出被打印在一行中,因为在'hello'后面不再打印换行,而是打印了一个空字符串,如果需要禁用加到每一个print()函数调用末尾的换行,这就很有用。 >>>print('zabbix','rabbit','dog') # 如果向print()传入多个字符串值,该函数就会自动用一个空格分隔它们。 zabbix rabbit dog >>>print('zabbix','rabbit','dog',sep=',') #当传入sep关键字参数,替换掉默认的分隔字符串。 zabbix,rabbit,dog
局部和全局作用域
- 在被调用函数内赋值的变元和变量,处于该函数的"局部作用域"。处于局部作用域的变量,称为"局部变量"
- 一个函数被调用时,就创建了一个局部作用域。在这个函数内的所有变量,存在于该局部作用域内。该函数返回时,这个局部作用域就被销毁了,这些变量就丢失了。下次调用这个函数,局部变量不会记得改函数上次被调用时它们保存的值。
2.在所有函数之外赋值的变量,属于"全局作用域"处于全局作用域的变量,称为"全部变量"
特点:
- 一个变量必是其中一种,不能既是局部的又是全局的!
- 全局作用域中的代码不能使用任何局部变量
def spam(): eggs = 33878 spam() print(eggs) #输出 Traceback (most recent call last): File "C:/Users/huwei/PycharmProjects/python_Reading/1.py", line 7, in <module> print(eggs) NameError: name 'eggs' is not defined
- 但是局部作用域可以访问全局变量(也就是说全局变量可以在局部作用域中读取)
def spam(): print(eggs) eggs =42 spam() print(eggs) #输出 42 42
- 一个函数的局部作用域中的代码,不能使用其他局部作用域中的变量
def spam(): eggs = 99 bacon() print(eggs) def bacon(): ham = 101 eggs = 0 spam() #输出 99
- 如果在不同的作用域中,你可以用相同的名字命名不同的变量。
def spam(): eggs = 'spam local' print(eggs) #prints 'spam local' def bacon(): eggs = 'bacon local' print(eggs) #prints 'bacon local' spam() print(eggs) #prints 'bacon local' eggs = 'global' bacon() print(eggs) #prints 'global' #输出 bacon local spam local bacon local global
global语句
如果需要在一个函数内修改全局变量,就使用global语句。
def spam(): global eggs eggs = 'spam' eggs = 'global' spam() print(eggs) #输出 spam #因为eggs在spam()的顶部被声明为global,所以当eggs被赋值为'spam'时,赋值发生在全局作用域的spam上。没有创建局部spam变量。
如何区分一个变量是处于局部作用域还是全局作用域?
def spam(): global eggs #(1) eggs = 'spam' # this is the global,eggs是全局eggs变量,因为在函数的开始处,有针对eggs变量的global语句。 def bacon(): eggs = 'bacon' #(2) this is a local,eggs是局部变量,因为在该函数中有针对它的赋值语句。 def ham(): print(eggs) #(3) this is the global,eggs是全局变量,因为在这个函数中,既没有赋值语句,也没有针对它的global语句。 eggs = 42 #this is the global spam() print(eggs) #输出 spam
在一个函数中,如果试图在局部变量赋值之前就使用它,Python就会报错!
def spam(): print(eggs) #ERROR eggs = 'spam local' eggs = 'global' spam() #输出 Traceback (most recent call last): File "C:/Users/huwei/PycharmProjects/python_Reading/1.py", line 9, in <module> spam() File "C:/Users/huwei/PycharmProjects/python_Reading/1.py", line 2, in spam print(eggs) # ERROR UnboundLocalError: local variable 'eggs' referenced before assignment #发生这个错误是因为,Python看到spam()函数中有针对eggs的赋值语句,因此认为eggs变量是局部变量。但是因为print(eggs)的执行在eggs赋值之前,局部变量eggs并不存在。Python不会退回到使用全局eggs变量。
异常处理
def spam(divideBy): return 42 / divideBy print(spam(2)) print(spam(12)) print(spam(0)) print(spam(1)) 执行结果: 21.0 3.5 Traceback (most recent call last): File "C:/Users/huwei/PycharmProjects/python_Reading/1.py", line 9, in <module> print(spam(0)) File "C:/Users/huwei/PycharmProjects/python_Reading/1.py", line 2, in spam return 42 / divideBy ZeroDivisionError: division by zero #当试图用一个数除以0时,就会发生ZeroDivisionError #解决方法:错误可以由try和except语句来处理。那些可能出错的语句被放在try子句中。如果错误发生,程序执行就转到接下来的except子句开始处。 def spam(divideBy): try: return 42 / divideBy except ZeroDivisionError: print('Error: Invalid argument!') print(spam(2)) print(spam(12)) print(spam(0)) print(spam(1)) #输出 21.0 3.5 Error: Invalid argument! None 42.0
如果在try子句中的代码导致一个错误,程序执行就立即转到except子句的代码。在运行那些代码之后,执行照常继续。
练习:一个小程序,猜数字
I am thinking of a number between 1 and 20.
take a guess.
10
your guess is too low.
take a guess.
15
your guess is too low.
take a guess.
17
your guess is too high.
take a guess.
16
Good Job! You guessed my number in 4 guesses!
import random def guess(): count = 0 print('I am thinking of a number between 1 and 20.') answer_number = random.randint(1,20) while True: print('take a guess.') guess_number = int(input()) count +=1 if guess_number < answer_number: print('your guess is too low.') elif guess_number > answer_number: print('your guess is too high.') elif guess_number == answer_number: print('Good Job! You guessed my number in %d guesses!'%count) break guess()
习题:
1.为什么在程序中加入函数会有好处。
答:函数是代码逻辑分组的主要方式,函数可以帮助你组织代码,因为函数中的变量存在于他们自己的局部作用域内,所以一个函数中的代码不能直接影响其他函数中变量的值。这限制了哪些代码才能改变变量的值,对于代码调试时很有帮助的。
正确答案:函数减少了重复代码。这让程序更短,更容易阅读,更容易修改。
2.函数中的代码何时执行:在函数被定义时,还是在函数被调用时?
答:函数中的代码只有在函数被调用时才会被执行。
3.什么语句创建一个函数?
答:def语句创建了一个函数
4.一个函数和一次函数调用有什么区别?
答:函数是一个开头为def的语句,它包含函数名和函数体。如def hello()
而函数之后的hello()是一次函数调用,只有当代码执行到hello()时,才会调用函数def hello()下面的函数体。
5.Python程序中有多少全局作用域?有多少局部作用域?
答:多个局部作用域,一个全局作用域
正确答案:在调用一个函数时,创建了一个全局作用域和一个局部作用域
6.当函数调用返回时,局部作用域中的变量发生了什么?
答:当函数调用返回时,局部作用域中的变量被释放/销毁
7.什么返回值?返回值可以作为表达式的一部分吗?
答:返回值是函数调用求值的结果,称为函数的“返回值”如用def语句创建函数时,可以用return语句指定应该返回什么值。
如:
return 3或x+1
分两种情况:1.如果在rerurn语句中使用了表达式,返回值就是该表达式求值的结果。2.可以直接返回一个值
8.如果函数没有返回语句,对它调用的返回值是什么?
答:如果函数没有返回语句,对它调用的返回值就是None,如print()语句
9.如何强制函数中的一个变量指的是全局变量?
答:使用global关键字,强制变量为全局变量
10.None的数据类型是什么?
答:None的数据类型就是NoneType
11.import areallyourpetsnamederic语句做了什么?
答:import语句导入了 areallyourpetsnamederic模块
12.如果在名为spam的模块中,有一个名为bacon()函数,在引入spam后,如何调用它?
答:该函数可以使用spam.bacon()调用
13.如何防止程序在遇到错误时崩溃?
答:使用try except语句
14.try子句中发生了什么?except子句中发生了什么?
答:如果try子句中的代码导致了一个错误,程序执行就立即转到except子句的代码。在运行那些代码之后,执行照常继续。
实践项目
作为实践,请编写程序完成以下任务
1.Collatz序列
编写一个名为collatz()的函数,它有一个名为number的参数,如果参数是偶数,那么collatz()就打印出number//2,并返回该值。如果number是奇数,collatz()就打印并返回3*number+1
然后编写一个程序,让用户输入一个整数,并不断对这个数调用collatz(),直到函数返回值1!(令人惊奇的是,这个序列对于任何整数都有效,利用这个序列,你迟早会得到1!既是数学家也不能确定为什么。你的程序在研究所谓的“Collatz序列”,它有时候被称为“最简单、不可能的数学问题”
def collatz(number): if number % 2 == 0: return number // 2 elif number % 2 == 1: return 3 * number + 1 while True: type_number = int(input('Pls input your number:')) correct_number = collatz(type_number) print(correct_number) if correct_number == 1: break
输入验证
在前面的项目中添加try和except语句,检测用户是否输入了一个非整数的字符串。正常情况下,int()函数在传入一个非整数字符串时,会产生ValueError错误,比如int('puppy')。在except子句中,向用户输出一条信息,告诉他们必须输入一个整数。
def collatz(number): if number % 2 == 0: return number // 2 elif number % 2 == 1: return 3 * number + 1 while True: try: type_number = int(input('Pls input your number:')) correct_number = collatz(type_number) print(correct_number) if correct_number == 1: break except ValueError: print('You must input one int!')
2