函数的调用方式、函数的参数、类型提示语、名称空间
【一】函数的调用方式
def student(name, age):
print(f"my name is {name} and my age is {age}")
[1]直接调用函数
- 函数名()-----> 调用函数
student(name = 'max', age = 18)
[2]用表达式调用函数
- 用一个新变量存储函数的内存地址然后调用
def add(x,y):
return x + y
result = add(2, 5) + 9
print(result)# 16
- 函数作为返回值
def delete(a, b):
return add(a, b)
print(delete(6, 9)) #15
def add(x,y):
return x + y
def func():
return add
foo = func()
print(foo(1, 9)) #10
[3]函数可以作为参数传递给另一个函数
def add(x,y):
return x + y
def login(func):
return func()
login(add) #所以login()函数中func参数进来的是add()中的x,y
【二】函数的参数
[1]形参和实参
- 形参即在定义函数时,括号内声明的参数。
- 形参本质就是一个变量名,用来接收外部传来的值。
- 实参即在调用函数时,括号内传入的值
- 值可以是常量、变量、表达式或三者的组合:
def add(x, y):
return x + y
# 1:实参是常量
res_first = add(1, 2)
print(res_first) # 3
# 2:实参是变量
x = 5
y = 6
res_second = add(x, y)
print(res_second) #11
# 3:实参是表达式
res_third = add(10 * 2, 10 * add(3, 4))
print(res_third) #90
# 4:实参可以是常量、变量、表达式的任意组合
a = 2
res_forth = add(a, 10 * add(3, 4))
print(res_forth) #72
-
在调用有参函数时,实参(值)会赋值给形参(变量名)
-
在Python中,变量名与值只是单纯的绑定关系,而对于函数来说,这种绑定关系只在函数调用时生效,在调用结束后解除。
[2]位置此参数
- 位置即顺序位置参数是按顺序定义的参数:
- 在定义函数时,按照从左到右的顺序依次定义形参,称为位置形参
- 凡是按照这种形式定义多的形参都必须被传值
(1)有位置参数而不传值会报错
# 定义位置形参:name,age,sex,三者都必须被传值
def register(name, age, sex):
print('Name:%s Age:%s Sex:%s' % (name, age, sex))
register() # TypeError:缺少3个位置参数
(2)对应位置传参
# 定义位置形参:name,age,sex,三者都必须被传值
def register(name, age, sex):
print('Name:%s Age:%s Sex:%s' % (name, age, sex))
register("max", 18, "男")
# Name:max Age:18 Sex:男
- 在调用函数时,按照从左到右的顺序依次定义实参,称为位置实参
- 凡是按照这种形式定义的实参会按照从左到右的顺序与形参一一对应
[3]关键字参数
在调用参数是,实参可以是 key=value 的形式
- 凡是关键字传参,可以完全不按照从左到右的顺序定义,但仍能为指定的形参赋值
def student(name, age, gender):
print(f"my name is {name} and my age is {age} and my gender is {gender}")
student('max',age=18,gender='male')
#输出结果:
my name is max and my age is 18 and my gender is male
# student(age=18,'max',gender='male')
# 这个是错误的,位置参数必须在关键字参数之前('max'必须在前面)
[4]默认参数
- 在定义函数时,就已经为形参赋值,这类形参称之为默认参数
- 当函数有多个参数时,需要将值经常改变的参数定义成位置参数,而将值改变较少的参数定义成默认参数
(1)默认参数不传值
# 定义位置形参:name,age 必须被传值 sex默认值为 男
def register(name, age, sex="男"):
print('Name:%s Age:%s Sex:%s' % (name, age, sex))
# 可以使用关键字和位置参数混合传参,但是一定要位置参数在前,关键字参数在后
# 参数有默认值,可以不传值
register("max", age=18)
# Name:max Age:18 Sex:男
(2)默认参数传值
【1】默认参数初始化
- 默认参数必须在位置参数之后
- 默认参数的值仅在函数定义阶段被赋值一次
# 定义位置形参:name,age 必须被传值 sex默认值为 男
def register(name, age, sex="男"):
print('Name:%s Age:%s Sex:%s' % (name, age, sex))
# 可以使用关键字和位置参数混合传参,但是一定要位置参数在前,关键字参数在后
# 参数有默认值,重复传值会覆盖掉默认值
register("Max", age=18, sex="女")
# Name:Max Age:18 Sex:女
【2】默认参数为可变数据类型
- 默认参数的值通常应设为不可变类型
- 每次调用是在上一次的基础上向同一列表增加值
def append_list(num, list_inner=[]):
list_inner.append(num)
return list_inner
# 传入参数 1 ,对应在函数中的空列表参数中添加值
res_1 = append_list(1)
print(res_1) # [1]
# 由于列表是可变数据类型,所以在上面的基础上,再添加值会添加到上一个列表内
res_2 = append_list(2)
print(res_2) # [1, 2]
# 同理
res_3 = append_list(3)
print(res_3) # [1, 2, 3]
【3】默认参数为空
- 默认参数的值通常应设为不可变类型
def append_list(num, list_inner=None):
if list_inner is None:
list_inner = []
list_inner.append(num)
return list_inner
# 传入参数 1 ,对应在函数中的参数为空
# 在函数内部会有一个判断,不传值为空,则列表重置为空
res_1 = append_list(1)
print(res_1) # [1]
# 在函数内部会有一个判断,不传值为空,则列表重置为空
res_2 = append_list(2)
print(res_2) # [2]
# 在函数内部会有一个判断,不传值为空,则列表重置为空
res_3 = append_list(3)
print(res_3) # [3]
[5]可变长参数
- 参数的长度可变指的是在调用函数时,实参的个数可以不固定
- 而在调用函数时,实参的定义无非是按位置或者按关键字两种形式
- 这就要求形参提供两种解决方案来分别处理两种形式的可变长度的参数
【1】可变长位置参数(*args)
[1]任意类型参数
- 如果在最后一个形参名前加 * 号,那么在调用函数时,溢出的位置实参,都会被 * 接收,以元组的形式保存下来赋值给该形参
# 在最后一个形参名args前加*号
def foo(x, y, z=1, *args):
print(x)
print(y)
print(z)
print(args)
foo(1, 2, 3, 4, 5, 6, 7)
# 实参1、2、3按位置为形参x、y、z赋值
# 多余的位置实参4、5、6、7都被*接收,以元组的形式保存下来,赋值给args
# 即args=(4, 5, 6,7)
def student(name, age, gender='male', *pppp):
print(name)
print(age)
print(gender)
print(pppp)
# student('dream', 18, 'male', 999, 888, 666)
num_list = [1,2,3,4,5,6,7,8,9,10]
num_list_tuple = (1,2,3,4,5,6,7,8,9,10)
name = 'dream'
# 解包语法 : * + 可迭代类型 就能将每一个元素单独解包出来
print(num_list)
print(*num_list)
print(*num_list_tuple)
print(*name)
student('dream', 18, 'male', num_list)
student('dream', 18, 'male', *num_list)
【2】命名关键字参数
'''
def student(name, age, gender, class_id, addr):
print(name, age, gender, class_id, addr)
# 调用上面的函数传递参数可以按照关键字传递参数
# 也可以按照位置传递参数
student('dream', 18, 'male', 2, '上海')
'''
# 在位置参数中间指定 * 隔开,
# * 之前的位置参数支持位置和关键字传参数
# * 后面的参数必须按照关键字传参数
'''
def student(name, age, gender,*, class_id, addr):
print(name, age, gender, class_id, addr)
student('dream', 18, 'male',class_id= 2, addr='上海')
'''
# 在命名关键字参数中指定默认参数的时候,默认参数允许放在位置参数之前
# 但是普通的位置参数是不允许关键字参数放在位置参数之前的
'''
def student(name, age, gender='male',*, class_id=2, addr):
print(name, age, gender, class_id, addr)
student('dream', 18, 'male',class_id= 2, addr='上海')
'''
[6]混合使用
# 综上所述所有参数可任意组合使用,但定义顺序必须是:
# 位置参数、默认参数、*args 、**kwargs
# 所有位置参数、默认参数、*args 、**kwargs 混合使用的时候不允许使用关键字参数
# def student(name, age=18,*args,**kwargs):
# 所有位置参数、默认参数、*args 、**kwargs | **kwargs 必须在 *args 之后
'''
def student(name, age=18, *args, **kwargs):
print(...)
'''
【三】类型提示语Optional
- 什么是注解
- 在定义函数的位置参数的时候可以指定参数的类型
def student(name: str, age: int, score: float, is_male: bool):
print(name, type(name))
print(age, type(age))
print(score, type(score))
print(is_male, type(is_male))
# 按照在定义函数的时候声明的参数类型传递参数
student('dream', 18, 100.0, True)
'''
# 【2】可变数据类型
# 字典 列表 元组 集合
# 字典:{键:值} ---> 约束二者
'''
from typing import Dict, List,Tuple,Set
def student(user_data: Dict[str, int], id_list: List[int],num_tuple:Tuple[str],num_set:Set[int]):
print(user_data, type(user_data))
student({'age': 18}, [1],('1',),{2,3})
'''
# 【三】约定函数的返回值类型
'''
def add(x:int,y:int)->int:
return x + y
print(add(1,2))
'''
# 【四】函数返回值既可以是字符串也可以是数字
# union 既可以返回一个又可以返回另一个类型
'''
from typing import Union
def add(x: int, y: int) -> Union[str, int]:
# return x + y
# return str(x + y)
return True
'''
#【五】可以返回可选的采纳数类型
# 可以返回None ,也可以返回 int
'''
from typing import Optional
def add(x: int, y: int) ->Optional[None]:
return 1
print(add(1,2))
'''
【四】名称空间
[1]什么是名称空间
- 名称空间就是存放函数名与函数值对应关系的地方
- 变量名 = 变量值
- 先开辟一块内存空间 ,把函数值扔到内存空间中 ---> 内存空间
- 然后你需要 用变量名 和 变量值进行绑定 ---> 名称空间
[2]名称空间的分类
(1)内建名称空间
- 会随着代码启动而生成,随着代码结束而关闭
- 第一个加载的名称空间
- if def else
(2)全局名称空间
# 随着内建名称空间加载完成
# 走到了你自己写的代码里面
# 自己创建的变量名 / 函数名 / 类名
'''
# 这个列表是全局的列表
# 列表是可变数据类型 ---> 列表被修改后id不变
num_list = []
print(id(num_list)) # 1200291395392
def add_list(num):
num_list.append(num)
print(id(num_list)) # 1200291395392
add_list(1)
add_list(2)
add_list(3)
print(num_list) # [1, 2, 3]
print(id(num_list)) # 1200291395392
'''
(3)局部名称空间
# 在函数内部或者是类内部定义的变量
# 随着函数启动而创建,随着函数结束而消失
# 这个列表是全局的列表
# 列表是可变数据类型 ---> 列表被修改后id不变
def add_list(num):
# 垃圾回收机制 ---> 节省内存开销
num_list = []
num_list.append(num)
print(num_list)
print(id(num_list)) # 1200291395392
add_list(1)
add_list(2)
add_list(3)
(4)名称空间的加载顺序
# 先加载内建名称空间
# 其次是全局名称空间
# 最后就是局部名称空间
(5)名称空间的查找顺序
# 如果你在全局查
# 先全局后内建 --> 在 py 文件中执行代码
# 如果在局部
# 先局部再全局再内建 ---> 在函数内部执行代码
# 所有的变量 尤其是 可变数据类型 字典 列表
# 尽量写在文件开头 , 如果放在函数下面可能会导致检索不到变量
num_list = []
def add_list(num):
num_list.append(num)
print(num_list)
add_list(num=1)