初始函数
什么是函数
函数就相当于具备某一功能的工具
使用函数必须遵循一些规则:
- 先定义
- 后调用
为何要使用函数
- 组织结构不清晰,可读性差
- 代码冗余,臃肿
- 因为代码冗余导致可维护性,扩展性差
函数的定义
函数是一个工具,函数名应该定义为动词,而不是名词。
def function_name(parameters):
"""函数文档字符串"""
# 函数体
# 可以包含多条语句
return expression
def 是定义函数使用的关键字
function_name是函数名,符合变量命令规范即可。
函数的命名应该反应出函数的功能
函数名指向函数内存地址,是对函数体代码的引用
函数名加上一个小括号就实现了这个函数的功能
( )括号内放函数需要输入的参数,即使函数不需要输入任何参数,也不能省略( )
parameters是函数的参数,确切的说是形参,即形式参数,如果函数不需要形参,可以不传参
: 括号后面要加上冒号,然后下一行开始缩进编写函数体的代码
"""函数文档字符串"""这部分是函数的注释部分,描述函数的功能和参数的意义
函数体代码块:函数体代码块是完成函数的功能部分
return后面跟函数的返回值,可以没有,也可以1个或多个
- 没有返回值,默认返回None
定义函数发生的事情
- 申请内存空间保存函数体代码
- 将上述内存地址绑定给函数名
- 定义函数不会执行函数体代码,但是会检测函数体的语法
调用函数发生的事情
- 通过函数名找到函数的内存地址
- 然后加括号就是在触发函数体代码的执行
形式1:无参函数
def func():
print('哈哈哈')
func()
# 哈哈哈
示范1
def bar(): # bar=函数的内存地址,所以可以直接访问到
print("from bar")
def foo():
print(bar) # 打印bar()函数的内存地址
bar() # 内存地址加括号可以执行函数体代码
print("from foo")
foo()
<function bar at 0x0000018F1054F0A0>
from bar
from foo
示范2
def foo():
print(bar) # 打印bar()函数的内存地址
bar() # 内存地址加括号可以执行函数体代码
print("from foo")
def bar(): # bar=函数的内存地址,所以可以直接访问到
print("from bar")
foo()
<function bar at 0x000001B62C1D4670>
from bar
from foo
示范3
def foo():
# 因为bar在
print(bar) # 打印bar()函数的内存地址
bar() # 内存地址加括号可以执行函数体代码
print("from foo")
foo()
def bar():
print("from bar")
# 结果报错:NameError: name 'bar' is not defined
在给出的代码中,调用 bar()
的语句位于 foo()
函数的定义之前,这会导致 NameError
异常。
在 Python 中,函数必须在调用它们之前被定义。因此,当 foo()
函数在定义的时候尝试调用 bar()
函数时,由于 bar()
函数还没有被定义,Python 会引发 NameError
异常。
要解决这个问题,您可以将 bar()
函数的定义移动到 foo()
函数的上方,或者将 bar()
函数的定义放在整个代码块的最上方。
以下是修改后的代码示例:
def bar():
print("from bar")
def foo():
print(bar) # 打印bar()函数的内存地址
bar() # 内存地址加括号可以执行函数体代码
print("from foo")
foo()
这样修改后,首先定义了 bar()
函数,然后定义了 foo()
函数,并在其中调用了 bar()
函数。现在,代码将按预期运行,输出如下:
<function bar at 0x00000123ABCD>
from bar
from foo
请注意,函数的定义顺序对代码的执行顺序非常重要。确保在调用函数之前先定义它们。
形式2:有参函数
def func(x, y): # x=1 y=2
print(x, y)
func(1, 2)
# 1 2
形式3:空函数,函数体代码为pass或者三个点...
一般都是使用pass,遵循python原则
def func(x, y):
pass
func(1, 2)
# 无结果输出
def func(x, y):
...
func(1, 2)
# 无结果输出
三种定义方式各用在何处
判断函数是否被写死了,就是看需不需要外部传参
1. 无参函数的应用场景
# 无参函数,假如用到30个地方
def interactive():
name = input("输入姓名:")
age = input("输入年龄:")
gender = input("输入性别:")
msg = f"姓名:{name} 年龄:{age} 性别:{gender}."
print(msg)
interactive()
interactive()
输入姓名:小满
输入年龄:3
输入性别:女
姓名:小满 年龄:3 性别:女.
输入姓名:大乔
输入年龄:4
输入性别:女
姓名:大乔 年龄:4 性别:女.
2. 有参函数的应用场景
如果把函数想象成工厂,形参相当于函数的材料,返回值相当于产品。
def add(x, y): # 参数--> 原材料
result = x + y
return result # 产品
res = add(10, 2)
print(res) # 12
3. 空函数的应用场景
主要用于构思程序的时候,可以适量写提示语或者文档描述
def auth_user():
pass
def download_file():
pass
def upload_file():
pass
函数的调用
1. 语句的形式:只加括号调用函数
interactive()
add(1, 2)
2. 表达式形式
def add(x, y): # 参数 --> 原材料
result = x + y
return result
# 赋值表达式
r1 = add(1, 2)
print(r1) # 3
# 数学表达式
res = add(10, 2) * 10
print(res) # 120
3. 函数调用可以当做参数
def add(x, y): # 参数 --> 原材料
result = x + y
return result # 产品
# 函数调用可以当做参数
res = add(add(1, 2), 10) # 第一次add(1, 2)返回3 然后add(3, 10)结果13
print(res) # 13
4. 间接调用
def func(x, y):
print(x + y)
add = func
add(2, 3)
函数对象
精髓:可以把函数当成变量去用
func = 内存地址
def func():
print('from func')
可以被赋值
def func():
print('from func')
f = func # 指向了同一个内存地址
print(f)
print(func)
f()
# 结果如下
<function func at 0x0000021E45D2D700>
<function func at 0x0000021E45D2D700>
from func
可以当做函数的参数传给另外一个函数
def func():
print('from func')
def foo(x): # x = func的内存地址
print(x)
foo(func)
>>> <function func at 0x00000182DF17D700>
def func():
print('from func')
def foo(x): # x = func的内存地址
x()
foo(func)
>>> from func
可以当做另外一个函数的返回值
def func():
print('from func')
def foo(x): # x = func的内存地址
return x
res = foo(func)
res()
>>> from func
可以当做容器类型的一共元素
列表, 字典, 元组等....
def func():
print('from func')
l = [func]
print(l)
l[0]()
# 结果如下
[<function func at 0x0000028ED007D700>]
from func
def func():
print('from func')
dic = {'k1': func}
print(dic)
dic['k1']()
# 结果如下
{'k1': <function func at 0x000001CAA467D700>}
from func
函数的返回值
1. 什么时候需要返回值
看用户需求
理想状态是返回一个布尔类型的标志位和一个表达式
2. 创建返回值
- return是函数结束的标志,即函数体代码一旦运行到return会立刻终止函数的运行,并且会将return后的值当做本次运行的结果返回。
- 即便写了很多死循环,只要遇到return立马终止函数的运行
- 返回值
None
的几种情况:
- 函数体内没有return
- return后没有任何代码(即单独一个return)
def func():
while True:
while True:
while True:
while True:
while True:
print("就打印了一次就结束了")
# return 之后什么都不写
return
res = func()
print(res)
就打印了一次就结束了
None
返回一个值
return 值
def func():
result = 10
return result
res = func()
print(res) # 10
返回多个值
用逗号分割开多个值,会被return返回成元组
def func():
return 10, "小满", True, [3, 4], {1: 1}
res = func()
print(res, type(res))
# (10, '小满', True, [3, 4], {1: 1}) <class 'tuple'>
函数作为参数
def plus(num):
return num + 10
def handler(func):
res = func(100)
msg = "执行func,并获取到的结果为:{}".format(res)
print(msg) # 执行func,并获取到的结果为:110
# 执行handler函数,将Plus作为参数传递给handler的形式参数func
handler(plus)
代码解读
这段代码定义了两个函数plus(num)
和handler(func)
,并进行了函数调用。下面是对代码的解读:
-
定义了一个函数
plus(num)
,它接受一个参数num
,并返回num
加上10的结果。 -
定义了一个函数
handler(func)
,它接受一个函数作为参数func
。在函数体内,它使用传入的函数func
,并将参数100
传递给func
进行调用,将调用结果赋值给变量res
。然后,使用字符串格式化的方式构建了一条消息msg
,其中包含了func
函数的执行结果。最后,通过打印输出了这条消息。 -
调用
handler(plus)
,将plus
函数作为参数传递给handler
函数。在handler
函数内部,它将接收到的plus
函数进行调用,并将参数100
传递给plus
函数。plus(100)
的结果为110
,赋值给res
。然后,构建一条消息msg
,其中包含了110
,并打印输出这条消息。
总结:这段代码展示了函数作为参数的概念。通过将函数作为参数传递给另一个函数,可以在接收函数的函数内部对其进行调用。在这个例子中,handler
函数接收到plus
函数作为参数,然后调用plus
函数并获取执行结果,最后打印输出结果。在最后一行代码中,调用handler(plus)
实际上是将plus
函数传递给handler
函数进行处理。
函数作为返回值
def plus(num):
return num + 100
def handler():
print("执行handler函数")
return plus
result = handler() # result = plus
data = result(20) # 函数加括号 执行Plus(20)
print(data) # data 相当于返回值
代码解读
这段代码定义了两个函数plus(num)
和handler()
,并进行了一系列函数调用和赋值操作。下面是对代码的解读:
-
定义了一个函数
plus(num)
,它接受一个参数num
,并返回num
加上100的结果。 -
定义了一个函数
handler()
,它在执行时打印一条消息"执行handler函数",然后返回plus
函数本身(没有调用plus
函数,只是返回了函数对象)。 -
执行
handler()
函数,并将返回的结果赋值给变量result
。由于handler
函数返回的是plus
函数本身,所以result
也指向了plus
函数。 -
调用
result
函数,传入参数20
,并将结果赋值给变量data
。由于result
指向了plus
函数,所以这里实际上是调用了plus(20)
,将20
作为参数传递给plus
函数,得到结果120
,并将结果赋值给data
。 -
打印变量
data
,输出结果为120
。
总结:这段代码展示了函数的定义、函数的返回值可以是函数本身,函数的调用和结果的赋值等基本概念。通过handler
函数返回plus
函数本身,可以将函数作为对象进行传递和调用。最终,代码输出了plus(20)
的结果120
。
函数的参数
形参
在定义函数阶段的参数称之为形式参数,简称形参
相当于变量名
实参
在调用阶段传入的值成为实际参数,简称实参
相当于变量值
形参与实参的关系
- 在调用阶段,实参(变量值)会绑定给形参(变量名)
- 这种绑定关系只能在函数体内使用
- 实参与形参的绑定关系在调用时生效,函数调用结束后解出绑定关系
def func(x, y): # x y 形参
print(x, y)
func(1, 2) # 1 2实参,结果也是1 2
实参是传入的值,但是可以是以下形式
形式1
func(1, 2)
形式2
a = 1
b = 2
func(a, b)
形式3
func(int("1"), 2)
func(func(1, 2), func(2, 3), 333)
形参与实参的具体使用
位置参数
按照从左到右的顺序依次定义的参数称之为位置参数
位置形参
在函数定义阶段,按照从左到右的顺序直接定义的“变量名”
特点:必须被传值,多一个不行少一个也不行
def func(x, y):
print(x, y)
func(1, 2) # 1 2
位置不够,报错的一般是miss...
def func(x, y):
print(x, y)
func(1) # 报错TypeError: func() missing 1 required positional argument: 'y'
参数多了,报错的一般是but ... given
def func(x, y):
print(x, y)
func(1, 2, 3) # 报错TypeError: func() takes 2 positional arguments but 3 were given
位置实参
在函数调用阶段,按照从左到右的顺序依次传入的值
特点:按照顺序与形参一一对应
弊端:只能按照参数的位置传入指定的参数,一旦位置调换就会产生错乱
def func(x, y):
print(x, y)
func(1, 2) # 1 2
关键字实参
在调用函数阶段,按照key=value
的形式传入的值
特点:指名道姓的给某个形参传值,可以完全不参考顺序
def func(x, y):
print(x, y)
func(y=2, x=1) # 1 2
混合使用(强调)
-
位置参数必须放在关键之参数之前
一般报错 follows ...
def func(x, y): print(x, y) func(1, y=2) # 1 2 func(y=2, 1) # 报错 语法错误 SyntaxError: positional argument follows keyword argument
-
不能为同一形参重复传值
一般报错 multiple ...
def func(x, y): print(x, y) func(1, y=2, x=3) # 报错 func(1, 2, x=3, y=4) # 报错 # TypeError: func() got multiple values for argument 'x'
默认形参
在定义函数阶段,就已经被赋值的形参,称之为默认形参
特点:在定义阶段就已经被赋值,意味着在调用阶段可以不用为其赋值
如果默认参数不想填写,可以写一个
None
def func(x, y=3):
print(x, y)
func(x=1) # 1 3
func(5) # 5 3
func(x=1, y=444) # 1 444
什么情况下使用位置参数和默认形参
看情况,实例分析注册功能
def register(name, age, gender="男"):
print(name, age, gender)
register("老夫子", 18)
register("夏侯淳", 19)
register("阿珂", 16, "女")
register("小满", 3, "女")
老夫子 18 男
夏侯淳 19 男
阿珂 16 女
小满 3 女
位置形参与默认形参混用(强调)
-
位置形参必须在默认形参的左边
def func(y=2, x): # 报错 语法错误 SyntaxError: non-default argument follows default argument pass
正确演示
def func(x, y=2): pass
-
默认参数的值是在函数定义阶段被赋值的,准确的说被赋值的是内存地址
示范1
m = 2 def func(x, y=m): # y --> 2的内存地址 print(x, y) # 1 2 func(1)
示范2
m = 2 def func(x, y=m): # y --> 2的内存地址 print(x, y) # 1 2 m = 333333333 func(1)
示范3
m = [11111] def func(x, y=m): # y --> [11111]的内存地址 print(x, y) # 1 [11111, 3333333333] m.append(3333333333) func(1)
虽然默认值可以被指定为任意数据类型,但是不推荐使用可变类型
解决可变类型形参
如果有默认参数为可变类型的需求,下面是推荐的写法,将形参默认值设置为None内部进行判断,增加可变类型
def func(x, y, z, ls=None):
if ls is None:
ls = []
ls.append(x)
ls.append(y)
ls.append(z)
print(ls)
new_ls = [44, 55, 66]
func(1, 2, 3)
func(77, 88, 99, new_ls)
[1, 2, 3]
[44, 55, 66, 77, 88, 99]
函数最理想的状态
函数的调用只跟函数本身有关系,不受外界代码的影响
可变长度参数(*与**的用法)
可变长度指的是在调用函数时,传入的值(实参)的个数不固定,而实参是用来为形参赋值的,所以对应着,针对溢出的实参必须有对应的形参来接收。
可变长度的位置参数
*
形参名:用来接收溢出的位置参数,溢出的位置实参会被*
保存成元组的格式,然后赋值给紧跟其后的形参名
*
后面跟的可以是任意名字,但是约定俗成应该是args
def func(x, y, *args): # (3, 4, 5, 6)
print(x, y, args)
print(type(args))
func(1, 2, 3, 4, 5, 6)
1 2 (3, 4, 5, 6)
<class 'tuple'>
def my_sum(*args):
count = 0
for index in args:
count += index
return count
res = my_sum(1, 24, 4, 5, 7)
print(res) # 41
可变长度的关键字参数
**
形参名:用来接收溢出的关键字实参,**
会将溢出的关键字实参保存成字典格式,然后赋值给紧跟其后的形参名,**
后跟的可以是任意名字,但是约定俗成应该是kwargs
def func(x, y, **kwargs):
print(x, y, kwargs)
print(type(kwargs))
func(1, y=2, a=1, b=2, c=3)
1 2 {'a': 1, 'b': 2, 'c': 3}
<class 'dict'>
*
可以用在实参中,实参中带*
,先*
后被打散成位置实参需要注意的是,数量需要一一对应,实参
*
后只要是可以被for循环的类型都行(list str tuple ...
)
def func(x, y, z):
print(x, y, z)
func(*[11, 22, 33])
ls = [44, 55, 66]
func(*ls)
11 22 33
44 55 66
def func(x, y, z):
print(x, y, z) # x y z
func(*{"x": 1, "y": 2, "z": 3})
形参与实参都带*
def func(x, y, *args):
print(x, y, args) # 1 2 ([3, 4, 5, 6],)
func(1, 2, [3, 4, 5, 6])
def func(x, y, *args):
print(x, y, args) # 1 2 (3, 4, 5, 6)
func(1, 2, *[3, 4, 5, 6])
def func(x, y, *args):
print(x, y, args) # h e ('l', 'l', 'o')
func(*"hello")
**
可以用在实参中(**
后面跟的只能是字典),实参中带**
,先**
后的值打散成关键字实参
def func(x, y, z):
print(x, y, z) # 1 2 3
func(**{"x": 1, "y": 2, "z": 3}) # 理解为 func(x=1, y=2, c=3)
形参与实参都带
*
def func(x, y, **kwargs):
print(x, y, kwargs) # 111 222 {'a': 333, 'z': 444}
func(**{"y": 222, "x": 111, "a": 333, "z": 444}) # func(y=222, x=111, a=333, z=444)
混用*
与**
*args
必须在**kwargs
之前
def func(**kwargs, *args):
pass
# 结果报错 语法错误
def func(*args, **kwargs):
print(args, kwargs) # (1, 2, 3, 4, 5) {'a': 6, 'b': 7, 'c': 9}
func(1, 2, 3, 4, 5, a=6, b=7, c=9)
拓展(掌握好这个对语法糖非常有帮助)
def index(x, y, z):
print("index======>", x, y, z) # index======> 1 2 3
def wrapper(a, b, c):
index(a, b, c)
wrapper(1, 2, 3)
关键字命名参数(了解)
命名关键字参数:
在定义函数时,*
后自定义的参数,如下所示,称之为命名关键字参数
特点
命名关键字实参必须按照
key=value
的形式为其传值不传参不行,位置参数也不行
def func(x, y, *, a, b): # # 其中a和b成为命名关键字参数
print(x, y) # 1 2
print(a, b) # 222 111
func(1, 2, a=222, b=111)
*
后面的key=value并不是默认参数
# 不会报错
def func(x, y, *, b=222, a): # 次处的b就不叫默认参数了,可以理解为为关键字参数b赋了一个默认值
print(x, y) # 1 2
print(a, b) # 222 222
func(1, 2, a=222)
组合使用(了解)
基本上没有应用场景,仅做了解.
形参混用的顺序:位置形参,默认形参,
*args
, 命名关键字形参,**kwargs
def func(a, b, c, *args, x=500, y, **kwargs):
print('a=', a)
print('b=', b)
print('c=', c)
print('x=', x)
print('y=', y)
print('args=', args)
print('kwargs=', kwargs)
func(20, 50, '纯二', '哈利波特', '会魔法', y=9527, music='天空之城', movie='哈尔的移动城堡', author='宫崎骏')
a= 20
b= 50
c= 纯二
x= 500
y= 9527
args= ('哈利波特', '会魔法')
kwargs= {'music': '天空之城', 'movie': '哈尔的移动城堡', 'author': '宫崎骏'}
实参混用的顺序:
遵循位置参数在关键字参数左边就行,包括*
和**
的参数
def func(x, y, z, a, b, c):
print('x=', x)
print('y=', y)
print('z=', z)
print('a=', a)
print('b=', b)
print('c=', c)
func(111, 333, 444, a=222, **{'b':555, 'c':666})
# func(111, 333, 444, a=222, b=555, c=666)
x= 111
y= 333
z= 444
a= 222
b= 555
c= 666
def func3(a1, a2, a3, a4=10, *args, a5=20, **kwargs):
print(a1, a2, a3, a4, a5, args, kwargs)
func3(11, 22, 33, 44, 55, 66, 77, a5=10, a10=123)
11 22 33 44 10 (55, 66, 77) {'a10': 123}
案例演示
不好的演示(x)
太多的elif判断,而且不方便功能的增删(函数)这样写代码大部分会被公司炒掉。不推荐这样写...
def login():
print('登录功能')
def transfer():
print('转账功能')
def check_banlance():
print('查询余额')
def withdraw():
print('提现功能')
def register():
print('注册功能')
while True:
print("""
0 退出
1 登录
2 转账
3 查询余额
4 提现
5 注册
""")
choice = input('请选择功能:')
if not choice.isdigit():
print('必须输入编号,傻叉')
continue
if choice == '0':
break
if choice == '1':
login()
elif choice == '2':
transfer()
elif choice == '3':
check_banlance()
elif choice == '4':
withdraw()
elif choice == '5':
register()
else:
print('输入的指令不存在')
优化后(√)
优化后通过字典的值作为函数的名字, 下面的判断如果选择在字典中,直接取到字典的值(函数)加括号就是执行具体函数的内部代码了.
def login():
print('登录功能')
def transfer():
print('转账功能')
def check_banlance():
print('查询余额')
def withdraw():
print('提现功能')
def register():
print('注册功能')
dic = {
'1': login,
'2': transfer,
'3': check_banlance,
'4': withdraw,
'5': register
}
while True:
print("""
0 退出
1 登录
2 转账
3 查询余额
4 提现
5 注册
""")
choice = input('请选择功能:')
if not choice.isdigit():
print('必须输入编号,傻叉')
continue
if choice == '0':
break
if choice in dic:
dic[choice]()
else:
print('输入的指令不存在')
进一步优化(get√)
优化后只需要修改字典就行, 因为字典里面键的元素运行后不需要被修改,所以本着严谨的态度,这里推荐使用元组而不是列表.
def login():
print('登录功能')
def transfer():
print('转账功能')
def check_banlance():
print('查询余额')
def withdraw():
print('提现功能')
def register():
print('注册功能')
dic = {
'0': ('退出', None),
'1': ('登录', login),
'2': ('转账', transfer),
'3': ('查询余额', check_banlance),
'4': ('提现', withdraw),
'5': ('注册', register)
}
while True:
for k, v in dic.items():
print(k, v[0])
choice = input('请选择功能:')
if not choice.isdigit():
print('必须输入编号,傻叉')
continue
if choice == '0':
break
if choice in dic:
dic[choice][1]()
else:
print('输入的指令不存在')
函数嵌套
- 函数的嵌套调用:在调用了一共函数的过程中又调用了其他的函数
实例:比较大小
def max2(x, y):
if x > y:
return x
else:
return y
def max(a, b, c, d):
# 第一步:比较a, b得到res1
res1 = max2(a, b)
# 第二步:比较res1, c得到res2
res2 = max2(res1, c)
# 第三步:比较res2, d得到res3
res3 = max2(res2, d)
return res3
res = max(1, 2 ,3 ,4)
print(res)
>>> 4
函数的嵌套定义:在函数内定义其他函数
实例:求圆形的面积和周长
def cirale(radius, action=0):
from math import pi
# 求圆形的周长 2*pi*radius
def perimiter(radius):
return 2*pi*radius
# 求圆形的面积 pi*(radius**2)
def area(radius):
return pi*(radius**2)
if action == 0:
return perimiter(radius)
elif action == 1:
return area(radius)
res = cirale(33, action=0)
print(res)
>>> 207.34511513692635
总结:
- 建议按关键字传参
- 返回值一般建议返回一个标志位布尔值如果有表达式可以返回一个标志位和表达式
- 函数的理想状态
- 函数的调用只跟函数本身有关系,不受外界代码的影响
*
args只能按照位置传参**
kwargs只能按照关键字传参*
和**
混用时,**
只能放在*
的后面- 参数和动态参数混合时,动态参数只能放在最后
- 命名关键字参数
*
后面的参数只能通过关键字传参 - 写函数功能的时候,尽量不要让代码超过一屏(屏幕)不然很low,就是不要有太多冗余的代码。
本文作者:小满三岁啦
本文链接:https://www.cnblogs.com/ccsvip/p/17880234.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。