10. 前方高能-函数的进阶

本节主要内容:

1.函数参数—动态传参

2.名称空间,局部名称空间,全局名称空间,作用域,加载顺序

3.函数的嵌套

4.gloabal,nonlocal关键字

一.函数参数—动态传参

之前我们说过了传参,如果我们需要一个函数传参,而参数又不确实的,或者我给一个函数传很多参数,我的形参就要写很多,很麻烦,那怎么办呢,我们可以考虑使用动态参数。

形参的第三种:动态参数

动态参数分为两种:

1.动态接受为止参数

首先我们先回顾一下为止参数,位置参数,按照位置进行传参

def chi(quality_food,junk_food):
    print("我要吃",quality_food,junk_food)
chi("大米饭","小米饭")  #“大米饭”传递给quality_food "小米饭"传递给junk_food按照位置传

现在问题来了,我想吃任意食物,数量是任意的,食物也是任意的,这时我们就要用到动态参数了。

在参数位置编写*表示接收任意内容

def chi(*food):
    print("我要吃",food)
chi("大米饭","小米饭")
结果:
我要吃("大米饭""小米饭"#多个参数传递进去,收到的内容是元祖tuple

动态接受参数的时候要注意:动态参数必须在位置参数后面

def chi(*food, a, b):
 print("我要吃", food, a, b)
chi("大米饭", "小米饭", "黄瓜", "茄子")

这时候程序会报错,因为前面传递进去的所有位置参数都被*food接受了,a和b永远接受不到参数

Traceback (most recent call last):
 File "/Users/sylar/PycharmProjects/oldboy/fun.py", line 95, in <module>
 chi("大米饭", "小米饭", "黄瓜", "茄子")
TypeError: chi() missing 2 required keyword-only arguments: 'a' and 'b'

所以必须修改成下面代码:

def chi(*food,a,b):
    print("我要吃",food,a,b)
chi("大米饭","小米饭",a="黄瓜",b="茄子")#必须用关键字参数来指定

这时候a和b就有值了,但是这样写呢位置参数就不能用了,所以,我们要先写位置参数,然后再用动态参数

def chi(a,b,*food):
    print("我要吃",a,b,food)
chi("大米饭","小米饭","馒头","面条") #前面两个参数用位置参数来接受,后面的参数用动态参数接受

那默认值参数呢?

def chi(a,b,c="馒头",*food):
    print(a,b,c,food)
chi("香蕉","菠萝") #香蕉 菠萝 馒头 ()默认值生效
chi("香蕉","菠萝","葫芦娃") #香蕉 菠萝 葫芦娃() 默认值不生效
chi("香蕉","菠萝","葫芦娃","口罩") #香蕉 菠萝 葫芦娃("口罩",)默认值不生效

我们发现默认值参数写在动态参数前面,默认值只有一种情况可能会生效。

def chi(a,b,*food,c="娃哈哈"):
    print(a,b,food,c)
chi("香蕉","菠萝")  #香蕉 菠萝 ()娃哈哈 默认值生效
chi("香蕉","菠萝","葫芦娃") #香蕉 菠萝 ("葫芦娃",) 娃哈哈 默认值生效
chi("香蕉","菠萝","葫芦娃","口罩") #香蕉 菠萝 ("葫芦娃","口罩")娃哈哈  默认值生效

这时候我们发现所有的默认值都生效了,这时候如果不给出关键字传参,那么你的默认值是永远都生效的。

顺序:位置参数,*动态参数,默认值参数 ,**kwargs

顺序: 位置参数  *args  默认值参数 **kwargs

2.动态接收关键字参数

在python中可以动态的位置参数,但是*args这种情况只能接收位置参数无法接受关键字参数。

在python中使用**kwargs来接收动态关键字参数

def func(**kwargs):
    print(kwargs)
func(a=1,b=2,c=3)
func(a=1,b=2)
结果:
{'a': 1, 'b': 2, 'c': 3}
{'a': 1, 'b': 2}

这个时候接收的是一个dict字典

顺序的问题,在函数调用的时候,如果先给出关键字参数,则整个参数列表会报错。

def func(a, b, c, d):
 print(a, b, c, d)
# 关键字参数必须在位置参数后面, 否则参数会混乱
func(1, 2, c=3, 4)

最终顺序(*)

位置参数>*args>默认值参数>**kwargs

这四种参数可以任意的进行使用。

如果想要接收所有的参数:

def func(*args,**kwargs):
    print(args,kwargs)
func("麻花藤","马云",wtf="胡辣汤")

动态参数的另一种传参方式:

def fun(*args):
    print(args)
lst = [1,4,7]
fun(lst[0],lst[1],lst[2])
fun(*lst)  #可以使用*把一个列表顺序打散
s="成妾做不到"
fun(*s)   #字符串也可以打散,(可迭代对象)

在实参位置上给一个系列,列表,可迭代对象前面加个*表示把这个序列按顺序打散。

在形参的位置上的*表示把接收到的参数组合成一个元祖

如果是一个字典,那么也可以打散,不过需要两个*

def fun(**kwargs):
    print(kwargs)
dic= {"a":1,"b":2}
fun(**dic)

函数的注释:

def chi(food,drink):
    """
    这里是函数的注释,先写一下当前这个函数是干什么的,比如我这个函数就是一个吃
    :param food:   参数food是什么意思
    :param drink:   参数drink是什么意思
    :return:   返回的是什么东东
    """
    print(food,drink)
    return "very good"

二.命名空间

在python解释器开始执行之后,就会在内存中开辟一个空间,每当遇到一个变量的时候,就把变量名和值之间的关系记录下来,但是当遇到函数定义的时候,解释器只是把函数名读入内存,表示这个函数存在了,至于函数内部的变量和逻辑,解释器是不关系的,也就是说一开始的时候函数只是加载进来,仅此而已,只有当函数被调用和访问的时候,解释器才会根据函数内部声明的变量 来进行开辟变量的内部空间,随着函数执行完毕,这些函数内部变量占用的空间也会随着函数执行完毕而被清空。

def fun():
    a =10
    print(a)
fun()
print(a)   #a不存在了已经

我们给存放名字和值的关系的空间起了一个名字:命名空间,我们的变量在存储的时候就是存储在这片空间中的。

命名空间的分类:

1.全局命名空间-->我们之间在py文件中的,函数外声明的变量都属于全局命名空间

2.局部命名空间-->在函数中声明的变量会放在局部命名空间

3.内置命名空间 -->存放python解释器为我们提供名字list,tuple,str,int这些都是内置命名空间

加载顺序:

1.内置命名空间

2.全局命名空间

3.局部命名空间(函数执行的时候)

取值顺序

1.局部命名空间

2.全局命名空间

3.内置命名空间

a = 10
def dunc():
    a = 20
    print(a)
dunc()  #20

作用域:作用域就是作用范围,按照生效范围来看分为全局作用域和局部作用域

全局作用域:包含内置命名空间和全局命名空间,在整个文件的任何位置都可以使用(遵循从上到下逐行执行).局部作用域:在函数内部可以使用。

作用域名空间:

1.全局作用域:全局命名空间+内置命名空间

2.局部作用域:局部命名空间

我们可以通过globals()函数来查看全局作用域的内容,也可以通过locals()来查看局部作用域中的变量和函数信息

a = 10
def func():
    a = 40
    b = 20
    def abc():
        print("哈哈")
    print(a,b)    # 这里是有的局部作用域
    print(globals())  #打印全局作用域中的内容
    print(locals())   #打印局部作用域中的内容
func()

三.函数的嵌套

1.只哎哟遇见了()就是函数的调用,如果没有()就不是函数的调用

2.函数的执行顺序

def fun1():
    print(111)
def fun2():
    print(222)
    fun1()
fun2()
print(111)
#函数的嵌套
def fun2():
    print(222)
    def fun3():
        print(666)
    print(444)
    fun3()
    print(888)
print(33)
fun2()
print(555)    

四.关键字global 和nonlocal

首先我们写这样一个代码,首先在全局声明一个变量,然后在局部调用这个变量,并改变这个变量的值

# a = 10
# def func():
#     global a # 这里用的a是全局的
#     a = 20
#     print(a)
# func()
# print(a)    # 20  20



# a = 100
# def func():
#     global a  #加了个global 表示不再局部创建这个变量,而是直接使用全局的a
#     a = 28
#     print(a)
# func()
# print(a)  #  28  28 

global 表示,不再使用局部作用域中的内容,而改变全局作用域中的变量

lst = ["麻花藤","刘嘉玲","詹姆斯"]
def func():
    lst.append("马云云")   # 对于可变数据类型可以直接进行访问,但是不能改地址,说白了,不能赋值
    print(lst)
func()
print(lst)

nonlocal 表示在局部作用中,调用父级命名空间中的变量

a = 10
def func1():
    a = 20
    def func2():
        nonlocal a
        a = 30
        print(a)
    func2()
    print(a)
func1()

结果:
加了nonlocal
30
30
不加nonlocal
30
20

再看,如果嵌套了很多层,会是一种什么效果:

a = 1
def fun_1():
    a = 2
    def fun_2():
        nonlocal a
        a = 3
        def fun_3():
            a = 4
            print(a)
        print(a)
        fun_3()
        print(a)
    print(a)
    fun_2()
    print(a)
print(a)
fun_1()
print(a)
结果:
1
2
3
4
3
3
1

这样的程序如果能分析明白,那么作用域,global ,nonlocal 就没问题了

 

posted @ 2018-06-12 16:36  扎西德勒119  阅读(347)  评论(0编辑  收藏  举报