函数存在的意义
print('拿到一本书')
print('看书')
print('收起')
# 模拟一个看书的操作,多次调用某个操作,直接封装在函数里即可
def learnling(name, course, start, end): # 带参数函数
print(f'{name}报名了课程:{course}')
print(f'从第{start}节课学习到{end}节课')
print(f'{name}学习结束')
# 调用函数
learnling('小明', 'Python入门到精通', 1, 2)
带返回值的函数
def add(x, y):
return x * y
print(add(5, 3) + 6)
print(add('-', 60))
变量作用域
x = 55 # 全局变量 Global
def func():
global x
# 函数内调用并改变函数体外的全局变量时需要使用global来指定当前使用的是全局函数的变量
x = 99 # 否则会在函数内创建一个新的x 局部变量 Local
return x
print('全局x', x)
print('函数内x', func())
def func2():
y = 100
def nested():
nonlocal y
# 里层的函调用并改变外层函数的变量时需要使用nonlocal来指定当前使用的是外层函数的变量
y = 99
print(y)
nested()
print(y)
func2() # 不加nonlocal打印结果99 \n 100 加了是99 \n 99
参数的传递注意事项:
不可变类型,传递副本给函数,函数内操作不影响原始值
可变那类型,传递地址引用,函数内操作可能会影响原始值
# 数字
def change_numer(x):
# global x # 这里跟变量作用域不是一个概念哦,这里加global会报错的,因为变量在函数下面的
x += 10 # 改变函数参数的值
x = 5
print('原始数字', x)
change_numer(x)
print('被函数祸害过的数字', x)
# 字符串
def change_str(s):
s = 'xxxxx.cn' # 改变函数参数的值
s = 'baidu.com'
print('原始字符串:', s)
change_str(s)
print('被函数祸害过的字符串:', s)
# 列表
def change_list(l):
l[0] = '我变' # 改变函数参数的值
l = ['小红', '小明', '泰罗']
print('原始列表', l)
change_list(l)
print('被函数祸害过的列表', l) # 列表内的小红被改成我变
# 想让列表不改变 用copy() 或者[:] 给它个副本就行了
print('原始列表', l)
change_list(l.copy()) # 并没有改变原来的列表l,传给函数的是一个副本
print('被函数祸害过的列表', l) # 并没有被祸害,啦啦啦....
函数的几种参数
位置参数
*args(可变参数)、**kwargs(关键字参数)传递及解包
lambda表达式、函数的委托
位置参数
# Python是动态类型,函数可以传递任何类型的参数
def function(a, b, c, d):
print(a, b, c, d)
function('a', 1, 2, 3)
function([1, 2, 3], (4, 5, 6), {'a': 1, 'b': 2})
# 位置参数可以按照关键字参数传递
function(b=1, a=2, d=5, c=4)
# 位置参数的默认值
def func(a, b=2, c='c'): # 定义参数(形参)的时候设置默认实参:(参数=值, ...)
print(a, b, c)
func(1) # 调用的时候可省略传递参数
func(1, c='5')
传递实参时位置顺序与形参不同的时候,需要按照关键词匹配方式:参数=值, …
如果是默认参数,传递的类型要和定义形参时一致
为了安全起见,在不确定的情况下可以尽量使用关键词匹配方式传参:参数=值, …,可以避免顺序错误
*args(可变参数)
def avg(score1, score2): # 计算平均分的函数
return (score1+score2)/2
def avg(a, b, c):
# 假如此时成绩变成了三门科目,可以重新定义这个函数进行代码重载(也叫做函数的迭代)
return (a+b+c)/3
def avg(*args):
# 如果有五门、十门成绩的话,这个时候又要重载函数,此时就用到可变参数了
return sum(args) / len(args) # sum可以把一个集合里的值汇总
avg(88, 89, 60) # 传递的时候 可以传递任意数量的参数
scores = (88, 89, 60) # 如果事先有了个元组,想把这个元组传入
avg(*scores) # 这里传递的时候要用*号解包,解包后就跟88, 89, 60这种一样了
# 不解包的话元组本身是带括号的,不能直接传个元组
# 虽然要求传递的值刚好就是元组,但是传递的时候习惯是还是以参数1, 参数2 这种方式传递的,所以要解包
**kwargs(关键字参数)
def display_enployee(**kwargs):
print(kwargs)
display_enployee(name="Ton", age=23, aa='用这种方式传而不是用冒号,传什么都行')
# 这种传值方式跟字典表的构造函数一样 dict(name="Ton", age=23)
emp = {
'name': 'tom',
'age': 23
} # 事先有了个字典表
display_enployee(**emp) # 这里传递的时候要用**号解包,解包后就跟name="Ton", age=23这种一样了
lambda表达式
def add(a, b):
return a+b
f1 = lambda a, b: a + b
def hello(name):
print(name)
f2 = lambda name: print(name)
print(f1(5, 3))
f2('tom')
综合实例、函数的委托
def hello_chinese(name):
print('你好', name)
def hello_english(name):
print('Hello', name)
while True:
name = input('请输入您的姓名:\n')
if name == 'stop':
break
language = input('请输选择语言版本:\n c =>中文\n e =>英文\n j =>日文\n')
# 直接调用函数执行
if language == 'c':
hello_chinese(name)
elif language == 'e':
hello_english(name)
elif language == 'j':
(lambda name: print('靠你吉娃', name))(name) # lambda用括号括起来直接传值并执行
# 委托 函数赋值给一个变量(定义一个函数指针)
if language == 'c':
action = hello_chinese # 把函数赋值给action 定义一个函数指针
elif language == 'e':
action = hello_english # 把函数赋值给action 定义一个函数指针
elif language == 'j':
action = lambda name: print('靠你吉娃', name) # 同上 这里相当于给匿名函数取了个名字
action(name) # 所有的函数和匿名函数都赋值给action 委托action来调用
def hello_chinese(name):
print('你好', name)
def hello_english(name):
print('Hello', name)
# 用字典表实现多个if elif(switch) 因为Python没有switch
operation = {
'c': hello_chinese, # 把函数当做字典表的值来使用
'e': hello_english,
'j': lambda name: print('靠你吉娃', name)
}
while True:
name = input('请输入您的姓名:\n')
if name == 'stop':
break
language = input('请输选择语言版本:\n c =>中文\n e =>英文\n j =>日文\n')
operation.get(language)(name)
operation.get(language, lambda name: print('输入的语言不存在'))(name)
# 更简洁的委托 将一个函数作为参数传递给另一个函数
def hello(action, name):
action(name)
hello(hello_english, 'jiuren')
# 这个函数的第一个形参传递的参数(实参)是上面的函数,第二个参数是name
# 间接的把hello_english的函数的功能都给了hello这个函数 执行的时候都是靠hello执行
hello(lambda name: print('靠你鸡娃', name), 'jiuren')
# 用lambda表达式直接就可以在第一个参数里临时里写一个匿名函数 参数二还是name
高级函数工具
函数高级工具 map() filter() 返回带next()的可迭代对象(迭代器对象)
需要返回列表可以用list转换。也可以用for循环遍历出来
l = list(range(1, 21))
res = []
for x in l: # for循环
if x % 2 == 0:
res.append(x)
res = [x for x in l if x % 2 == 0] # 推导
'''
1.for循环'''
for x in l:
res.append(x + 5)
'''
2.推导 最实用'''
res = [x + 5 for x in l]
'''
3.map() 灵活性好 返回迭代器对象'''
def add(x):
return x + 5
res = list(map(add, l))
# 把l这个可迭代对象的每个值作为参数传递给add这个函数,返回迭代器对象,使用list转换成列表
res = list(map(lambda n: n**2, l)) # 直接传入一个lambda表达式
'''
4.filter() 灵活性好 返回迭代器对象'''
l = list(range(1, 16))
res = []
def even(x):
return x % 2 == 0
res = list(filter(even, l))
# 从列表里找到符合even()这个函数条件的值返回给列表
# 用for循环遍历map()和filter()返回的结果
res = filter(even, l)
for i in res:
print(i, end=' ') # end=' ' 是指用空格分割 默认是换行符
# 用lambda表达式 直接传一个匿名函数
res = list(filter(lambda l: l % 2 == 0, l))
res = filter(lambda l: l % 2 == 0, l)
for i in res:
print(i, end=' ')
作用域测试
"""
函数外的变量、函数内的变量和语句/循环体内的变量"""
def f():
a1 = 0 # Python每次定义变量要赋值,相当于其他语言定义一个变量不赋值的默认值和类型
# 空变量一般可以赋值为None,不占内存,这里赋值为0
for i in range(1, 6):
print('for循环里打印', a1) # 这里只能打印a1的初始值0
a1 = i
a2 = i
print('for循环里打印', a1, a2)
a1 += 1
a2 += 1
print('函数里 for循环体外打印', a1, a2)
while True:
a1 -= 1
a2 -= 1
print('while循环里打印', a1, a2)
if a1 and a2 <= 2:
break
if a1 >= 0 or a2 >= 0:
a1 += 1
a2 += 1
print('if里打印', a1, a2)
# print(a1, a2) # 函数内的变量在外面调用不了
abc = '函数体外的全局变量'
print('全局变量的初始值 => ' + abc)
def f1():
print('函数内调用函数外的全局变量(只调用不做更改其值) => ' + abc)
def f2():
# 函数内调用并更改函数体外的abc,需要用global指定,其他语言貌似不需要(易语言的程序集变量就不需要)
global abc
# abc *= 3
abc = '函数体外的全局变量被改变'
print('指定global后 更改过的全局变量 => ' + abc)
def f3():
abc = '不指定global,函数体外的全局变量不会改变,将创建一个新的局部变量'
# 如果不指定global,直接赋值给一个与函数体外同名的变量,会创建一个新变量,而不是改变函数体外的变量,
# 相当于易语言在程序集和局部分别创建一个变量abc,两个变量同名,一般是需要避免这种重名的
print('无global => ' + abc) # 这里打印会打印函数里(自己的)变量abc
'''
想调用并更改函数体外的变量就必须有global,有了global,就不可以再有其他与函数体外同名的变量了,
所有针对这个变量abc的调用和操作都需要在global下面。
没有global的话可以调用函数体外的变量的,且只能调用不能更改,
如果更改的话,会创建一个新变量,而不是改变函数体外的变量,这个时候就永远调用不到函数体外面的变量了,
所有针对这个新变量的调用和操作都需要在创建这个变量的下面。
'''
if __name__ == '__main__':
# f()
# f()
'''
对于Python来说,每次调用函数的时候a1的初始值都是等于0
因为Python定义变量是一定要先赋值的,在函数外面定义的a1初始值就是0
易语言不给初始值默认就有初始值,比如整数型是0,文本型是“”,每次调用子程序的时候所有局部变量也会变为初始值
'''
f1()
f2()
f1()
f3()
f1()
'''
作用域只是对函数来说的,对循环和判断语句没影响,跟易语言一样,java中只要是代码块,都有作用域
只是有时候定义变量 初始化的时候,需要考虑放到循环体外还是循环体内,比如用变量计数的时候
将变量放到函数外面,所有子程序都能调用到它,更改它的时候需要指定global
'''
拓展: