如何定义函数
'''
1、功能的单一化
2、函数内部需要外部的资源:通过函数的参数来实现
3、函数执行后的结果需要告诉外界:通过返回值告诉给外界
'''
函数的参数
形参与实参
'''
形参:在函数定义时()里出现的参数
-- 形参本身没有实际值(意义),在函数调用时,传入什么实参,形参就装有什么值
实参:在函数调用时()里出现的参数
-- 实参有实际值(意义)
重点:函数调用传参:将实参的值赋值给形参 | 形参要获取外界的值只能通过实参进行获取
'''
def fn(a, b): # a, b:形参
pass
a = 10
fn(a, 20) # a, 20:实参
code:
# 形参与实参
def fn(a, b=10, *args, c, d=20, e, **kwargs):
pass
a = 100
b = '200'
def fn1(a, b): # 形参:在函数定义时()里出现的参数
print(a, b)
fn1(a, '2000') # 实参:在函数调用时()里出现的参数
# 函数调用传参:将实参的值赋值给形参
# 实参有实际值(意义)
# 形参本身没有实际值(意义),在函数调用时,传入什么实参,形参就装有什么值
print('end')
两种实参
'''
位置实参:
1.传参两种方式:实参名 | 实参具体值
2.必须按位置对形参进行传值
关键字实参:
1.传参两种方式:形参名=实参名 | 形参名=实参值
2.可以指名道姓对形参进行传值,所以可以不用按位置进行传参
'''
def func(a, b, c):
print(a, b, c)
# func(10, b=20, 200) 报错:SyntaxError: positional argument follows keyword argument
# 重点:两种实参在一起进行传参时:必须位置在前,关键字在后
code:
实参
def fn(a, b):
print(a, b)
# 实参:
# 1.位置实参: 按位置先后进行传参,a,b不能被颠倒位置进行传参,a永远比b先接受值
fn(10, 20)
fn(20, 10)
a = 100
b = 200
fn(a, b)
fn(b, a)
# 传参两种方式:实参名 | 实参具体值
# 2.关键字实参:指名道姓进行传参,a,b能被颠倒位置进行传参,名字指向谁,就是谁接受值
c = 1000
fn(a=10, b=c)
fn(b=20, a=10)
# 传参两种方式:形参名=实参名 | 形参名=实参值
# 结合传参
def func(a, b, c):
print(a, b, c)
# func(10, b=20, 200) 报错:SyntaxError: positional argument follows keyword argument
# 两种实参在一起进行传参时:必须位置在前,关键字在后
形参
'''
形参种类:
1)位置形参
-- 普通位置形参
-- 默认值形参
-- 可变长位置形参
2)关键字形参
-- 有默认值关键字形参
-- 无默认值关键字形参
-- 可变长关键字形参
'''
def fn1(a, b, *, x, y):
pass
# fn1()
# 位置形参与关键字形参的分水岭 *
# 重点:
# 位置实参只能给位置形参进行传值
# fn1(10, 20, 30, 40) # 只有两个位置,给了四个位置
# 关键字实参可以给位置形参与关键字形参进行传值
# fn1(x=30, y=40, a=10, b=20)
# part2
# 两个带默认值的形参
def fn2(a=10, *, x=20):
print(a, x)
fn2(100, x=200)
# 总结:
# 1.有默认值的参数可以不用传值
# 2.*前有默认值的叫默认值参数,属于位置形参,可以被位置及关键字实参进行传值
# 3.*后有默认值的叫有默认值的关键字形参,属于关键字形参,只能被关键字实参进行传值
# def get_p(name, sex='男'):
# pass
# get_p('Bob', '女')
# part3
def fn3(a, b=10, *, x, y=20, z):
print(a, b, x, y, z)
fn3(100, x=200, z=300)
# 总结:
# 1.没有默认值的必须传参,有默认值的可以传也可以不传
# 2.位置有值的必须出现在无值之后,关键字顺序不做要求
# part4
def fn4(a, b=10, *args, x, **kwargs):
print(a, b, x)
print(args)
print(kwargs)
fn4(10, 20, 30, x=100, y=200, z=300)
# 总结:
# 1.可变长是用来接收未接收完的值(接收0到n个):
# -- *args用来接收所以没有接收完的位置(只能接收位置实参)
# -- **kwargs用来接收所以没有接收完的关键字(只能接收关键字实参)
# 2.*args必须出现在所以位置参数之后,**kwargs必须出现在所以参数之后
# 假设第一个位置永远是参数name
def func4(*args, **kwargs):
name = args[0] # 将name抽出来
def func44(name, *args, **kwargs):
# name 可以直接接收,省了抽出来的过程
pass
# part5
def fn5(*args, **kwargs):
print(args, kwargs) # args=(10, 20) kwargs={'x': 100, 'y': 200}
def fn55(*args, **kwargs):
print(args, kwargs) # args=(10, 20) kwargs={'x': 100, 'y': 200}
fn5(*args, **kwargs) # *args就会把元组打散传递,**kwargs就会把字典打散传递
fn55(10, 20, x=100, y=200)
# 容器类型可以打散传值
def temp(*args, **kwargs):
print(args, kwargs)
ls = [1, 2, 3, 4, 5]
dic = {'a': 1, 'b': 2}
temp(*ls, **dic)
两大形参分类
'''
形参种类:
1)位置形参
-- 普通位置形参
-- 默认值形参
-- 可变长位置形参
2)关键字形参
-- 有默认值关键字形参
-- 无默认值关键字形参
-- 可变长关键字形参
'''
def fn(a, b, *, x, y): # 位置形参:a, b 关键字形参: x, y
pass
'''
重点:
1.*为分水岭
2.位置实参只能给位置形参进行传值
3.关键字实参可以给位置形参与关键字形参进行传值
'''
两个带默认值的形参
def fn2(a=10, *, x=20):
print(a, x)
fn2(100, x=200)
# 总结:
# 1.有默认值的参数可以不用传值
# 2.*前有默认值的叫默认值参数,属于位置形参,可以被位置及关键字实参进行传值
# 3.*后有默认值的叫有默认值的关键字形参,属于关键字形参,只能被关键字实参进行传值
# 4.如果省略*, 有默认值的形参都是默认值参数
不带默认值与带默认值形参结合使用
def fn3(a, b=10, *, x, y=20, z):
print(a, b, x, y, z)
fn3(100, x=200, z=300)
# 总结:
# 1.没有默认值的必须传参,有默认值的可以传也可以不传
# 2.位置有值的必须出现在无值之后,关键字顺序不做要求
可变长位置形参与可变长关键字形参
def fn4(a, b=10, *args, x, **kwargs):
print(a, b, x)
print(args)
print(kwargs)
fn4(10, 20, 30, x=100, y=200, z=300)
# 总结:
# 1.可变长是用来接收未接收完的值(接收0到n个):
# -- *args用来接收所有没有接收完的位置(只能接收位置实参)
# -- **kwargs用来接收所有没有接收完的关键字(只能接收关键字实参)
# 2.*args必须出现在所以位置参数之后,**kwargs必须出现在所以参数之后
# 常见应用场景
# 假设第一个位置永远是参数name
def func4(*args, **kwargs):
name = args[0] # 将name抽出来
def func44(name, *args, **kwargs):
# name 可以直接接收,省了抽出来的过程
pass
总结
'''
1.位置实参只能给位置形参传值
2.关键字实参可以给位置及关键字形参传值
3.有默认值的可以不用传参
4.可变长位置形参只能接受位置实参,接受位置形参没有接收完的位置实参,存放到元组中
5.可变长关键字形参只能接受关键字实参,接受关键字形参没有接收完的关键字实参,存放到字典中
6.*args必须出现在所有位置形参之后,**kwargs必须在所有形参之后
'''
可变长整体传参:打散传值
def fn(*args, **kwargs):
print(args, kwargs)
fn([1, 2, 3], {'a':1 , 'b': 2}) # =>接收到的 ([1, 2, 3], {'a':1 , 'b': 2}) {}
fn(*[1, 2, 3], **{'a':1 , 'b': 2}) # =>接收到的 (1, 2, 3) {'a':1 , 'b': 2}
# 注:字符串也可以作为单列集合进行打散传递
fn(*'abc') # => ('a', 'b', 'c') {}
函数对象
# 函数名就是存放了函数的内存地址,存放了内存地址的变量都是对象,即 函数名 就是 函数对象
# 函数对象的应用场景
# 1 可以直接被引用
# 2 可以当作函数参数传递
# 3 可以作为函数的返回值
# 4 可以作为容器类型的元素
# 功能体
def add(n1, n2):
return n1 + n2
def low(n1, n2):
return n1 - n2
def jump(n1, n2):
return n1 * n2
# 完成功能
def computed(n1, n2, fn): # fn = add|low|jump
res = fn(n1, n2) # 调用具体的功能
return res
# 功能对应关系
method_map = { # 指令与函数对象的对应关系
'1': add,
'2': low,
'3': jump
}
# 获取功能
def get_method(cmd):
if cmd in method_map:
return method_map[cmd] # 返回 add|low|jump
return add # 当指令错误,add作为默认功能
while True:
cmd = input('cmd: ')
res = get_method(cmd)(10, 20) # 根据指令获取功能并调用得到结果
print(res)
code:
# part1
owen_a = 10
# owen_a存放10的地址,所以也是对象,普通的整型对象
# 函数对象:存放函数地址的变量就叫函数对象,就是函数名
def fn():
print(1)
print(2)
print(3)
return 1
print(fn)
# 1.函数对象():拿到函数地址并执行 - 函数的调用
# 2.函数调用一定会得到一个结果 - 函数的返回值 - 函数值: res=fn() == 函数调用后res=1
res = fn()
print(res)
res = 1
print(res)
print('------------------------')
# part2
# 应用场景:
# 1 可以直接被引用
# 2 可以当作函数参数传递
# 3 可以作为函数的返回值
# 4 可以作为容器类型的元素
def fn2():
print('fn2 run')
# fn2()
aaa = fn2 # 直接赋值
# aaa()
def fn22(fn): # fn = aaa = fn2 # 作为参数
fn()
fn22(aaa)
def fn222():
# return fn2()
return fn2 # 作为返回值
res = fn222() # res = fn2() = None | res = fn2 = 函数对象
print(res())
ls = [fn2, 10, 20] # 作为容器对象的成员
print(ls[1])
print(ls[0]())
# 案例:
def add(n1, n2):
return n1 + n2
def low(n1, n2):
return n1 - n2
def jump(n1, n2):
return n1 * n2
def computed(n1, n2, fn):
res = fn(n1, n2)
return res
method_map = {
'1': add,
'2': low,
'3': jump
}
def get_method(cmd):
if cmd in method_map:
return method_map[cmd]
return add
while True:
cmd = input('cmd: ')
res = 0
# if cmd in method_map:
# # res = method_map[cmd](10, 20)
# fn = get_method(cmd)
# res = fn(10, 20)
res = get_method(cmd)(10, 20)
print(res)
# while True:
# cmd = input('cmd: ')
# res = 0
# if cmd == '1':
# res = computed(10, 20, add)
# elif cmd == '2':
# res = computed(10, 20, low)
# elif cmd == '3':
# res = computed(10, 20, jump)
# print(res)
函数的嵌套调用
# 函数的嵌套调用:在一个函数内部调用另一个函数
# 求两个数最大值
def max_two(n1, n2):
if n1 > n2:
return n1
return n2
# 求三个数最大值
def max_three(n1, n2, n3):
max = max_two(n1, n2)
return max_two(max, n3)
# 求四个数最大值
def max_four(n1, n2, n3, n4):
max = max_three(n1, n2, n3)
return max_two(max, n4)
print(max_four(20, 50, 30, 50))
code:
# 函数的嵌套调用:在一个函数内部调用另一个函数
def fn1():
pass
def fn2():
fn1() # 函数的嵌套调用
def fn3():
fn2() # 函数的嵌套调用
fn3()
# 案例:
# 求两个数最大值
def max_two(n1, n2):
if n1 > n2:
return n1
return n2
# 求三个数最大值
def max_three(n1, n2, n3):
max = max_two(n1, n2)
return max_two(max, n3)
# 求四个数最大值
def max_four(n1, n2, n3, n4):
max = max_three(n1, n2, n3)
return max_two(max, n4)
print(max_four(20, 50, 30, 50))
名称空间
# 名称空间:存放名字与内存空间地址对应关系的容器
# 作用:解决由于名字有限,导致名字重复发送冲突的问题 - 内置全局局部可以同时使用一个名字存放不同地址
# 三种名称空间
# Built-in:内置名称空间;系统级,一个;随解释器执行而产生,解释器停止而销毁
# Global:全局名称空间;文件级,多个;随所属文件加载而产生,文件运行完毕而销毁
# Local:局部名称空间;函数级,多个;随所属函数执行而产生,函数执行完毕而销毁
# 加载顺序:Built-in > Global > Local
# -- 采用堆栈存储数据的方式(压栈),导致内置最后被访问
code:
# part1
'''
x = 100
def fn():
a = 10
fn()
# 没有缩进直接使用, 和在其他函数中,都只能使用x,不能使用函数内部的a
def func():
print(x)
func()
# 问题点:什么导致变量的访问权限
'''
# part2
# 名称空间:存放变量名与栈区内存地址的对应关系
print(len('123'))
len = 10
print(len)
del len
print(len('abc'))
print(len)
len = 200
print(type(len))
def fn():
len = 2000
print(len)
fn()
print(len)
函数的嵌套定义
# 函数的嵌套定义:在函数内部定义函数
# 诞生的理由:一个函数想使用另一个函数内部的变量,可以定义在其内部
def func():
a = 10
def fn():
print(a)
return fn
new_fn = func()
new_fn()
code:
'''
# 函数的嵌套定义:在函数内部定义函数
# 诞生的理由:一个函数想使用另一个函数内部的变量,可以定义在其内部
def func():
a = 10
def fn():
print(a)
return fn
new_fn = func()
new_fn()
'''
'''
# global关键字: 统一局部与全局的变量名
a = 100
def fn1():
global a
a = 200 # 让函数的局部变量a与全局变量a统一,两者是一个
print(a)
fn1()
print(a)
'''
# nonlocal关键字:统一局部与嵌套局部的变量名
def fn2():
num = 666
def fn22():
nonlocal num
num = 888
fn22()
print(num)
fn2()
# 作用域:
len = 10
def fn3():
len = 100
def fn33():
len = 1000
print('1:', len)
fn33()
print('2:', len)
fn3()
print('3:', len)
两个与函数有关的关键字:global nonlocal
# global:统一局部与全局的变量名
num = 10
def outer():
# global num
# num = 100
def inner():
global num
num = 1000
# nonlcal: 统一局部与嵌套局部的变量名
def outer():
num = 100
def inner():
nonlocal num
num = 1000
作用域
# 作用域:名字起作用的范围
# 作用:解决同名字可以共存问题 - 不同作用域相同名字的值都能在其作用域范围下进行使用
'''
四种作用域: LEGB
Built-in:内置作用域 - 所有文件所有地方都可以被访问
Global:全局作用域 - 在当前文件的所有位置
Enclosing:嵌套作用域 - 自身内部与内部的子函数
Local:局部作用域 - 只有自身内部
'''
# 加载顺序:Built-in > Global > Enclosing > Local
# 访问(查找)顺序:报错 < Built-in < Global < Enclosing < Local
# 作用范围:Built-in > Global > Enclosing > Local
网友评论