函数基础
1.函数前戏
name_list = ['jason', 'kevin', 'oscar', 'jerry']
需求1.想要统计列表中的元素个数,该怎样做?
name_list = ['jason', 'kevin', 'oscar', 'jerry']
print(len(name_list)) # 4
需求2:如果不能用len(),请统计列表中元素个数:
count = 0
for i in name_list:
count += 1
print(count) # 4
统计列表内部数据值个数的代码需要在很多地方使用,因此引出函数概念:
"""
循环:相同代码在相同位置反复执行。
函数:相同代码在不同位置反复执行。
ps:相同的代码不是真正一模一样而是可以通过传入的数据值不同而做出不同的改变。
"""
定义一个简单的函数,统计列表中的个数,并且在其他需要的地方可以调用到该数据值:
name_list = ['jason', 'kevin', 'oscar', 'jerry']
def my_len():
count = 0
for i in name_list:
count += 1
print(count)
my_len() # 4
"""
函数相当于是工具(具有一定功能)
不用函数
修理工需要修理器件要用锤子 原地打造 每次用完就扔掉 下次用继续原地打造
用函数
修理工提前准备好工具 什么时候想用就直接拿出来使用
"""
2.函数的语法结构
def 函数名(参数):
'''函数注释'''
函数体代码
return 返回值
1.def:
定义函数的关键字
2.函数名:
命名规则等同于变量名,应做到见名知意,不建议使用汉字或拼音,可以使用数字、字母、下划线,但是数字不能开头,函数名不能和关键字重复
3.参数:可有可无,根据使用函数时是否需要外界数据传入而定
4.函数注释:类似于工具说明书
5.函数体代码:是整个函数的核心,主要取决于程序员的编写
6.return:使用函数之后可以返回给使用者的数据,可有可无
3.函数的定义和调用
1.函数在定义阶段只检测语法,不执行代码。
"""
函数在定义阶段如果函数体代码有错误也不会报错,只有在调用阶段才会报错。
"""
\n
2.函数在调用阶段才会执行函数体代码
调用方式:函数名+括号
3.函数必须遵循先定义后调用的原则
4.函数定义阶段使用def+函数名+括号来定义函数,如果有参数则需要在括号内按照相应的规则传递参数。
4.函数的分类
1.空函数
空函数代码为空,使用pass或者...补全语法结构
空函数主要用于项目前期的功能框架搭建
def register():
"""注册功能"""
pass
def load():
"""登录功能"""
pass
def del_user():
"""删除用户功能"""
pass
2.无参函数
定义函数的时候括号内没有参数
def index():
print('hello world')
index()
3.有参函数
定义函数的时候括号内写参数,调用函数的时候括号内传参数
def index(a):
print(a)
index('hello world') # hello world
5.函数的返回值
1.什么是返回值?
调用函数之后返回给调用者的结果
2.如何获取函数的返回值?
res = func() # 先执行func函数,然后将返回值赋值给变量res
变量名、赋值符号、函数的调用。print(res)是不仅会调用函数也会返回res后面的值。
3.函数返回值的多种情况:
3.1:函数体代码没有return关键字,默认返回None
3.2:函数体代码有return,如果后面什么都没写,还是返回None
3.3:函数体代码有return,后面写什么就返回什么
3.4函数体代码有return并且后面有多个数据值,则自动组成元组返回
3.5函数体代码遇到return会立刻结束
"""
return后面跟代码时不会执行,但是也不会报错
"""
6.函数的参数
1.形式参数:
在函数定义阶段括号内填写的参数,简称'形参'
2.实际参数
在函数调用阶段括号内填写的参数,简称'实参'
"""
形参和实参的关系:
形参类似于变量名,在函数定义阶段写,要做到见名知意。
def register(name,pwd):
pass
实参类似于数据值,在函数调用阶段与形参临时绑定,函数运行结束立刻断开。
register(max,123)
形参name与实参max临时绑定,形参pwd与123临时绑定。
"""
7.函数参数之位置参数
"""
当函数体代码只有一行并且很简单的情况下,可以直接在冒号后面编写,不用换行。
eg:
def index(): pass
"""
1.位置形参:
函数定义阶段括号内从左往右依次填写的变量名。
def index(a, b, c): pass
2.位置实参:
函数调用阶段括号内从左往右依次填写的数据值,位置实参和位置形参数量要相等。
index(1, 2, 3)
2.1 位置实参数量少于形参:会报错
2.2 位置实参数量多于形参:会报错
2.3 应按照个数一一对应传入参数
2.4 关键字传参:应当指名道姓的传
2.5 关键字参数一定要在位置参数的后面
# 如图操作效果同位置参数
2.6 同一个形参在调用的时候不能多次赋值
# 如图操作1通过位置参数传递给a,2又通过关键字参数传递给a,造成多次赋值
2.7 实参既可以是数据值,也可以是绑定了数据值的变量名
"""
位置参数要在关键字参数的后面(越短的越简单的越靠前,越长的越复杂的越靠后)
"""
8.默认参数
1.定义:提前已经设置好,用户可以传也可以选择不传,本质就是关键字参数。默认参数的定义也遵循段的简单的靠前,长的复杂的靠后。
def index(name, age, gender='male'):
print(f"""
-------------------------------
姓名:{name}
年龄:{age}
性别:{gender}
-------------------------------
""")
2.如果信息和默认参数信息一致,可以不传默认参数。
# index('max', 25)
3.如果信息和默认参数信息一致,也可以传默认参数。
# index('tony', 22, 'male')
4.如果信息和默认参数信息不一致,直接传入参数或者用关键字传入。
# index('jenny', 18, 'female')
9.可变长形参
1.*在形参中,用于接收多余的位置参数,组织成元组赋值给*后面的变量名。
def index(*a):
print(a)
index() # ()
index(11) # (11,)
index(11, 22, 33) # (11, 22, 33)
"""
本案例中只有*a一个参数,所有的位置参数都会被a接收,形成一个元组返回
"""
def func(a, *b):
print(a, b)
func(1) # 1 ()
func(1, 2) # 1 (2,)
func(1, 2, 3, 4) # 1 (2, 3, 4)
"""
本案例中实参中的第一个参数会和a绑定,剩下的实参会被b接收形成一个元组。
"""
2.**在形参中,用于接收多余的关键字参数,组织成字典的形式赋值给**后面的变量名
ps:如果只有形参中只有**k那么实参中传入位置参数会报错。
def index(**k):
print(k)
index(1, 2, 3) # 报错
def index(**k):
print(k)
index(username = 'max', age = 25 ) # {'username': 'max', 'age': 25}
3.*和**混合使用
def index(*args, **kwargs):
print(args, kwargs)
index() # () {} 没有相应的参数会形成一个空元组或空字典
index(1, 2) # (1, 2) {}
index(a=1, b=2, c=4) # () {'a': 1, 'b': 2, 'c': 4}
index(1, 3, 5, username= 'max', userjob= 'sale') # (1, 3, 5) {'username': 'max', 'userjob': 'sale'}
def index(x, *args, **kwargs):
print(x, args, kwargs)
index(1, 2, 4, 5, username='max', userjob='sale') # 1 (2, 4, 5) {'username': 'max', 'userjob': 'sale'} # ba 1赋值给x,剩余的位置参数2,3,4被args接收,关键字参数被kwargs接收形成字典。
def index(x, *args, **kwargs):
print(x, args, kwargs)
index(1, username = 'max', userage = 25) # 1 () {'username': 'max', 'userage': 25}
def index(x, *args, **kwargs):
print(x, args, kwargs)
index(1, 2, 3, 4) # 1 (2, 3, 4) {}
"""
由于*和**在函数的形参中使用频率很高 后面跟的变量名推荐使用
*args
**kwargs
def index(*args,**kwargs):pass
"""
10.可变长实参
1.如果有一个函数有3个位置形参,如何将列表中的元素(3个)依次当做位置实参传入函数?
l1 = [1, 2, 3]
def index(a, b, c):
print(a, b, c)
index(l1[0], l1[1], l1[2]) # 1 2 3
"""
由于用列表索引的方法取值当做位置实参的方法较慢,引出新的方法:*+列表、元组、字符串、字典、集合都可以把个数据类型中的元素挨个取出当做函数的位置实参。
注意:1.*+字典只能取出字典的键。
2.集合是无序的,*+集合取出的元素顺序会打乱
"""
l1 = [1, 2, 3]
t1 = (4, 5, 6)
s = 'wow'
set1 = {2, 5, 4}
dict1 = {'username': 'max', 'age':25, 'hobby':'soccer'}
def index(a, b, c):
print(a, b, c)
print(*l1) # 1 2 3
print(*t1) # 4 5 6
print(*s) # w o w
print(*set1) # 2 4 5
print(*dict1) # username age hobby
"""
在实参中,*类似于for循环,将所有循环遍历出来的数据按照位置参数一次性传递给函数
"""
2.在实参中,**可以将字典打散成关键字参数传递给函数
userdict = {'username': 'max', 'userage': 25, 'userjob': 'sale'}
def index(username, userage, userjob):
print(username, userage, userjob)
index(**userdict)
11.命名关键字参数
'''形参必须按照关键字参数传值>>>:命名关键字参数'''
def index(name, *args, gender='male', **kwargs):
print(name, args, gender, kwargs)
index('max', 1, 2, 3, a=1, b=2) # max (1, 2, 3) male {'a': 1, 'b': 2}
index('max', 1, 2, 3, 'female', a=1, b=2) # max (1, 2, 3, 'female') male {'a': 1, 'b': 2}
12.名称空间
"""
回顾:变量名绑定数据值的深层原理:
name = 'max'
1.申请一块内存空间存储数据值'max'
2.给'max'绑定一个变量名name
3.以后通过变量名name就可以访问到数据值‘max’
"""
名称空间:就是用来存储变量名与数据值绑定关系的地方,也可以直接理解为存储变量名的地方。
1.内置名称空间:
解释器运行时自动产生,里面包含了很多名字,eg:print、len、def
2.全局名称空间
py文件运行产生,里面存放文件级别的名字
name = 'max'
if name = 'max
if pwd = 123
while True:
count = 0
def index():
pass
3.局部名称空间:
函数体代码运行产生的空间
13.名称空间存货周期及作用范围(域)
1.存活周期:
内置名称空间:python解释器启动则创建,关闭则销毁
全局名称空间:py文件执行则创建,运行结束则销毁
局部名称空间:函数体代码运行创建,函数体代码结束则销毁
2.作用域:
内置名称空间:
解释器级别全局有效
全局名称空间:
py文件级别全局有效
局部名称空间:
函数体代码内有效
14.名字的查找顺序
涉及到名字的查找,一定要先搞明白自己在哪个空间(遵循只能向更大的方向查找,不能向更小的方向查找)
1.当我们在局部名称空间时:
局部名称空间>>>全局名称空间>>>内置名称空间
2.当我们在全局名称空间时:
全局名称空间>>>内置名称空间
15.查找顺序案例
1.相互独立的局部名称空间不能够互相访问
def index1():
name = 'jason'
print(age)
def index2():
age = 18
print(name)
index1()
index2()
# 报错原因:age在index2()的局部名称空间下,而name在index(1)的局部名称空间下,互相独立的局部名称空间不能互相访问。
\n
2.局部名称空间嵌套:
先从自己的局部名称空间查找,之后由内而外依次查找。
示例1:
x = '干饭了'
def func1():
x = 1
def func2():
x = 2
def func3():
print(x)
x = 3
func3()
func2()
func1() # 报错
'''
报错原因:local variable 'x' referenced before assignment,因为在func3()的局部名称空间
下先打印x然后才定义x=3,如果同一个局部名称空间内有赋值和输出,那么赋值一定要在输出之前
'''
示例2:
x = '干饭了'
def func1():
x = 1
def func2():
x = 2
def func3():
x = 3
print(x)
func3()
func2()
func1() # 3
'''
顺序和上一题一样,本题x = 3和print(x)都在func3()的局部名称空间下所以在func3()的局部名称空间
就可以找到x = 3
'''
示例3:
x = '干饭了'
def func1():
x = 1
def func2():
x = 2
def func3():
print(x)
func3()
func2()
func1() # 2
'''
顺序也和第一题一样,本题func3()的局部名称空间下无法找到x所以只能去func2()的局部名称空间中找,得x = 2
'''
查找顺序为:
示例4:
x = '干饭了'
def func1():
x = 1
def func2():
x = 2
def func3():
x = 3
print(x) # 4号x:3
func3()
print(x) # 3号x:2
func2()
print(x) # 2号x: 1
func1()
print(x) # 1号x: 干饭了
'''
1号x首先在全局名称空间中查找,全局名称空间中的x = '干饭了',2号x首先在func1()的局部名称空间中
查找x = 1,3号x首先在func2()中查找x = 2,4号x首先在func3()中的局部名称空间中寻找x = 3.
'''
作业
1.判断下列money的值是多少并说明理由 思考如何修改而不是新增绑定关系
money = 100
def index():
money = 666
print(money)
答案为100,因为index()函数未调用,寻找的money为全局名称空间中的money。
money = 100
def func1():
money = 666
def func2():
money = 888
func2()
print(money)
答案为100,因为func1()函数未调用,寻找的money为全局名称空间中的money。