• python 全栈开发,Day10(动态参数,命名空间,作用域,函数嵌套)


    一、动态参数

    def func(a,b,c,d,e,f,g):
        pass
    func(1,2,3,4,5,6,7)

    如果加30个参数呢?
    有没有万能的参数,可以代表一切参数呢?

    *args 动态参数,万能参数
    agrs接收的就是实参对应的所有位置参数,并将其放在元组中
    它不会接收关键字参数

    def func(*args):
        pass
    func(1,2,3,4,5,6,7)
    

    打印返回值

    def func(*args):
        print(args)
    func(1,2,3,4,5,6,7)
    

    执行输出:
    (1, 2, 3, 4, 5, 6, 7)

    结果是一个元组

    二、形参对应顺序

    def func(*args,a,b,c,d,e='sex'):
        print(args,a,b,c,d,e)
    func(1,2,3,4,5)
    

    执行报错,因为*args接收了所有实参,所以缺少了b,c,d参数

    TypeError: func() missing 4 required keyword-only arguments: 'a', 'b', 'c', and 'd'

    修改正确的位置

    def func(a,b,c,d,*args,e='sex'):
        print(a,b,c,d,args,e)
    func(1,2,3,4,5,6,7,8,e='女')

    执行输出:

    1 2 3 4 (5, 6, 7, 8) 女

    形参对应顺序:

    位置参数,*args,默认参数

    *args参数,可以不传,默认为空()

    def func(a,b,c,d,*args,e='sex'):
        print(a,b,c,d,args,e)
    func(1,2,3,4,e='女')
    

    执行输出:

    1 2 3 4 () 女

    *args 名字可以改的,但是约定成熟使用*args

    **kwargs也是动态参数,和*args 不同的是,它只接收关键字参数

    **kwargs 动态传参,他将所有的关键字参数(未定义的)放到一个字典中。

    def func(a,b,c,**kwargs):
        print(kwargs)
    func(1,2,r=4,b1=5,c1=6,c=7)

    执行输出:

    {'r': 4, 'c1': 6, 'b1': 5}

    执行没有报错,是因为函数接收参数后,它会从左边到右找,最后找到了c

    c=7参数,在a,b,c里面已经定义好了,所以在输出的字典中,并未出现。

    因为kwargs返回的是未定义的关键字参数。

    **kwargs正确的位置

    def func(a,b,c,d,*args,e='男',**kwargs):
        print(a,b,c,d,args,e,kwargs)
    func(1,2,3,4,5,6,7,v=3,m=7,h=9,e='女')
    

    执行输出:

    1 2 3 4 (5, 6, 7) 女 {'v': 3, 'h': 9, 'm': 7}

    最终所有参数顺序:位置参数,*args,默认参数,**kwargs

    如果函数含有多个未知参数,一般使用如下格式:

    def func1(*args,**kwargs):
        pass
    func1()
    

    它能接收所有的参数

    比如len()源码

    def len(*args, **kwargs): # real signature unknown
        """ Return the number of items in a container. """
        pass
    

    源码千万别不要修改

    三、 * 魔法运用

    有如下函数

    def func(*args):
        print(args)
    l1 = [1,2,30]
    l2 = [1,2,33,21,45,66]
    

    需要将2个列表的所有元素赋值给args,如何去做?

    这个时候,需要用到*

    def func(*args):
        print(args)
    l1 = [1,2,30]
    l2 = [1,2,33,21,45,66]
    func(*l1)
    func(*l1,*l2)

    执行输出:

    (1, 2, 30)
    (1, 2, 30, 1, 2, 33, 21, 45, 66)

    传两个字典给**kwargs

    def func(**kwargs):
        print(kwargs)
    dic1 = {'name':'jack','age':22}
    dic2 = {'name1':'rose','age1':21}
    func(**dic1,**dic2)
    

    执行输出:

    {'name': 'jack', 'age': 22, 'name1': 'rose', 'age1': 21}

    **kwargs只限于字典类型

     在函数的调用执行时,
       *可迭代对象,代表打散(list,tuple,str,dict(键))将元素一一添加到args。
       **字典,代表打散,将所有键值对放到一个kwargs字典里。

    def func(*args,**kwargs):
        print(args,kwargs)
    dic1 = {'name':'jack','age':22}
    dic2 = {'name1':'rose','age1':21}
    func(*[1,2,3,4],*'asdk',**dic1,**dic2)
    

    执行输出:

    (1, 2, 3, 4, 'a', 's', 'd', 'k') {'age1': 21, 'name': 'jack', 'age': 22, 'name1': 'rose'}

     四、命名空间和作用域

    a = 4
    b = 3
    c = [1,2,3,4]
    c1 = {'name':'alex'}
    
    def func1():
        name = '老男孩'
        print(name)
    func1()

    当执行函数的时候,他会在内存中开辟一个临时名称空间,存放函数体内的所有变量与值的关系,
    随着函数的执行完毕,临时空间自动关闭。
    input(),print(),len 内置函数

    函数里面的变量,在函数外面能直接引用么?

    def func1():
        m = 1
        print(m)
    
    print(m)  # 这行报的错

    执行报错:
    NameError: name 'm' is not defined


    上面为什么会报错呢?现在我们来分析一下python内部的原理是怎么样:
      我们首先回忆一下Python代码运行的时候遇到函数是怎么做的,从Python解释器开始执行之后,就在内存中开辟里一个空间,每当遇到一个变量的时候,就把变量名和值之间对应的关系记录下来,但是当遇到函数定义的时候,解释器只是象征性的将函数名读如内存,表示知道这个函数存在了,至于函数内部的变量和逻辑,解释器根本不关心。
      等执行到函数调用的时候,Python解释器会再开辟一块内存来储存这个函数里面的内容,这个时候,才关注函数里面有哪些变量,而函数中的变量回储存在新开辟出来的内存中,函数中的变量只能在函数内部使用,并且会随着函数执行完毕,这块内存中的所有内容也会被清空。
    我们给这个‘存放名字与值的关系’的空间起了一个名字-------命名空间。
    代码在运行伊始,创建的存储“变量名与值的关系”的空间叫做全局命名空间
    在函数的运行中开辟的临时的空间叫做局部命名空间

    python之禅

    import this
    

      执行输出:

    The Zen of Python, by Tim Peters
    
    Beautiful is better than ugly.
    Explicit is better than implicit.
    Simple is better than complex.
    Complex is better than complicated.
    Flat is better than nested.
    Sparse is better than dense.
    Readability counts.
    Special cases aren't special enough to break the rules.
    Although practicality beats purity.
    Errors should never pass silently.
    Unless explicitly silenced.
    In the face of ambiguity, refuse the temptation to guess.
    There should be one-- and preferably only one --obvious way to do it.
    Although that way may not be obvious at first unless you're Dutch.
    Now is better than never.
    Although never is often better than *right* now.
    If the implementation is hard to explain, it's a bad idea.
    If the implementation is easy to explain, it may be a good idea.
    Namespaces are one honking great idea -- let's do more of those!
    View Code

    在python之禅中提到过:命名空间是一种绝妙的理念,让我们尽情的使用发挥吧!

    命名空间一共分为三种:

      全局命名空间

      局部命名空间

      内置命名空间

    *内置命名空间中存放了python解释器为我们提供的名字:input,print,str,list,tuple...它们都是我们熟悉的,拿过来就可以用的方法。

    三种命名空间之间的加载与取值顺序:

    加载顺序:内置命名空间(程序运行前加载)->全局命名空间(程序运行中:从上到下加载)->局部命名空间(程序运行中:调用时才加载)

    取值顺序

      在局部调用:局部命名空间->全局命名空间->内置命名空间

      在全局调用:全局命名空间->内置命名空间

    综上所述,在找寻变量时,从小范围,一层一层到大范围去找寻。

    作用域

    作用域就是作用范围,按照生效范围可以分为全局作用域和局部作用域。

    全局作用域:包含内置名称空间、全局名称空间,在整个文件的任意位置都能被引用、全局有效

    局部作用域:局部名称空间,只能在局部范围内生效

    局部变量举例

    name = '老男孩'
    def func1():
        name = 'taibai'
        print(name)
    func1()
    

    执行输出:

    taibai

    执行func1()方法时,进入函数体内部,加载name变量,执行print(name),从函数体找到了name变量,直接输出taibai

    name = '老男孩'
    def func1():
        print(name)
    func1()
    

    执行输出:

    老男孩

    函数执行到print(name)时,从函数体中寻找name变量,发现没有。从外部中寻找,找到了,就输出,否则报错。

    取值是从内到外

    a =2
    print(a)
    def func1():
        age = 11
        print(age)
        
    func1()
    

    执行输出:

    2
    11

    函数什么时候执行? 函数调用的时候执行。
    代码从上至下依次执行, 调用函数:函数里面从上至下依次执行。

    print(111)
    def func1():
        print(333)
    def func2():
        print(444)
    def func3():
        print(555)
        func2()
    
    func1()
    print(222)
    

    执行输出:

    111
    333
    222

    print(111)
    def func1():
        print(333)
        func2()
        print(666)
    def func2():
        print(444)
    def func3():
        print(555)
        func2()
    
    func1()
    print(222)
    

    执行输出:

    111
    333
    444
    666
    222

    分析下面一个例子

    def f1():
        def f2():
            def f3():
                print("in f3")
    
            print("in f2")
            f3()
    
        print("in f1")
        f2()
    
    f1()
    

    执行输出:

    in f1
    in f2
    in f3

    1.执行函数f1(),加载函数变量f2,执行print("in f1"),输出in f1

    2.执行函数f2(),加载函数变量f3,执行print("in f2"),输出in f2

    3.执行函数f3(),执行print("in f3"),输出in f3

    globals和locals方法

    在全局调用globals和locals
    在局部调用locals和globals

    print(globals()) #全局名称空间:所有变量
    print(locals()) #局部名称空间,所有变量
    
    a = 2
    b = 3
    def func1():
        c = 5
        d = 6
        print(globals()) #全局变量放在一个字典中
        print(locals()) #局部变量{'c': 5, 'd': 6}
    func1()
    

      执行输出:

    {'__doc__': None, '__cached__': None, 'b': 3, '__package__': None, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'E:/python_script/day10/hanshu.py', 'a': 2, '__spec__': None, 'func1': <function func1 at 0x000001DE7426A950>, '__name__': '__main__', '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001DE74036CF8>}
    {'c': 5, 'd': 6}

    globals()和locals()一般很少用,在函数逻辑比较复杂的情况下,可能会用到。

    def func1():
        name = '老男孩'
        print(name)
    print(name)
    

    执行报错:

    NameError: name 'name' is not defined

    因为name是一个局部变量,只有当函数运行时,它才会加载到内存中

    global关键字,nonlocal关键字

    global:

      1,声明一个全局变量。

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

      

    def func1():
        global name
        name = '老男孩'
        print(name)
    func1()
    print(name)
    

    执行输出:

    老男孩
    老男孩

    name = '金角大王' #全局变量
    def func1():
        global name #声明局部变量为全局变量
        name = '老男孩' #局部变量
        #print(name)
    func1()
    print(name)
    

    执行输出:

    老男孩

    因为全局变量name被函数体的global name 覆盖了

    nonlocal:

      1,不能修改全局变量。

      2,在局部作用域中,对父级作用域的变量进行引用和修改,并且引用的哪层,从那层及以下此变量全部发生改变。

    a = 4
    def func1():
        nonlocal a
        a = 5 #修改全局变量
        #print(name)
    func1()
    print(a)
    

    执行输出:

    SyntaxError: no binding for nonlocal 'a' found

     不允许修改全局变量

    def func1():
        b = 6
        def func2():
            print(666)
        func2()
        
    func1()
    

    执行输出:

    666

    def func1():
        b = 6
        def func2():
            nonlocal b #表示可以影响父级,也就是func1()
            b = 666 #重新赋值
            print(b)
        func2()
    print(b) #这个时候,影响了b的值,输出666 func1()

    执行输出:

    666

    666

    def func1():
        b = 6
        def func2():
            b = 666
            print(b)
        func2()
        print(b) #父级不受影响
    
    func1()
    

    执行输出:

    666
    6

    def add_b(): #不受dd_nonlocal影响
        b = 42
        def do_global():
            b = 10 #影响子级函数,b都是10
            print(b)
            def dd_nonlocal():
                nonlocal b #只能影响父级,也就是do_global()
                b = b + 20 #b=10+20 也就是30
                print(b)
            dd_nonlocal()
            print(b)
        do_global()
        print(b)
    
    add_b()
    

    执行输出:

    10
    30
    30
    42

    课后作业:

    1、继续整理函数相关知识点,写博客。
    2、写函数,接收n个数字,求这些参数数字的和。(动态传参)
    3、读代码,回答:代码中,打印出来的值a,b,c分别是什么?为什么?
    	a=10
    	b=20
    	def test5(a,b):
         		print(a,b)
    	c = test5(b,a)
    	print(c)
    4、读代码,回答:代码中,打印出来的值a,b,c分别是什么?为什么?
    	a=10
    	b=20
    	def test5(a,b):
    		a=3
    		b=5
         	print(a,b)
    	c = test5(b,a)
    	print(c)
    
    
    相关面试题(先从纸上写好答案,然后在运行):
    1,有函数定义如下:
    def calc(a,b,c,d=1,e=2):
    return (a+b)*(c-d)+e
    请分别写出下列标号代码的输出结果,如果出错请写出Error。
    print(calc(1,2,3,4,5))_____ print(calc(1,2))____print(calc(e=4,c=5,a=2,b=3))___ 
    print(calc(1,2,3))_____ print(calc(1,2,3,e=4))____print(calc(1,2,3,d=5,4))_____
    2,下面代码打印的结果分别是_________,________,________.
    def extendList(val,list=[]):
        list.append(val)
        return list
    list1 = extendList(10)
    list2 = extendList(123,[])
    list3 = extendList('a')
    
    print('list1=%s'%list1)
    print('list2=%s'%list2)
    print('list3=%s'%list3)
    

    答案:

    2、写函数,接收n个数字,求这些参数数字的和。(动态传参)

    def summation(*args,**kwargs):
        the_sum = 0
        for i in args:
            the_sum += int(i)
        for j in kwargs.values(): #kwargs是字典
            the_sum += int(j)
        return the_sum
    
    print(summation(1,2,3,c=6,d=7))
    

    执行输出:

    19

    *args用来接收位置参数,**kwargs用来接收关键字参数,它的返回值是字典,所以必须要kwargs.values()

    3、读代码,回答:代码中,打印出来的值a,b,c分别是什么?为什么?

    a=10
    b=20
    def test5(a,b):
        print(a,b)
    c = test5(b,a)
    print(c)
    

    代码从上向下执行:

    1.加载变量a,b,函数名test5

    2.执行test5函数,传2个位置参数20,10

    3.函数接收2个变量,根据位置变量一一对应。a = 20,b=10

    4.执行print(a,b),输出 20 10

    5.执行print(c),输出None,因为函数没有给返回值,为什么呢?因为没有return。

    综上所述:

    a,b,c分别是20,10,None

    4、读代码,回答:代码中,打印出来的值a,b,c分别是什么?为什么?

    a=10
    b=20
    def test5(a,b):
        a=3
        b=5
        print(a,b)
    c = test5(b,a)
    print(c)
    

    代码从上向下执行:

    1.加载变量a,b,函数名test5

    2.执行函数test5,传位置参数 20,10

    3.函数test5接收2个位置参数,根据位置关系匹配,a=20,b=10

    4.局部变量a=3,b=5,重新赋值了,覆盖接收的2个参数。

    5.执行print(a,b),输出3,5

    6.执行print(c),输出None,因为函数没有return

    综上所述:

    a,b,c分别是3,5,None

    相关面试题(先从纸上写好答案,然后在运行):
    1,有函数定义如下:

    def calc(a,b,c,d=1,e=2):
        return (a+b)*(c-d)+e

    请分别写出下列标号代码的输出结果,如果出错请写出Error。
    print(calc(1,2,3,4,5))_____ print(calc(1,2))____print(calc(e=4,c=5,a=2,b=3))___
    print(calc(1,2,3))_____ print(calc(1,2,3,e=4))____print(calc(1,2,3,d=5,4))_____

     1.print(calc(1,2,3,4,5))

    执行函数calc,a,b,c,d,e分别对应1,2,3,4,5

    (a+b)*(c-d)+e 等同于 (1+2)*(3-4)+5

    先计算括号的 3*-1 = -3 最后计算加法 -3+5 = 2 

    2.print(calc(1,2))

    执行函数calc,a,b,c参数是必须要传的,这一步只传了2个参数,所以执行报错

    3.print(calc(e=4,c=5,a=2,b=3))

    执行函数calc,a,b,c,d,e分别对应2,3,5,1,4 由于没有传d,所以为默认值1

    (a+b)*(c-d)+e 等同于 (2+3)*(5-1)+4

    先计算括号的 5*4=20,最后计算加法20+4 = 24

    4.print(calc(1,2,3))

    执行函数calc,a,b,c,d,e分别对应 1,2,3,1,2 由于没有传c,d 所以默认值为1,2

    (a+b)*(c-d)+e 等同于 (1+2)*(3-1)+2

    先计算括号的 3*2=6 最后计算加法6+2 = 8

    5.print(calc(1,2,3,e=4))

    执行函数calc,a,b,c,d,e分别对应 1,2,3,1,4 由于没有传d,所以为默认值1

    (a+b)*(c-d)+e 等同于 (1+2)*(3-1)+4

    先计算括号的 3*2=6 最后计算加法6+4 = 10

    6.print(calc(1,2,3,d=5,4))

    执行函数calc,由于关键字参数(d=5)必须在位置参数的后面,所以执行报错

    所以最后结果为

    print(calc(1,2,3,4,5))__2___ print(calc(1,2))__Error__print(calc(e=4,c=5,a=2,b=3))_24__
    print(calc(1,2,3))__8___ print(calc(1,2,3,e=4))__10__print(calc(1,2,3,d=5,4))__Error___

    2,下面代码打印的结果分别是_________,________,________.

    def extendList(val,list=[]):
        list.append(val)
        return list
    list1 = extendList(10)
    list2 = extendList(123,[])
    list3 = extendList('a')
    
    print('list1=%s'%list1)
    print('list2=%s'%list2)
    print('list3=%s'%list3)
    

    分析代码:

    def extendList(val,list=[]):
        list.append(val)
        print(id(list)) #由于list是局部变量,只能在这里打印内存地址
        return list
    list1 = extendList(10) #var=10,list为默认参数,它是空列表,执行append,最终结果list1 = [10]
    
    list2 = extendList(123,[]) #var=123,list的值被覆盖,开辟了一个新的内存地址,执行append,最终结果list2 = [123]
    
    list3 = extendList('a') #var='a',list为默认参数,由于默认list用的是同一个内存地址,此时list为[10],执行append,最终结果list3 = [10,'a']
    
    print('list1=%s'%list1) #输出list1=[10,'a'],因为list1和list3使用的是默认值,共用一个内存地址。所以这2个值是一样的。
    print('list2=%s'%list2) #输出list2=[123]
    print('list3=%s'%list3) #输出list3=[10,'a']
    

    执行输出:

    1499634951496
    1499634952520
    1499634951496
    list1=[10, 'a']
    list2=[123]
    list3=[10, 'a']

    可以看出,list1和list3的list局部变量,用的是同一个内存地址,而list2用的是新的内存地址。

    所以最终结果为:

    list1=[10, 'a']
    list2=[123]
    list3=[10, 'a']

    6,默写:

    1,形参的接收顺序。

    顺序从左至右:

    位置参数,*args,默认参数,**kwargs


    2,什么是命名空间,什么是全局名称空间,什么是局部名 称空间,什么是内置名称空间。

    命名空间:存放名字与值的关系的空间

    全局命名空间:代码在运行伊始,创建的存储“变量名与值的关系”的空间
    局部命名空间:在函数的运行中开辟的临时的空间

    内置名称空间:  python自带的名字,如print、int、str,解释器启动就会生效

    3,什么是全局作用域,什么是局部作用域。

    全局作用域:包含内置名称空间、全局名称空间,在整个文件的任意位置都能被引用、全局有效

    局部作用域:局部名称空间,只能在局部范围内生效


    4,名称空间的加载顺序,取值顺序。

    加载顺序:内置命名空间(程序运行前加载)->全局命名空间(程序运行中:从上到下加载)->局部命名空间(程序运行中:调用时才加载)

    取值顺序

      在局部调用:局部命名空间->全局命名空间->内置命名空间

      在全局调用:全局命名空间->内置命名空间

    综上所述,在找寻变量时,从小范围,一层一层到大范围去找寻。


    5,解释一下什么是global,什么是nonlocal。

    global:

      1,声明一个全局变量。

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

    nonlocal:

      1,不能修改全局变量。

      2,在局部作用域中,对父级作用域的变量进行引用和修改,并且引用的哪层,从那层及以下此变量全部发生改变。

  • 相关阅读:
    入职linux基本命令学习
    C++大师Stan Lippman:我对中国程序员的忠告
    Create Your Own Search Engine with Python
    获取相加等于某个值的集合
    关于mysql的连接数问题
    修改Windows2003Server的Telnet默认端口(23)为其它端口的方法
    解决WCF传输大数据量时出错并提示:远程主机强迫关闭了一个现有的连接
    IIS服务器静态网页被挂马和解决方法(ZT)
    CAD2016 Object Arx开发环境搭建与HelloWorld实现
    我的Java之路(5)
  • 原文地址:https://www.cnblogs.com/xiao987334176/p/8669958.html
Copyright © 2020-2023  润新知