函数的参数,名称空间,作用域

1 函数的调用方式

【1】直接调用

函数名()-->调用函数

def student(name, age):
    print(f"my name is {name} , my age is {age}")

# 函数名()-->调用函数
student(name="silence", age=16)
# my name is silence , my age is 16

【2】用表达式调用函数

用一个新变量存储函数的内存地址然后调用

def add(x, y):
    return x + y

result = add(2, 5)+ 9
print(result)  # 16

【3】函数作为返回值

def add(x, y):
    return x+y

def delete(a,b):
    return add(a, b)

print(delete(6,9)) # 15

【4】函数可以作为一个参数传递给另一个函数

def login(func):
    return func()
login(add())   # 相当于return中进入的是add()

2 函数的参数

【1】形参

  • 形参就是在函数定义阶段定义在函数名后面的()里的参数
  • 需要在函数调用的时候传递进去并且这个参数只在当前函数内部生效

【2】实参

  • 实参就是再调用函数的时候传递进去的那个真实的值
  • 可以按照位置和按照关键字传递参数
  • 实参可以是表达式,常量,变量
  • 表达式的意思就是表达式 1+1 1 x+y x/y

(1)按位置传参数

  • 有位置参数但是不传,参数不够,就会报错提示缺少必要的位置参数
  • 没有位置但是多传了,参数够了,但是给多了,提示只需要指定数量参数位置,但是给多了
  • 位置参数必须按照位置传递值,如果换位置就会导致函数内的参数错乱

(2)按照关键字传参数

  • 关键字传递参数的时候,要指定上函数定义时的位置参数名
  • 允许按照位置传递参数,并且指定上关键字
  • 允许不按照位置传递参数,指定上关键字

(3)位置参数和关键字参数连用

要传递的位置参数一定要在关键字参数之前否则会报错

def student(name, age, gender):
    print(f"my name is {name} and my age is {age} and my gender is {gender}")

student('silence',age=16,gender='male')  
# my name is silence and my age is 16 and my gender is male
student('silence',gender='male',age=16)
# my name is silence and my age is 16 and my gender is male

# 错误示例如下
#位置参数必须在关键字参数之前
student(age=18,'silence',gender='male')
# positional argument follows keyword argument

【3】默认参数

  • 在函数定义阶段的时候定义在函数名后面的 () 里面的参数可以给默认值
  • 位置参数给了默认值之后,在函数调用时,可以不指定默认参数的值,不给值默认就是默认值,给了新值就会被新值覆盖掉
def student(name, gender='male'):
    print(f' my name is {name} my gender is {gender}')

##给了name但是没给 gender , gender使用的就是默认的值 male
student('silence')   # my name is silence my gender is male
# 新值覆盖掉默认值
student('silence',gender='other')
my name is silence my gender is other

扩展内容:

# num_list 可变数据类型 ---> 定义一次下一次修改列表会影响到原来列表
def app_list(num, num_list=[]):
    num_list.append(num)
    print(num_list)
app_list(1)
app_list(2)
app_list(3)
# [1,2,3]  逐个进入循环后原来的会输出再将后续的也添加进来 所以最后是输出[1,2,3]


# num_list 每一次调用函数都是一个新的列表所以值只会是最后一个
def app_list(num):
    num_list = []
    num_list.append(num)
    print(num_list)
app_list(1)
app_list(2)
app_list(3) # [3] num_list 每一次调用函数都是一个新的列表,所以最后只有[3]


num_list = []
def app_list(num):
    num_list = []
    num_list.append(num)
app_list(1)
app_list(2)
app_list(3)
print(num_list)  # []  这个是输出外部的num_list  修改局部不会影响整体


#若要修改整体的
num_list = []
def app_list(num):
    global num_list
    num_list = []
    num_list.append(num)
app_list(1)
app_list(2)
app_list(3)
print(num_list)  # [3]

【4】可变长位置参数

可变长位置参数接收到多余的位置参数放到一个元组中

(1)什么是可变长位置参数

可变长位置参数 ,就是可以用一个变量来接收所有超出指定参数的位参数

在函数定义的时候在形参的位置增加 *args 用来接收多余的位置参数

args 默认的属性 但是是可以自己指定的

def student(name, age, gender='male', *pppp):
    print(name)
    print(age)
    print(gender)
    print(pppp)


student('silence', 18, 'male', 741, 159, 753)

# silence
# 18
# male
# (741, 159, 753)   *args 用来接收多余的位置参数


num_list = [1,2,3,4,5,6,7,8,9,10]
print(*num_list) 
# 1 2 3 4 5 6 7 8 9 10 在列表里会解出来一个个元素

