zoukankan      html  css  js  c++  java
  • 函数的进阶

    一、函数参数—动态传参

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

    形参的第三种:动态参数

    动态参数分为两种:动态接收位置参数、动态接收关键字参数。

    1,动态接收位置参数

           首先我们先回顾一下位置参数,就是按照位置进行传参,具体如下:

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

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

    使用位置参数的动态传参:就是在形参位置编写*args表示接收任意内容,具体示例如下:

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

    动态接收位置参数的时候要注意:动态参数必须在位置参数后面。具体请看下面几个示例:

        def chi(*food, a, b):
            print("我要吃", food, a, b)
    chi(
    "大米饭", "小米饭", "黄瓜", "茄子") # 这时程序会报错,因为前面传递的所有位置参数都被*food接收了,a和b永远接收不到参数 # 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("香蕉","菠萝","葫芦娃","口罩") # 香蕉 菠萝 葫芦娃("口罩",)         默认值不生效
        # 我们发现默认值参数写在动态参数前面,默认值只有一种情况可能会生效,即默认值生效时,其后的动态参数接收的参数一定是0个
    
        def chi(a, b, *food, c="娃哈哈"):
            print(a,b,food,c)
        chi("香蕉","菠萝")  # 香蕉 菠萝 ()娃哈哈              默认值生效
        chi("香蕉","菠萝","葫芦娃") # 香蕉 菠萝 ("葫芦娃",) 娃哈哈             默认值生效
        chi("香蕉","菠萝","葫芦娃","口罩") # 香蕉 菠萝 ("葫芦娃","口罩")娃哈哈             默认值生效
        # 将默认值参数放在动态参数之后,我们发现所有的默认值都生效了,这时候如果不给出关键字传参,那么你的默认值是永远都生效的。

    综上可得参数顺序:位置参数,*args(动态接收位置参数),默认值参数

    2,动态接收关键字参数

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

    使用关键字参数的动态传参:就是用**kwargs来接收动态关键字参数,具体示例如下:

        def func(**kwargs):
            print(kwargs)
    func(a
    =1, b=2, c=3) # 结果为:{'a': 1, 'b': 2, 'c': 3} func(a=1, b=2) # 结果为:{'a': 1, 'b': 2} # 这时候接收的是一个字典dict

    最终可得形参顺序:位置参数  >  *args  >  默认值参数  >  **kwargs,这四种参数可以任意进行使用。

        # 如果想要接收所有的参数可以这样写
        # 当你不知道需要传递多少参数的时候可这样写,
        # 因为这样可以传递任意参数,即无敌传参
        def func(*args, **kwargs):
            print(args,kwargs)      # ('麻花藤', '马云') {'wtf': '胡辣汤'}
    func(
    "麻花藤", "马云", wtf="胡辣汤")
    3,动态参数的聚合与实参的打散

      综上可知,在形参位置上*表示把接收到的位置参数聚合(组合)成一个元组,**表示把接收到的关键字参数聚合(组合)成一个字典,那么在实参位置上给一个字符串,列表,元组或者集合(可迭代对象即可),并在其前面加个*表示把这个序列打散,如果是一个字典,则在其前面加上**。具体示例如下:

        def fun(*args):
            print(args)
    lst
    = [1,4,7] fun(*lst) # 可以使用*把一个列表按顺序打散,效果同fun(lst[0], lst[1], lst[2])
    s = "臣妾做不到" fun(*s) # 字符串也可以打散(可迭代对象即可)
    def fun(**kwargs): print(kwargs)
    dic
    = {"a":1, "b":2} fun(**dic)
    4,函数的注释(文档注释)
        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解释器为我们提供的名字,如int,str,list这些都属于内置命名空间;

    加载顺序:

      1,内置命名空间

      2,全局命名空间

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

    取值顺序:

      1,局部命名空间

      2,全局命名空间

      3,内置命名空间

        a = 10
        def dunc():
            a = 20
            print(a)
    dunc()
    # 结果为:20

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

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

    局部作用域:在函数内部可以使用;

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

        a = 10
        def func():
            a = 40
            b = 20
            def abc():
                print("哈哈")
            print(a,b)    # 这里使用的是局部作用域
            print(globals())  # 打印全局作用域中的内容,结果为 {'__name__': '__main__', ..., '__file__': 'D:/myproject/my.py', 'a': 10, 'func': <function func at 0x0000018C4D781E18>}
            print(locals())   # 打印局部作用域中的内容,结果为 {'abc': <function func.<locals>.abc at 0x0000018C4DBB98C8>, 'b': 20, 'a': 40}
    
        func()

    三、函数的嵌套

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

    2,函数的执行顺序,具体示例如下:

        # 函数的嵌套
        def fun2():
            print(222)
            def fun3():
                print(666)
            print(444)
            fun3()
            print(888)
    print(33)
    fun2()
    print(555) # 执行该脚本,结果为: # 33 # 222 # 444 # 666 # 888 # 555

    四、关键字global和nonlocal

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

        a = 10
        def func():
            global a   # 这里用的a是全局的
            a = 20
            print(a)
    func() # func函数执行结果为:20
    print(a) # 此时a为:20
        def func():
            global a  # 加global 表示不在局部创建这个变量,而是直接使用全局的a,若不存在全局变量a,则创建一个全局变量a
            a = 28
            print(a)
    
        func() # 函数func的执行结果是:28
        print(a)  # a是全局变量,值为:28

    global:

      1,声明一个全局变量;

           2,在局部作用域想要对全局作用域的全局变量进行修改时,需要用到 global(限于字符串,数字);

        def func():
            global a
            a = 3
        func()
        print(a)   # 结果为:3
    
        count = 1
        def search():
            global count
            count = 2
        search()
        print(count)   # 结果为:2
    
        lst = ["麻花藤", "刘嘉玲", "詹姆斯"]
        def func():
            lst.append("马云云")   
            # 对于可变数据类型可以直接进行访问,但是不能改地址,说白了,不能赋值
            # 可变的数据类型的变量名中存放的是内存地址,对它们进行访问并没有改变其内存地址
            print(lst)
        func()   # 结果为:["麻花藤","刘嘉玲","詹姆斯","马云云"]
        print(lst)    # 结果为:["麻花藤","刘嘉玲","詹姆斯","马云云"]

    注意:对可变数据类型(list,dict,set)可以直接引用,不用通过global也能改变。具体如下:

        li = [1,2,3]
        dic = {'a':'b'}
        def change():
            li.append('a')
            dic['q'] = 'g'
            print(dic)
            print(li)
    change()
    print(li) print(dic) # 结果为: # {'a': 'b', 'q': 'g'} # [1, 2, 3, 'a'] # [1, 2, 3, 'a'] # {'a': 'b', 'q': 'g'}

    nonlocal :

      1,不能修改全局变量;

      2,在局部作用域中,对父级作用域(或者更外层作用域且非全局作用域)的变量进行引用和修改,并且引用的哪层,从那层及以下此变量全部发生改变。具体示例如下:

        a = 10
        def func1():
            a = 20
            def func2():
                nonlocal a
                a = 30
                print(a)
            func2()
            print(a)
    func1()
    # 结果: # 加了nonlocal # 30 # 30 # 不加nonlocal # 30 # 20

    练习,如果嵌套了很多层,会是一种什么效果,分析明白,那么作用域,global ,nonlocal 就没问题了:

        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
        
  • 相关阅读:
    [Unity3D]UI界面之瞄准镜设置说明
    [Unity3d]游戏中子弹碰撞的处理
    [Unity3D]引擎学习之注意事项(持续更新中)
    [Unity3D]添加音效说明
    [Unity3d]3D项目转换为VR项目(暴风魔镜SDK)
    [Unity3d]调试问题之UI/Image不显示
    [Unity3d]向量的过度方法以及拖尾效果
    [Unity3D]脚本生命周期学习
    windows server 2008 应用程序池自动关闭 C:Windowssystem32RpcProxyRpcProxy.dll failed to load
    create a (VSTO) Office 2007 add-in using VS 2012?
  • 原文地址:https://www.cnblogs.com/li-li/p/9451149.html
Copyright © 2011-2022 走看看