1、如何自定义一个函数
前面我们已经使用过像print()、len()这些函数,那么如何自己定义一个函数呢,函数的格式如下:
def function_name(参数、参数):
做一些事情
return value
函数的定义都是以def开头的,然后是函数的名字,以一个冒号结束,冒号后会新起一行,从这一行开始你可以写任何的python语句来实现一些函数功能,当然函数结束后,还可以返回一个值给调用者,value就是返回给调用者的值。
例如,定义一个叫greeting的函数,如下:
def greeting(username):
print(“hello “ + username)
print(“hello “ + username)
print(“end...”)
return len(username)
length = greeting(“xiaoming”)
print(length)
上面定义了一个叫greeting的函数,他带一个叫做username的参数,在函数体中输出了两行”hello,username”,最后还返回给调用者username的长度。greeting(“xiaoming’)就是在调用这个函数,只有实际调用函数时才会执行函数体中的语句,”xiaoming”这个字符串会赋值给username,那么在greeting这个函数体中的username这个变量的值就是”xiaoming”了,这个函数会在屏幕上打印两行”hello xiaoming”,然后使用len(username)计算”xiaoming”这个字符串的长度,并把这个长度返回给调用者,这里是变量length,最后用print函数输出了”xiaoming”这个字符串的长度。
注意:username仅在greeting这个函数中有效,出了这个函数就被销毁了,如果在print(length)后面加一条print(username)的语句会报错。
2、None值
像上面的自定义函数有一个return返回值,但并不是每个函数都需要返回值此,像print()函数,这种不需要返回值的函数可以不写return这条语句,python会默认在最后加上return None,None表示什么都没有的意思,None是NoneType数据类型的值,NoneType和其他数据类型一样,不过仅有一个None值而已。
在交互式shell中输入以上语句,可以证明print()确实返回了None。
3、关键字参数
前面定义的函数,当去调用他时,参数和变量是用位置来对应的,例如greeting(“xiaoming”),xiaoming对应username,也就是第一个参数对应第一个变量。如果greeting还有 一个参数,像这样greeting(username,password),那么调用时为greeting(“xiaoming”,”1234”),则”xiaoming”对应username,”1234”对应password,也就是说第一个参数”xiaoming”对应第一个变量username,第二个参数”1234”对应变量password。
除了位置对应外,还有一种使用关键字的对应方法,说的直白点,就是在调用时,可以用凑字字指定把这个参数传递给以关键字命名的这个变量,在交互式shell中实践一下print()函数(print中的关键字参数end指定在输出字符串后,需要在此行末尾加的字符,默认是加换行符):
4、变量的作用域
变量的作用域是指变量在其规定的范围内有效,超过这个范围后,则变量失效(不存在)。目前有两种变量作用域:
- 全局作用域:没有在任何函数内创建的变量
- 局部作用域: 在函数内创建的变量,包括函数的参数
一个程序内可以有多个函数,每个函数都是一个局部作用域,所以一个程序会存在多个局部作用域,但只有一个全局作用域。
例如下面的代码:
作用域有4条规则
- 局部作用内的变量不能在全局作用里使用
- 在一个局部任务域内,不能使用另一个域名作用内的变量
def spam():
eggs = 99
bacon()
print(eggs)
def bacon():
ham = 101
eggs = 0
spam()
结果是99,说明在bacon函数内的变量eggs并没有对spam函数内的变量eggs造成任何影响,这是因为每个函数都有一个独立的局部作用域,在这个独立的作用域内创建的变量只在这个作用域内有效,而且与其他局部作用域没有任何关系,所以在bacon函数内会新创建eggs变量,bacon函数被调用完成后,这个eggs变量会被丢弃,然后又回到spam的作用域内,因为print(eggs)是在spam的作用域内,所以会使用spam作用域内的eggs变量。
- 局部作用域可以使用全局作用里的变量
def spam():
print(eggs)
eggs = 42
spam()
print(eggs)
在spam函数内并没有变量eggs,但可以正确的输出结果,因为在全局作用域内有一个叫eggs的变量。
- 局部变量可以和全局变量同名,此时在函数内局部变量会屏蔽全局变量
def spam():
eggs = 'spam local'
print(eggs) # 使用spam内的eggs变量
def bacon():
eggs = 'bacon local'
print(eggs) # 使用bacon内的eggs变量
spam()
print(eggs)# 还是使用bacon内的eggs变量
eggs = 'global'
bacon()
print(eggs) # 这是在全局作用域内,所以所有的局部作用里的变量都不存在了,所以会使用全局变量eggs
尽管在全局作用域、spam作用域、bacon作用域内都有同名的变量eggs,但一旦程序运行到相应的函数作用域内,其他所有作用域的同名变量就失效了,所以会使用当前函数内的变量。
那如果我不想在spam函数内新创建一个叫eggs的变量呢,我只想在这个函数内使用全局变量eggs呢,好办,使用global关键字,如下:
def spam():
global eggs
eggs = 'spam'
eggs = 'global'
spam()
print(eggs)
此时,在spam函数内,并没有新创建一个变量,而是使用global关键字表示使用在全局作用域定义的变量eggs,所以对eggs变量重新赋值会更改 全局变量eggs的值,所以最后输出了更改后的值spam
5、异常处理
现在我们的程序只要有错误或者异常,就会直接退出,这也太不健壮了,在真实环境中,我们希望我们的程序能够在 发生错误的场景下能自动处理错误,并能继续往下执行,在python中,可以把可能发生 错误的代码放在try和except之间,如果发行错误,程序会跳到except后的语句继续执行,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))
当程序执行到print(spam(0))时,spam实际执行的语句是42/0,学过小学数学的都知道,不能被0整除,这会发生错误,引发异常,所以会被异常捕获语句except ZeroDivisionError捕获,所以会执行except ZeroDivisionError下的print语句,其中ZeroDivisionError叫做异常类型,只有发生错误的类型和except后在类型匹配时会被捕获,可以有多条except语句。
6、综合实例
import time, sys
indent = 0 # 在一行开始前会有多少个空格
indentIncreasing = True # 当前缩进是否增加还是减少
try:
while True: # 无限循环
print(' ' * indent, end='') # ' ' * indent是空格与缩进数目的乘积,end=''表示不使用默认的换行符
print('********')
time.sleep(0.1) # 程序暂停0.1秒
if indentIncreasing: # 如果缩进是增加的,则下面的每一行都会比前一行多一个空格
indent = indent + 1 # 增加一个空格缩进
if indent == 20: # 最多20个空格缩进
indentIncreasing = False # False表示缩进的空格数开始减少
else:
indent = indent - 1 # 减少一个空格缩进
if indent == 0: # 缩进最少是0,不可能有负缩进数
indentIncreasing = True # True表示增加缩进空格数
except KeyboardInterrupt: # 当用户按下Ctrl + C时,程序会引发KeyboardInterrupt异常,昆except会匹配这个异常,并捕获
sys.exit() # 捕获异常后,我们自己使用sys.exit()退出程序,如果不捕获异常会打印出一串错误消息,不美观
原创文章,转载请注明出处:http://b.nwumba.cn/article/10/