解包:* + 可迭代类型 就能将每一个元素单独解包出来

# 解包语法:只能用在元组,列表等可迭代对象
# * + 可迭代类型 就能将每一个元素单独解包出来
def student(name, age, gender='male', *pppp):
    print(name)
    print(age)
    print(gender)
    print(pppp)

num_list = [1,2,3,4,5,6,7,8,9,10]

student('silence', 18, 'male', num_list)
# ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10],)
student('silence', 18, 'male', *num_list)
# (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

【5】可变长关键字参数

语法:*** + kwargs*

可变长关键字参数接收到多余的关键字参数放到一个字典中

def student(name, age, gender, *args, **kwargs):
    print(name, age, gender)
    print(f'args :>>>> {args}')
    print(f'kwargs :>>>> {kwargs}')


student('silence', 16, 'male', 888, 666, class_id=2, addr='上海')

# silence 16 male
# args :>>>> (888, 666)
# kwargs :>>>> {'class_id': 2, 'addr': '上海'}

解包: 字典支持 * 解包 ,解包后得到的结果是 字典的每一个键

字典不支持 ** 解包

user_data = {'username': "silence", 'password': "741"}
print(*user_data) # username password
print(**user_data) 
# 报错'username' is an invalid keyword argument for print()

【6】命名关键字参数

def student(name, age, gender,*, class_id, addr):
    # * 前可以根据位置参数  后面必须根据关键字符给
    print(name, age, gender, class_id, addr)
# 调用上面的函数传递参数可以按照关键字传递参数
# 也可以按照位置传递参数
# *后面必须根据关键字符给
student('silence', 16, 'male',class_id= 2, addr='上海')
# silence 16 male 2 上海

# 在位置参数中间指定 * 隔开,
# * 之前的位置参数支持位置和关键字传参数
# * 后面的参数必须按照关键字传参数
# 在命名关键字参数中指定默认参数的时候,默认参数允许放在位置参数之前
# 但是普通的位置参数是不允许关键字参数放在位置参数之前的

def student(name, age, gender='male',*, class_id=2, addr):
    print(name, age, gender, class_id, addr)
student('silence', 16, 'male',class_id= 2, addr='上海')
#但是用来命名关键字参数 是允许上面存在的

【7】混合使用

  • 所有参数可任意组合使用,但定义顺序必须是:
    • 位置参数、默认参数、*args ,**kwargs
  • 所有位置参数、默认参数、*args ,**kwargs 混合使用的时候不允许使用关键字参数
  • 所有位置参数、默认参数、*args ,**kwargs
    • **kwargs 必须在 *args 之后

类型提示

【1】什么是注解

定义函数的位置参数的时候可以指定参数的类型

【2】基本数据类型的注解

(1)可变数据类型

  • 字典, 列表, 元组, 集合
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})

(2)不可变数据类型

  • 字符串, 整数,浮点数,布尔

弱类型约束:建议你按照我指定的传,但是你不按照我的传也可以

def student(name: str, age: int, score: float, is_male: bool):
    print(name, type(name)) # silence <class 'str'>
    print(age, type(age))    # 16 <class 'int'>
    print(score, type(score))   # 124.8 <class 'float'>
    print(is_male, type(is_male))   # True <class 'bool'>
student('silence', 16, 124.8, True)   

【3】约定函数的返回值类型


def add(x:int,y:int)->int:
    return x + y

print(add(1,2)) # 3


def add(x:str,y:str)->str:
    return x + y

print(add('1','2'))  # 12

【4】函数返回值既可以是字符串也可以是数字

  • union 既可以返回一个又可以返回另一个类型
def add(x: int, y: int) -> Union[str, int]:
    return x + y
    return str(x + y)

print(add(1, 2),type(add(1, 2))) # 3 <class 'int'>
print(add(1, 2),type(add(1, 2)))  # 3 <class 'str'>

【5】可以返回可选的参数类型

from typing import Optional
def add(x: int, y: int) ->Optional[None]:
    return 1
print(add(1,2))  # 1

名称空间

【1】什么是名称空间

  • 名称空间就是存放函数名与函数值对应关系的地方
  • 变量名 = 变量值
  • 内存空间:开辟一块内存空间,把函数值扔到内存空间
  • 名称空间:变量名和变量值进行绑定

【2】名称空间的分类

(1)内建名称空间

python解释器自带的名称空间,打开时第一个加载的名称空间,随着代码启动生成,结束而关闭

  • 例如: if else def

(2)全局名称空间

随着内建名称空间加载完成,走到了你自己写的代码里面

  • 例如:自己创建的变量名 / 函数名 / 类名
