函数的基本使用
什么是函数
函数就类似于具备某一功能的工具,提前定义好之后可以反复使用(即:函数就是盛放代码和功能的容器)
函数的使用原则:
先定义 ,后调用
为何要用函数==>解决下述问题:
如何用函数
函数定义的语法:
def 函数名(参数1,参数2,参数3,...):
'''函数的注释'''
函数体代码
return 返回值
'''
1.def: (必须)
定义函数的关键字;
2.函数名:(必须)
函数名指向函数内存地址,是对函数体代码的引用。函数的命名应该反映出函数的功能;
3.括号冒号:(必须)
括号后要加冒号,然后在下一行开始缩进编写函数体的代码;
4.参数:(可选)
括号内定义参数,参数是可有可无的,且无需指定参数的类型;
5.函数注释: (可选)
描述函数功能,参数介绍,非必要,但是建议加上,从而增强函数的可读性;
6.函数体:(必须)
由语句和表达式组成;
7.return值:(可选)
定义函数的返回值,return是可有可无的。
'''
函数的定义与调用
定义函数
定义函数发生的事情
- 1、在内存空间中申请一块内存空间存储函数体代码
- 2、将函数体代码所在的内存空间地址绑定给函数名
强调:定义函数只检测语法,不执行代码
def login(): # login = 函数的内存地址
print(111) # 函数体代码
print(222)
x = 10 # x = 10的内存地址
print(x) # 10
'''
原则上打印应该返回内存地址,但是返回的是值。
变量名受到python解释器特殊照顾由内存地址直接找到返回值打印。
'''
print(login) # <function login at 0x7fa136db91f0>
'''
但函数名不同,打印显示的是内存地址。定义函数的目的是为了把代码丢到容器,
只需要运行不需查看。则无需被照顾。
'''
调用函数
调用函数发生的事情:
- 1、先通过函数名定位到函数的内存地址
- 2、函数名加括号会触发函数体代码的运行
强调:调用函数才会执行函数体代码
# 示例:
def login():
print('hello')
login() # hello 触发函数体代码的运行
# 语法错误
def func():
print(111 # 无效语法,定义阶段都无法通过
# 逻辑错误
def func():
print(111)
x # x 没有定义,变量名也需要先定义后引用
func() # 调用肯定出错
# 示例:
def bar():
print('from bar')
def foo():
print('from foo')
bar()
foo()
'''
from foo
from bar
'''
# 语法错误
def foo():
print('from foo')
bar()
foo() # bar没有定义,调用肯定出错
def bar():
print('from bar')
定义函数的三种形式
1.无参函数
- 函数在定义阶段括号内没有参数
# 函数体不需要外部传参数进行,它本身代码就是固定的,所以就是无参函数
def say():
print('welcome to here')
say()
# 参数是函数的调用者向函数体传值的媒介,有参无参是根据函数体代码去走的,看函数体代码是否需要外部传参数
def login():
inp_name = input("请输入用户名>>>: ").strip()
inp_pwd = input("请输入密码>>>: ").strip()
if inp_name == "jack" and inp_pwd == "123":
print('登录成功!')
else:
print("用户名或密码错误")
login() # 直接调用无需传参数,input功能已经实现与用户交互
2.有参函数
- 函数在定义阶段括号内有参数
# 函数体代码需要外部传参数进行,这种函数就需要定义为有参函数
def max2(x, y): # 参数---》原材料
if x > y:
print(x)
else:
print(y)
max2(10,20)
3.空函数
- 函数在定义阶段括号内没有参数,并且函数体代码为空(pass)
# 函数体为pass代表什么都不做,称之为空函数。定义空函数通常是有用的,因为在程序设计的开始,往往是先想好程序都需要完成什么功能,然后把所有功能都列举出来用pass充当函数体“占位符”,这将使得程序的体系结构清晰且可读性强。例如要编写一个程序,我们可能想到的功能有用户认证,下载,上传,等功能,可以先做出如下定义:
def auth_user():
"""用户认证"""
pass # 代表占位符
def download_file():
"""下载文件"""
pass
def upload_file():
"""上传文件"""
pass
调用函数的三种形式
# 1.语句
len("hello") # 单纯的调用,例如len统计hello的长度
# 2.表达式
res = len("hello") # 将调用函数的返回值赋值给res
res = len("hello") * 10 # 将调用函数的返回值乘以10的结果赋值给res
print(res)
# 3 .可以当做参数传给另外一个函数
len("hello") # 从形式上看就是将函数调用,从本质讲是跟2同理
print(len("hello")) # 先调len的返回值然后再赋值给print
函数的返回值
return 详解
def max(x, y):
if x > y:
return x
else:
return y
res = max(10, 20)
print(res)
1、return是一个函数结束的标志,函数可以有多个return,但只要执行一次return,整个函数就立即结束,并且将return后的值当做本次调用的结果返回
```python def func(): print(“=====”) # 只运行到了此行 return 111 # return后的值当做本次调用的结果返回 print(“**“) # ruturn 下面的值不会被执行 return 222
res = func() print(res)
‘’’
111 ‘’’
-
<a name="0cee9588"></a>
#### 2、return后的返回值有三种情况
- 1)函数内可以没有return 或者 return None 或者 return:返回的都是None
- 2)return 值:返回的就是这一个值
- 3)return 值1,值2,值3:会自动组织成一个元组返回
```python
# 示例1:
def func():
print(111)
return
print(2222)
res = func()
print(res)
'''
111
None
'''
# 示例2:
def func():
return 111,"xxx",[22,33]
res = func()
print(res) # (111,"xxx",[22,33])返回的是元组
函数参数的使用
形参与实参的关系:
我们可以将形参看成是变量名,实参看成是变量值,在调用函数时,实参值会绑定给形参名,在函数调用完毕后解除绑定
def func(x, y): # x, y是形参,在定义阶段()内传值。形参指的是变量名。
print(x, y)
func(111, 222) # 111,222是实参,在调用阶段传送的值,实参指的是变量值
"""
形参的表现形式只有一种就是变量名
实参的表现形式有很多种(但是把握核心就是:数据值)
"""
形参系列
位置形参
- 在函数定义阶段按照从左往右的顺序依次定义的变量名
- 特点:
- 每次调用必须被赋值,按照位置一一对应
- 位置参数在绑定的时候多一个不行少一个也不行
def func(x, y): # 定义位置形参:x,y,两者都必须被传值
print(x)
print(y)
func(1, 2) # 可以正常赋值,必须刚刚好
func(1, 2, 3) # 报错,多了一个值
func(1) # 报错,少了一个值
默认形参
- 在函数定义阶段就已经为某个形参赋值了
- 特点:
- 在函数调用阶段,可以不用为其赋值,则使用默认的
- 在函数调用阶段,也可以继续为其赋值, 则使用你给的
def func(name, age=18): # age=18是默认形参,无需赋值,也可以被赋值
print(name)
print(age)
func("jack") # name正常赋值
func("jack", 19) # 正常赋值
# 例一:
def func(name, hobby, hobbies = []): # 三次调用都用的是同一列表
hobbies.append(hobby)
print('%s的爱好是%s'%(name,hobbies))
func('jack','read') # jack的爱好是['read']
func('tom','play') # tom的爱好是['read', 'play']
func('bob','movie') # bob的爱好是['read', 'play', 'music']
'''格式越简单的越靠前 格式越复杂的越靠后'''
# 例二:
m = 111
def func(x, y ,z=m): # 在定义阶段就把111赋值给了z
print(x)
print(y)
print(z)
m = 666
func(1, 2)
实参系列
位置实参
- 在函数调用阶段括号内按照从左往右的顺序依次传入的数据值
- 特点:
- 按照位置为形参赋值,按照位置一一对应
- 位置参数在绑定的时候多一个不行少一个也不行
def func(name, age): # 定义位置形参:name,age,两者都必须被传值
print(name)
print(age)
func("jack") # 报错,少了一个值
func("jack",19) # 正常传值
关键字实参
- 在函数调用阶段,按照 形参名 = 数据值 的形式指名道姓的传值
- 特点:可以打乱顺序,但是仍然能够指定道姓的为指定的形参赋值
def func(name, age): # 定义位置形参:name,age,两者都必须被传值
print(name)
print(age)
func(age = 18,name = "jack") # 正常传值
# 例一:
def func(name, age): # 定义位置形参:name,age,两者都必须被传值
print(name)
print(age)
func("jack",age = 18) # 正常传值
func(age = 18,"jack") # 报错,语法错误
'''格式越简单的越靠前 格式越复杂的越靠后'''
# 例二:
def foo(x, y, z):
pass
foo(1, y=2,3) # 报错,语法错误
foo(1, y=2, z=3, x=4) # 报错,语法错误
可变长参数
- 可变长参数指的是在函数调用阶段,传入的实参个数不固定,对应着必须有特殊形式的形参来接收多余的实参
- 实参无非两种形式
- 多余的位置实参 *
- 多余的位置关键字实参 **
与*在形参中是一种汇总行为
* 在形参中的使用
# 函数无论传入多少位置参数都可以正常运行
def func(x, *y): # *是接收多余位置实参的功劳,不是y变量名的功劳。*后面可以跟任意变量名。
print(x)
print(y)
func(1, 2, 3, 4, 5) # x = 1 , y = (2,3,4,5)
'''
1
(2,3,4,5)
'''
def func(x, *args): # args变量名通常搭配*使用
print(x)
print(args)
func(1, 2, 3, 4, 5) # 同上是一个道理
def my_sum(*args): # 求和运算的函数
res = 0
for i in args:
res += i
print(res)
my_sum(1, 2) # 3
** 在形参中的使用
# 函数无论传入多少关键字参数都可以正常运行
def func(x, **kwargs): # kwargs变量名通常搭配**使用
print(x)
print(kwargs)
func(1,a=2,b=3,c=4)
'''
1
{'a': 2, 'b': 3, 'c': 4}
'''
# 定义一个函数无论传入多少位置参数和关键字参数都可以正常运行
def index(*a,**b):
print(a,b)
index() # () {}
index(1,2,3,4) # (1, 2, 3, 4) {}
index(a=1,b=2,c=3) # () {'a': 1, 'b': 2, 'c': 3}
index(1,2,3,4,a=1,b=2,c=3) # (1, 2, 3, 4) {'a': 1, 'b': 2, 'c': 3}
"""
墨守成规
可变长形参 *与**后面的变量名其实是可以随便定义的
但是python中推荐使用
*args
**kwargs
def index(*args, **kwargs):
pass
"""
与*在实参中是一种打散行为
* 在实参中的使用
def func(x, y, z):
print(x)
print(y)
print(z)
func([11, 22, 33]) # 报错
func(*[11,22,33]) # func(11,22,33) *会把列表这种实参打散成位置实参
func(*"hello") # func("h","e","l","l","o") 报错,但能证明*的打散行为
func(*{"k1":111,"k2":2222}) # func("k1","k2") 报错,但证明*能把字典打散成key
** 在实参中的使用
# ** 只能跟字典类型
def func(x, y, z):
print(x)
print(y)
print(z)
func(**{"k1": 111, "k2": 2222}) # func(k2=2222,k1=111) 报错,没有k关键字,打散后对应不上
func(**{"x": 111, "y": 2222, "z": 333}) # **将字典打散成关键字实参
'''
111
2222
333
'''
# 例1:
def wrapper(*args, **kwargs): # 形参中带*和**是一种汇总行为
print(args)
print(kwargs)
wrapper(1, 2, 3, 4, 5, 6, a=1, b=2, c=3)
'''
(1, 2, 3, 4, 5, 6)
{'a': 1, 'b': 2, 'c': 3}
'''
# 例2:
def index(x, y, z):
print(x, y, z)
def wrapper(*args, **kwargs):
index(*args, **kwargs)
wrapper(1, y=2, z=3) # 1 2 3
'''
实参中带 * 和 ** 是一种打散行为
index(*(1, 2, 3, 4, 5, 6), **{"a": 1, "b": 2, "c": 3})
index(1, 2, 3, 4, 5, 6, a=1, b=2, c=3)
'''
命名关键字形参: 在与*中间的形参称之为命名关键字形参(了解)
特点: 必须按照key=value的形式传值
def register(name,age,*,sex,height):
pass
# register('jason',18,'male',183)
register('lili',18,sex='male',height='1.8m') #正确使用
"""
sex height在传入实参的时候必须以关键字参数的形式
ps:该类型的参数几乎不用 也几乎很少能碰到
"""