什么是函数
函数就相当于具备某一功能的工具
使用函数必须遵循一些规则:
- 先定义
- 后调用
为何要使用函数
- 组织结构不清晰,可读性差
- 代码冗余,臃肿
- 因为代码冗余导致可维护性,扩展性差
函数的定义
函数是一个工具,函数名应该定义为动词,而不是名词。
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
函数的返回值
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 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'
默认形参
在定义函数阶段,就已经被赋值的形参,称之为默认形参
特点:在定义阶段就已经被赋值,意味着在调用阶段可以不用为其赋值
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
标签:基本,bar,函数,形参,func,使用,print,def
From: https://www.cnblogs.com/ccsvip/p/17880234.html