# 这个列表是全局的列表
# 列表是可变数据类型 ---> 列表被修改后id不变
num_list = []
print(id(num_list)) # 2865103328448
def add_list(num):
    num_list.append(num)
    print(id(num_list)) # 2865103328448
add_list(1)
add_list(2)
add_list(3)
print(num_list) # [1, 2, 3]
print(id(num_list)) # 2865103328448

(3)局部名称空间

在函数内部或者是类内部定义的变量,随着函数启动而创建,随着函数结束而消失

num_list = []  # 这个列表是全局的列表
def add_list(num):
    num_list.append(num)
    print(num_list)
    print(id(num_list))
    # 1695055893696  列表是可变数据类型 ---> 列表被修改后id不变
add_list(1)  # [1]
add_list(2)  # [1,2]
add_list(3)  # [1,2,3]


def add_list(num): #这个列表是局部的列表
    # 垃圾回收机制 ---> 节省内存开销
    num_list = []  # 每次进来就是一个空列表
    num_list.append(num)
    print(num_list)
    print(id(num_list)) # 1586648339648
add_list(1) # [1]
add_list(2) # [2]
add_list(3) # [3]

【3】名称空间的加载顺序

  • 先加载内建名称空间
    • 其次是全局名称空间
      • 最后就是局部名称空间

【4】变量的查找顺序

所有的变量 尤其是 可变数据类型 字典 列表,尽量写在文件开头 , 如果放在函数下面可能会导致检索不到变量

全局查

  • 先全局后内建 --> 在 py 文件中执行代码

局部查

  • 先局部再全局再内建 ---> 在函数内部执行代码

作用域

【1】什么是作用域

变量的作用域

Python是静态作用域,也就是说Python中,变量的作用域源于它在代码中的位置

在不同的位置,可能有不同的命名空间。命名空间是变量作用域的体现形式

【2】默认知识

名称空间 ---> 分为内建 局部 全局

存放变量名和变量值关系的地方 ---> 内存中 / 硬盘中

【3】四种作用域

(1)内置作用域

python解释器自带的作用域

(2)全局作用域

全局名称空间

(3)局部作用域

在函数内部或者是类似内部的名称空间

a = 1
def func():
    b = 2
   
print(a) # a
print(b) # 报错   原因:全局作用域没办法使用局部变量

(4)嵌套作用域

在函数内部的函数的名称空间

# 局部能用全局 , 全局用不了局部
a = 1
def func():
    b = 2
    def inner():
        c = 3
        print(c) # 3
        print(b) # 2
        print(a) # 1
    print(c) # 报错

print(a) # 1
print(b) # 报错
print(c) # 报错

【4】局部修改全局变量

# 错误示例
name = 'silence'
def func():
    name = 'happy'
    print(name)   # happy
func()
print(name) # silence

# 在局部修改全局的变量
name = 'silence'
def func():
    # 在局部修改全局的变量
    # 使用关键字 global 在局部将局部变量的等级提升为全局
    #一般都是用在不可变数据类型身上
    global name
    name = 'happy'
    print(name)  # happy
func()
print(name)  # happy

【5】在内嵌函数中修改外部函数的变量

 # 错误示例
    age = 18
    def inner():
        global age
        age = 19
        print(age)# 19
    inner()
    print(age)  # 18
func()

#在内嵌函数中修改外部函数的变量值不能使用 global,要用nonlocal
    age = 18
    #在内嵌函数中修改外部函数的变量值不能使用 global
    # 要使用nonlocal 来提升内嵌函数的变量的等级到外部
    def inner():
        nonlocal age
        age = 19
        print(age)

    inner()
    print(age)
func()

【6】LEGB规则

(1)作用域的加载顺序

作用域加载顺序是由外而内

  • B : built in : 内置变量
  • G : global : 全局变量
  • L : locals() 局部变量
  • E : enclose function locals : 内嵌变量

(2)作用域的查找顺序

作用域查找顺序是由内而外

  • E : enclose function locals : 内嵌变量
  • L : locals() 局部变量
  • G : global : 全局变量
  • B : built in : 内置变量

【6】locals()和globals() 区别

locals 打印的是局部名称空间,globals 打印的是全局的名称空间

a = 1
def outer():
    b = 2
    # locals 打印的是局部名称空间
    print('outer', locals())
    print('outer', locals().get('b'))
    print('outer', locals().get('a'))
    # globals 打印的是全局的名称空间
    print('outer', globals())
    print('outer', globals().get('a'))

    def inner():
        c = 3
        print('inner', locals())
        print('inner', globals())
    inner()
print(locals())
print(globals())
outer()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值