python学习笔记8
一、python中的函数
函数是组织好的,可重复使用的,用来实现单一或相关功能的代码段。
函数能提高应用的模块性,和代码的重复利用率。
函数的结构:
# 函数基本结构
def 函数名(参数):
函数体
# 函数示例
# 下面是一个求字符串列表长度的函数
def len_func(s): # len_func是函数名,s是函数的输入参数
count = 0
for i in s: # 循环遍历所求对象
count += 1 # 用count对长度进行计数
return count # 返回长度值
一般来说,要求函数名具有描述性。
函数调用方法:函数名+()
# 在定义了上述len_func函数的基础上进行调用
li = [1,2,3,34,56,7]
num = len_func(li) # 这里赋给num的是函数返回的count值
print(num) # 6
函数的返回值:在上面定义的函数中,函数返回了return语句后面的变量值。事实上,python中函数的返回值正式由return语句控制的,注意当函数运行过程中执行了一次return语句后,函数直接跳出,不在执行后续语句。
# 示例函数
def test_1():
print('hello',end=',')
print('world')
test_1() # 正常情况下输出 hello,world
# 当加入return语句时
def test_2():
print('hello',end=',')
return
print('world')
test_2() # 此时函数输出 hello,
# 知识因为函数执行到return语句后就跳出函数了,print('world')并没有执行
当返回的数有多个时,会将数据存放在一个元组里返回给执行者
# 多个返回值的情况
def test_3():
return 'abc',[1,2,3],{'name':'Lieve'}
s = test_3()
print(s,type(s)) # ('abc', [1, 2, 3], {'name': 'Lieve'}) <class 'tuple'>
# 此时可以利用元组的拆包性质将数据赋值给多个变量
s1,s2,s3 = test_3()
print(s2,type(s2)) # [1, 2, 3] <class 'list'>
# 此时变量数据类型是数据原本的类型
函数的参数:在前面定义的函数中,len_func和test_1函数在调用的时候,一个在括号里输入了变量而另一个没有,这个输入的变量就叫做函数的参数。由于函数是封装好的功能,所以传入参数就是为了将其对应的函数功能盘活。
函数的参数又实参和形参。其中函数执行传入的参数是实际参数,函数定义时接收的参数是形式参数。
从函数实参的角度出发:
- 位置参数:从左到右,实际参数与形式参数按照顺序一一对应。
- 关键字参数:关键字与相应参数一一对应,但是并不一定要按照顺序输入。
- 混合参数:上面两者都有。但是此时位置参数一定要在关键字参数前面。
从形参的角度出发:
- 位置参数:与实参的位置参数是一种,与之一一对应。
- 默认值参数:在定义函数的时候,给参数设置了常用的默认值,当实际参数没有默认值参数赋值时按照默认值运行。如需修改,需要在函数调用时对参数进行赋值。
默认参数的陷阱:对于可变(不可哈希)数据类型的默认参数
def func(name,alist=[]):
alist.append(name)
return alist
res_1 = func('Lieve')
print(res_1) # ['Lieve']
res_2 = func('Luo')
print(res_2) # ['Lieve', 'Luo']
# 这里alist这个默认参数被修改了
# 函数的默认参数是可变的数据类型时,调用这个函数时如果默认参数没有传入
# 则始终使用的是同一个数据
def func_test(a,list_1=[]):
list_1.append(a)
return list_1
# 默认参数list_1没有传入数据时,默认为[]
print(func_test(10)) # [10]
# list_1传入数据时,优先使用传入数据
print(func_test(20,[100])) # [100,20]
# list_1再次没有传入数据时,使用的是之前引用过的[10]
print(func_test(30)) # [10,30]
#相当于下面程序的操作
li_1 = []
li_1.append(10)
print(li_1) # [10]
li_2 = [100]
li_2.append(20)
print(li_2) # [100,20]
li_1.append(30)
print(li_1) # [10,30]
- 万能参数:一种是可以接受所有位置参数的万能参数(形参)。这种参数需要在参数名称前加上*,形如
def my_func(*args):
print(args)
print('%s,%s,%s,%s.'%args) # 这里%s的个数需要与args中元素个数一致
# 调用函数
my_func('a',[1,2,3],'b','c')
# ('a', [1, 2, 3], 'b', 'c')
# a,[1, 2, 3],b,c.
# 这里*args代表函数参数是一个万能参数
# 它将输入的一系列位置参数聚合成一个元组赋值给args。
另一种是可以接受所有关键字参数的万能参数(形参)。这种参数需要在参数名称前加上**,形如:
def my_func_1(**kwargs):
print(kwargs)
# 调用函数
my_func_1(a=1,b=2,c='a',d=[1,2,3])
# {'a': 1, 'b': 2, 'c': 'a', 'd': [1, 2, 3]}
# 这里**kwargs代表函数参数是一个万能参数
# 它将输入的一系列关键字参数聚合成一个元组赋值给kwargs。
形参角度参数的顺序:位置参数>>>接受位置参数的万能参数>>>关键字参数>>>仅限关键字参数>>>接收关键字参数的万能参数。
# 在函数的调用中,*表示将原本数据结构拆散。
def func_1(*args,**kwargs):
print(args,kwargs)
func_1([1,2,3],[4,5,6]) # ([1,2,3],[4,5,6]) {}
func_1(*[1,2,3],*[4,5,6]) # (1, 2, 3, 4, 5, 6) {}
func_1({'name':'Lieve'},{'age':22}) # ({'name': 'Lieve'}, {'age': 22}) {}
func_1(**{'name':'Lieve'},**{'age':22}) # () {'name': 'Lieve', 'age': 22}
python中分为三个空间:
- 内置名称空间:builtins.py
- 全局名称空间:当前的.py文件
- 局部名称空间:对于定义的函数,只有在函数执行时才开辟
三个空间的加载顺序:内置名称空间>>>全局名称空间>>>局部名称空间
三个空间的取值顺序:就近原则,单向不可逆
python中的作用域:
- 全局作用域:内置名称空间,全局名称空间
- 局部作用域:局部名称空间(可从全局作用域获取变量,但是不能修改)
n = 1
def func_2():
print(n)
func_2() # 1 此时局部引用了全局变量n
def func_3():
n = 100
print(n)
func_3() # 100 此时函数内重新定义了n
print(n) # 1 但是全局变量并没有修改
def fun_4():
n = n+1
print(n)
func_4() # NameError: name 'func_4' is not defined
# 当在局部作用域修改一个全局变量时
# python解释器默认你在局部作用域定义了一个新的变量
# 并且此时的修改操作是作用在这个新变量上
def func_5():
num = 1
def func_6():
num += 1
print(num)
print(num)
func_5()
# UnboundLocalError: local variable 'num' referenced before assignment
# 此时内层函数也是只能调用num,不能修改
内置函数globals()、locals()
- globals():返回一个字典,字典里面的键值对是全局作用域里面的所有内容
- locals():返回一个字典,字典里面的键值对是当前作用域里面的所有内容
全局变量和局部变量的声明:
global:
- 在局部作用域声明一个全局变量
# 一种易错情形
def func_t():
global a
a = 'hello'
print(a) # NameError: name 'a' is not defined
# 因为定义全局变量是函数内部的操作,所以在函数执行前这个变量并没有定义
func_t()
# 函数运行后定义了全局变量a
print(a) # hello
- 修改一个全局变量
n = 1
def func():
global n # 此时函数内的n不再是局部变量而是全局变量
n += 1
print(n)
func() # 2
nonlocal:功能跟global类似
- 不能操作全局变量
- 局部作用域:内层函数对外层函数的局部变量进行修改