9. 函数参数与名称空间

函数介绍

什么是函数

​ 函数就相当于具备某一功能的工具
​ 函数的使用必须遵循一个原则:
​ 先定义,后调用

​ 函数分为内置函数和自定义函数

使用函数所解决的问题

​ 1、组织结构不清晰,可读性差
​ 2、代码冗余
​ 3、可维护性、扩展性差

函数的定义与调用

'''
def 函数名(参数1,参数2.....)
	'''文档描述'''
    函数体
    return 函数返回值的值
'''
# 定义函数的方式一:无参函数
def func():
    print('nihao')
# 方式二:有参函数
def func(a):
    print(a)
# 方式三:空函数,用于构思代码时使用
def func(a):
    pass

定义函数发生的事情:

  1. 申请内存空间保存函数的代码
  2. 将内存空间的地址绑定给函数名
  3. 定义函数并不会执行函数的代码,但是会检查函数体的语法,如果有问题不需要调用就会报错
# 调用函数的三种形式
# 方式一:语句的形式,只加括号执行
func()
# 方式二:表达式形式
res=function()
# 方式三:函数调用可以做参数
# print(add_two(1,2))

调用函数发生的事情:

  1. 通过函数名找到函数的内存地址
  2. 加上括号就是进行函数体代码的执行

函数的返回值

就是函数的结果,关键字是return

def add_two(a,b):
    return a+b

return是函数终止的标志,并且会将return后面的值返回函数的结果

返回值的三种形式:

  1. 函数内没有return或者是指写一个return,函数返回none
  2. 返回一个值:return 结果
  3. 返回多个值:return a , b , c 函数会以元组的形式返回一个值

函数的参数

简介

形式参数(形参):在函数定义阶段定义的参数,形参相当于变量名

def func(x,y) # x,y就是形参

实际参数(实参):在调用函数阶段调用的值,实参相当于变量值

func(1,2) # 1,2就是实参(这里的1,2是值,就是说代表值的变量,返回值的表达式都可以作为参数)

python中传递的实质上是值的内存地址,而不是值本身

形参与实参的关系:在调用阶段,实参会绑定给形参,这种绑定关系只是在函数体内有效,在函数调用的时候,绑定关系生效,在调用结束后,绑定关系解除(都是在内存中产生的,调用结束后直接释放内存)

形参传参:

  1. 位置参数:按照从左到右位置一次定义的参数,位置形参必须被传值,必须一一对应

  2. 默认参数:以name=value的形式传入的参数,默认参数在函数调用时可以不进行传值,即使用默认值

  3. 动态参数(*和**的用法):传入的参数的个数不固定时,使用这种用法

    # 1. 专门接收溢出的位置实参
    # 溢出的位置实参将会被*保存成元组然后赋值给紧跟其后的变量名,一般使用*args表示(规范用法)
    # 2. 专门接收溢出的关键字实参
    # 溢出的关键字实参将会被**保存成字典然后赋值给紧跟其后的变量名,一般使用**kwargs表示(规范用法)
    
  4. 命名关键字参数:定义函数时,在*后面的参数

    def func(x,y,*,a,b):  # a和b就是命名关键字参数,在传参的时候,需要按照关键字参数的形式进行传值,因为在形参上是位置参数的形式,但是传参的时候必须以关键字参数传值的形式,所以叫命名关键字参数,有的也叫仅限关键字参数
        pass
    

    形参设置的时候参数顺序的关系:

    位置参数、默认参数、*args、命名关键字参数,**kwargs

形参混用的问题:

  1. 位置参数必须在默认参数的左边

  2. 默认参数的值是在函数定义是就被赋值的

  3. 虽然默认值参数可以指定任意类型,但是不建议使用可变的数据类型

    def func(x,y,l=None):
        if l=None:
            l=[]
        l.append(x)
        l.aapend(y)
    
  4. 混用时:*args必须在**kwargs之前

实参传参:

  1. 位置参数:与位置一一对应的进行传值,位置形参必须被传值
  2. 关键字参数:函数调用的时候,以name=value的形式传入的参数,关键字参数可以不参照顺序
  3. *的实参用法:实参中带星就是将后面的数据打散成位置参数,如func(*[1,2,3])就是func(1,2,3),一个效果
  4. **的实参用法:当**的后面跟的是一个字典的时候,会将字典打散成若干关键字参数func(**{1:'a',2:'b'})等同于func(1='a',2='b')

混合使用的时候,需要注意以下几点:

  1. 可以混合使用,位置实参必须放在关键字实参的前面
  2. 不能给同一个形参多次进行传值

实参设置的时候参数顺序的关系:

遇到*进行传参,先将后面的可迭代对象打散成位置参数,这样的话*用法传递的参数必须在关键字参数的后面了,这里位置参数必须在关键字参数的前面,剩下的都可以灵活调换

名称空间和作用域

名称空间(namespaces)

定义:存放名字(变量名、函数名等)的地方,是对栈区的划分

​ 内存分为栈区和堆区,栈区存放的是函数的名称与数据地址的绑定关系,堆区存放的是数据本身(看可以简单的理解为栈区放的是变量名,堆区放的是变量值),其中栈区又被重新分区,分为内置名称空间、全局名称空间和局部名称空间,这样的管理即使出现相同名称的变量名,也能根据分区找到变量名(变量名查找的时候有一个优先级),不会产生冲突,其中全局名称空间和内置名称空间称为全局范围,局部名称空间称为局部范围,全局名称空间和内置名称空间都只有一个,局部名称空间可以有多个(先了解,后面会解释)

  • 内置名称空间:存放的是解释器内置的名称(built-in function),在解释器开启的时候打开该空间,解释器关闭的时候关闭该空间。
  • 全局名称空间:运行顶级代码产生的名字,或者说只要不是函数内定义的,或者是内置的名字,就都是全局名称空间的;在python程序的第三个阶段(运行文件代码)时产生,在文件运行结束之后关闭该空间。
  • 局部名称空间:调用函数过程中,在函数代码运行的过程中产生的函数内的名字;该空间在函数调用时存活,在函数调用完毕时销毁(即使是一个函数调用了很多遍,也会产生多个局部名称空间)。

名称空间的加载顺序:先是加载内置名称空间,接着是全局名称空间,最后是局部名称空间

销毁顺序:先是局部的,然后是全局的,最后是内置的

名字的查找顺序:对于局部的名称,先在局部名称空间查找,接着是全局名称空间,最后是内置名称空间(可以理解为上下级的关系,后面会讲到函数的嵌套,本级的名称空间没有,调用的是上一级的名称空间),全局的名称直接在全局名称中查找,内置的名称直接在内置名称空间查找

函数的嵌套

def func1():
	a='nihao'
    def func2():
        print(a)
# 这就是函数的嵌套,需要注意的是,局部名称空间也是一种嵌套的关系,当func2的局部空间中找不到a时,会到func1的空间中去找,使用func1的a,所以上面的结果应该是'nihao'

# 也可以使用函数功能直接进行嵌套
def max3(a,b,c):
	res1=max(a,b)
    res2=max(res1,c)
    print(res2)

全局作用域和局部作用域

作用域就是作用范围

  • 全局作用域:内置名称空间和全局名称空间对应的就是全局作用域
  • 局部作用域:局部名称空间对应局部作用域

LEGB原则:l是local,指的是函数嵌套后的局部作用域的最内层,局部作用域除了最内层之外的都叫enclosing,全局作用域叫global,内置名称空间叫builtin

global和nonlocal

global可以在局部作用域修改全局变量的值(仅仅是对于局部作用域想要修改全局变量中不可变数据类型的值,对于可变的数据类型,局部作用域可以直接修改,底层逻辑允许)

nonlocal会将嵌套的函数内层的变量声明为外层的变量,使内层的局部作用域可以操作外层的变量

x=100
def func():
    global x
    x=200
    print(x)
 # 局部作用域只能引用全局变量,但不能改变全局变量(为了安全,修改了的话就是在局部名称空间中创建了一个同名的变量),global可以声明该变量变成了全局变量,从而进行修改


def func1():
    x=100
    def func2():
    
    	def func3():
        nonlocal x=200
    	print(x)
# nonlocal是逐层向外找的(找不到就继续向外层找),global是直接在全局作用域找

globals()和locals()

  • globals()返回一个字典,字典的内容是全局作用域中的所有对象
  • locals()返回一个字典,显示的是当前作用域的所有对象

函数对象

就是可以将函数当成变量使用,函数名绑定的是内存地址

  • 可以赋值给其他的变量

  • 可以当做函数的参数传入

  • 可以当做函数的返回值

  • 可以当做容器类型的一个元素

# 函数对象的使用案例
def login():
    print('登录功能')
def transfer():
    print('转账功能')
def check_banlance():
    print('查询余额')
def withdraw():
    print('提现')
def register():
    print('注册')
func_dic = {
    '0': ['退出', None],
    '1': ['登录', login],
    '2': ['转账', transfer],
    '3': ['查询余额', check_banlance],
    '4': ['提现', withdraw],
    '5': ['注册', register]
}

while True:
    for k in func_dic:
        print(k, func_dic[k][0])

    choice = input('请输入命令编号:').strip()
    if not choice.isdigit():
        print('必须输入编号,傻叉')
        continue

    if choice == '0':
        break
        
    if choice in func_dic:
        func_dic[choice][1]()
    else:
        print('输入的指令不存在')

三元表达式

三元表达式是一种简化代码的方法

res = 条件成立时返回的值 if 条件 else 条件不成立时返回的值
def max2(x,y):
    if x > y:
        return x
    else:
        return y
# 下面进行简化:三元表达式
x=1
y=2
res = x if x > y else y  # 只要是二选一的都可以使用
posted @ 2021-07-20 00:21  奇点^  阅读(71)  评论(0)    收藏  举报