• 函数,参数及参数解构, 返回值,作用域


    函数,参数及参数解构

    函数

           数学定义: y=f(x),y是x的函数,x是自变量.

       python函数: 完成一定的功能,由若干语句组成的语句块,函数名称,参数列表构成,它是组织代码的最小单位.

       

        函数的作用:

           结构化变成对代码的最基本的封装,一般按照功能组织一段代码.

           封装的目的为了复用,减少冗余代码.

           代码更加简洁美观,可读易懂.

       

        函数的分类:

           内建函数: 如max(),reversed()等.

           库函数: 如math.ceil()等.

    函数定义,调用

           def语句定义函数.

        语法:

           def 函数名(参数列表):

               函数体(代码块)

               [return 返回值]

           函数名就是标识符,命名要求一样.

           语句块必须缩进,约定4个空格.

           python的函数没有return语句,隐式会返回一个None值.

           定义中的参数列表成为形式参数,只是一种符号表达,简称形参.

           调用:

           函数定义,只是声明了一个函数,它不会被执行,需要调用.

           调用的方式,就是函数名加上小括号,括号内写上参数.

           调用时写的参数是实际参数,是实实在在传入的值,简称实参.

        函数举例:

          

    1

    2

    3

    4

    5

    def add(x, y):

        result = x + y

        return result

    out = add(4, 5)

    print(out)  # 9

       

    上面只是一个函数的定义,有一个函数add,接收2个参数.

        计算的结果,通过返回值返回.

        调用通过函数名add加2个参数,返回值可使用变量接收.

        定义需要在调用前,也就是说调用时,已经被定义过了,否则抛异常NameError.

        函数是可调用的对象,callable().

    函数参数

           参数调用时传入的参数要和定义的个数相匹配(可变参数例外).

        位置参数:

           def f(x,y,z) 调用使用f(1,3,5).

           按照参数定义顺序传入实参.

        关键字参数:

           def f(x,y,z)调用使用f(x=1,y=3,z=5).

           使用形参的名字来出入实参的方式,如果使用了形参名字,那么传参顺序就可以和定义顺序不同.

           传参:

               f(z=None,y=10,x=[1])

               f((1,),z=6,y=4.1)

               f(y=5,z=6,2)

               要求位置参数必须在关键字参数之前传入,位置参数是按位置对应的.

       参数规则: 参数列表参数一般顺序是,普通参数,缺省参数,可变位置参数,keyword-only参数(可带缺省值),可变关键字参数.

       

    1

    2

    3

    4

    5

    6

    7

    In [44]: def fn(x,y,z=3,*arg,m=4,n,**kwargs):

        ...:     print(x,y,z,m,n)

        ...:     print(args)

        ...:     print(kwargs)

        ...:

     

    In [45]:

        注:

           代码应该易读易懂,而不是为难别人.

           按照书写习惯定义函数参数.

    函数参数默认值

           参数默认值:

           定义时,在形参后跟上一个值.

       

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    def add(x=4,y=5):

        result = x + y

        print(result)

     

    add(6,10)

    add(6,y=7)

    add(x=5)

    add()

    add(y=7)

    # add(x=5,6) # error.

    # add(y=8,4) # error.

    add(x=5,y=6)

    add(y=5,x=6)

       

           注: def add(x=4, y)是错误的.

       

        作用:

           参数的默认值可以在未传入足够的实参的时候,对没有给定的参数赋值为默认值.

           参数非常多时,并不需要用户每次都输入所有的参数,简化函数调用.

        举例:

           定义一个函数login,参数名称为host,port,username,password.

          

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    def login(host='127.0.0.1', port='8080', username='ames', password='ames'):

        print('{}:{}@{}/{}'.format(host, port, username, password))

    login()

    login('127.0.0.1', 80, 'tom', 'tom')

    login('127.0.0.1', username='root')

    login('localhost', port=80, password=1234)

    login(port=80,password='python', host='www')

     

    # 运行结果:

     

    127.0.0.1:8080@ames/ames

    127.0.0.1:80@tom/tom

    127.0.0.1:8080@root/ames

    localhost:80@ames/1234

    www:80@ames/python

    可变参数

           一个形参可以匹配任意个参数.

       

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    In [1]: def add(nums):

       ...:     sum = 0

       ...:     for x in nums:

       ...:         sum += x

       ...:     return sum

       ...:

     

    In [2]: add([1, 3, 5])

    Out[2]: 9

     

    In [3]: add([2, 4, 6])

    Out[3]: 12

     

    In [4]:

        位置参数的可变参数: 有多个数,需要累加求和.

       

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    In [4]: def add(*nums):

       ...:     sum = 0

       ...:     print(type(nums))

       ...:     for x in nums:

       ...:         sum += x

       ...:     print(sum)

       ...:

     

    In [5]: add(3, 6, 9)

    <class 'tuple'>

    18

     

    In [6]:

        在形参前使用*表示该形参是可变参数,可以接收多个实参.

        收集多个实参为一个tuple.

       

        关键字参数的可变参数:

       

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    In [6]: def showconfig(**kwargs):

       ...:     for k,v in kwargs.items():

       ...:         print('{} = {}'.format(k,v))

       ...:

     

    In [7]: showconfig(host='127.0.0.1', port=8080, username='ames', password=123456)

    username = ames

    host = 127.0.0.1

    port = 8080

    password = 123456

     

    In [8]:

     

    # 运行结果:

    password = 123456

    port = 8080

    username = ames

    host = 127.0.0.1

       

        形参前使用**符号,表示可以接收多个关键字参数.

        收集的实参名称和值组成一个字典.

       

        可变参数混合使用:

           def showconfig(username,password,**kwargs)

           def showconfig(username,*args,**kwargs)

           def showconfig(username,password,**kwargs,*args)

       

        总结:

           可变参数分为位置可变参数和关键字可变参数.

           位置可变参数在形参前使用一个星号*.

           关键字可变参数在形参前使用两个星号**.

           位置可变参数和关键字可变参数都可以收集若干个实参,位置可变参数收集形成一个tuple,关键字可变参数收集形成一个dict.

           混合使用参数时,可变参数要放在参数列表的最后,普通参数需要放到参数列表前面,位置可变参数需要放在关键字可变参数之前.

        可变参数举例:

       

        举例1:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    In [8]: def fn(x,y,*args,**kwargs):

       ...:     print(x)

       ...:     print(y)

       ...:     print(args)

       ...:     print(kwargs)

       ...:

     

    In [9]: fn(3,5,7,9,10,a=1,b='ames')

    3

    5

    (7, 9, 10)

    {'a': 1, 'b': 'ames'}

     

    In [10]: fn(13,15)

    13

    15

    ()

    {}

     

    In [11]: fn(23,25,27)

    23

    25

    (27,)

    {}

     

    In [12]: fn(33,35,a=1,b='ames')

    33

    35

    ()

    {'a': 1, 'b': 'ames'}

     

    In [13]: fn(7,9,y=5,x=3,a=1,b='ames')

    ---------------------------------------------------------------------------

    TypeError                                 Traceback (most recent call last)

    <ipython-input-13-a0c84e79923c> in <module>()

    ----> 1 fn(7,9,y=5,x=3,a=1,b='ames')

     

    TypeError: fn() got multiple values for argument 'y'  # 错误,7和9分别赋给了x,y,又y=5 x=3,x和y的值重复.

     

    In [14]:

        举例2:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    In [14]: def fn(*args, x, y, **kwargs):

        ...:     print(x)

        ...:     print(y)

        ...:     print(args)

        ...:     print(kwargs)

        ...:

     

    In [15]: fn(3, 5)

    ---------------------------------------------------------------------------

    TypeError                                 Traceback (most recent call last)

    <ipython-input-15-4ac8ce4a4cba> in <module>()

    ----> 1 fn(3, 5)

     

    TypeError: fn() missing 2 required keyword-only arguments: 'x' and 'y'

     

    In [16]: fn(13, 15, 17)

    ---------------------------------------------------------------------------

    TypeError                                 Traceback (most recent call last)

    <ipython-input-16-75940373c950> in <module>()

    ----> 1 fn(13, 15, 17)

     

    TypeError: fn() missing 2 required keyword-only arguments: 'x' and 'y'

     

    In [17]: fn(23, 25, a=1, b='ames')

    ---------------------------------------------------------------------------

    TypeError                                 Traceback (most recent call last)

    <ipython-input-17-709612ad798f> in <module>()

    ----> 1 fn(23, 25, a=1, b='ames')

     

    TypeError: fn() missing 2 required keyword-only arguments: 'x' and 'y'

     

    In [18]: fn(37, 39, y=35, x=33, a=1, b='ames')

    33

    35

    (37, 39)

    {'a': 1, 'b': 'ames'}

     

    In [19]:

    keyword-only参数

           keyword-only参数 (python3加入). 

            如果在一个星号参数后,或者一个位置可变参数后,出现的普通参数,实际上已经不是普通的参数,而是keyword-only参数.

       

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    In [19]: def fn(*args, x):

        ...:     print(x)

        ...:     print(args)

        ...:

     

    In [20]: fn(3, 5)

    ---------------------------------------------------------------------------

    TypeError                                 Traceback (most recent call last)

    <ipython-input-20-4ac8ce4a4cba> in <module>()

    ----> 1 fn(3, 5)

     

    TypeError: fn() missing 1 required keyword-only argument: 'x'

     

    In [21]: fn(13, 15, 17)

    ---------------------------------------------------------------------------

    TypeError                                 Traceback (most recent call last)

    <ipython-input-21-57115e70fa2e> in <module>()

    ----> 1 fn(13, 15, 17)

     

    TypeError: fn() missing 1 required keyword-only argument: 'x'

     

    In [22]: fn(23, 25, x = 27)

    27

    (23, 25)

     

    In [23]:

        注: 如上错误,args可看做已经截获了所有位置参数,x不使用关键字参数就不可能拿到实参.

            以上函数如果换成def fn(**kwargs,x),会直接报语法错误,可以理解为kwargs会截获所有的关键字,就算写了x=5,x也永远得不到这个值,所以语法错误.

       

    keyword-only参数的另一种形式:

    1

    2

    3

    4

    5

    6

    7

    8

    In [23]: def fn(*,x,y):

        ...:     print(x,y)

        ...:

     

    In [24]: fn(x=5,y=6)

    5 6

     

    In [25]:

     

    *号之后,普通形参都变成了必须给出的keyword-only参数.

    可变参数和参数默认值

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    In [25]: def fn(*args,x=5):

        ...:     print(x)

        ...:     print(args)

        ...:

     

    In [26]: fn()  # 等价于fn(x=5)

    5

    ()

     

    In [27]: fn(5)

    5

    (5,)

     

    In [28]: fn(x=6)

    6

    ()

     

    In [29]: fn(1,2,3,x=10)

    10

    (1, 2, 3)

     

    In [30]:

          

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    In [30]: def fn(y,*args,x=5):

        ...:     print('x={},y={}'.format(x,y))

        ...:     print(args)

        ...:

     

    In [31]: fn()

    ---------------------------------------------------------------------------

    TypeError                                 Traceback (most recent call last)

    <ipython-input-31-fd7064640434> in <module>()

    ----> 1 fn()

     

    TypeError: fn() missing 1 required positional argument: 'y'

     

    In [32]: fn(5)

    x=5,y=5

    ()

     

    In [33]: fn(x=6)

    ---------------------------------------------------------------------------

    TypeError                                 Traceback (most recent call last)

    <ipython-input-33-704ccb59ea7c> in <module>()

    ----> 1 fn(x=6)

     

    TypeError: fn() missing 1 required positional argument: 'y'

     

    In [34]: fn(1,2,3,x=10)

    x=10,y=1

    (2, 3)

     

    In [35]: fn(y=17,2,3,x=10)

      File "<ipython-input-35-717813843388>", line 1

        fn(y=17,2,3,x=10)

               ^

    SyntaxError: positional argument follows keyword argument

     

     

    In [37]: fn(17,2,3,x=10)

    x=10,y=17

    (2, 3)

     

    In [36]: fn(1,2,y=3,x=10)

    ---------------------------------------------------------------------------

    TypeError                                 Traceback (most recent call last)

    <ipython-input-36-c795acbab6ef> in <module>()

    ----> 1 fn(1,2,y=3,x=10)

     

    TypeError: fn() got multiple values for argument 'y'

     

    In [38]:

          

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    In [38]: def fn(x=5, **kwargs):

        ...:     print('x={}'.format(x))

        ...:     print(kwargs)

        ...:

     

    In [39]: fn()

    x=5

    {}

     

    In [40]: fn(5)

    x=5

    {}

     

    In [41]: fn(x=6)

    x=6

    {}

     

    In [42]: fn(y=3,x=10)

    x=10

    {'y': 3}

     

    In [43]: fn(3,y=10)

    x=3

    {'y': 10}

     

    In [44]:

    参数解构

           参数解构:

           给函数提供实参时,可在集群类型前使用*或**,把集群类型的解构解开,提取出所有元素作为函数的实参.

           非字典类型使用*解构成位置参数.

           字典类型使用**解构成关键字参数.

           提取出来的元素数目要和参数的要求匹配,也要和参数的类型匹配.

          

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    In [45]: def add(x,y):

        ...:     return x+y

        ...:

     

    In [46]: add(*(4,5))

    Out[46]: 9

     

    In [47]: add(*[4,5])

    Out[47]: 9

     

    In [48]: add(*{4,6})

    Out[48]: 10

     

    In [49]: d = {'x':5, 'y':6}

     

    In [50]: add(**d)

    Out[50]: 11

     

    In [51]: add(**{'x':5, 'y':6})

    Out[51]: 11

     

    In [52]:

       

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    In [54]: def add(*iterable):

        ...:     result = 0

        ...:     for x in iterable:

        ...:         result += x

        ...:     return result

        ...:

     

    In [55]: add(1,2,3)

    Out[55]: 6

     

    In [56]: add(*[1,2,3])

    Out[56]: 6

     

    In [57]: add(*range(10))

    Out[57]: 45

        练习:

       

    1.编写一个函数,能够接受至少2个参数,返回最小值和最大值.

       

    1

    2

    3

    4

    5

    6

    7

    8

    9

    import random

     

    def double_values(*nums):

        print(nums)

        return max(nums), min(nums)

    print(*double_values(*[random.randint(10, 20) for _ in range(10)]))  # 两次解构

    # 运行结果:

    (18, 16, 16, 12, 13, 13, 14, 20, 18, 16)

    20 12

    1. 编写一个函数,接受一个参数n,n为正整数,左右两种打印方式.要求数字必须对齐.

       

    1

    2

    3

    4

    5

    6

    7

    def show(n):

        tail = ' '.join([str(i) for i in range(n, 0, -1)])

        width = len(tail)

        for i in range(1, n):

            print("{:>{}}".format(" ".join([str(j) for j in range(i, 0, -1)]),width))

        print(tail)

    show(12)

       

    1

    2

    3

    4

    5

    6

    7

    def showtail(n):

        tail = ' '.join([str(i) for i in range(n, 0, -1)])

        print(tail)

        for i in range(len(tail)):

            if tail[i] == ' ':

                print(' '*i, tail[i+1:])

    showtail(12)

    插入排序

    函数返回值,作用域

    函数的返回值

           举例:

       

    1

    2

    3

    4

    5

    6

    7

    8

    9

    def showplus(x):

        print(x)

        return x+1

        print(x+1)  # 该行不会执行.

    print(showplus(5))

     

    # 运行结果:

    5

    6

       

        多条return语句:  

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    def guess(x):

        if x > 3:

            return '>3'

        else:

            return '<=3'

    print(guess(10))

    -------------------------------------------

    def showplus(x):

        print(x)

        return x+1

        return x+2  # 该行不会执行.

    print(showplus(5))

     

    # 运行结果:

    5

    6

       

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    def fn(x):

        for i in range(x):

            if i > 3:

                return i

        else:

            print('{} is not greater than 3'.format(x))

    print(fn(5)) # 运行结果: 4

    print(fn(3))

     

    # 运行结果:

    3 is not greater than 3

    None   # 所有函数都有函数值,如果没有return语句,隐式调用return None.

           总结:

           python函数使用return语句返回'返回值'.

           所有函数都有函数值,如果没有return语句,隐式调用return None.

           return语句并不一定是函数的语句块的最后一条语句.

           一个函数可以存在多个return语句,但是只有一条可以被执行.如果没有一条return语句被执行到,隐式调用return None.

           如果有必要,可以显示调用return None,可以简写为return.

           如果函数执行了return语句,函数就会返回,当前被执行的return语句之后的其他语句就不会被执行.

           作用: 结束函数调用和返回值.

       

        返回多个值:

       

    1

    2

    3

    4

    5

    6

    7

    def showlist():

        return [1, 3, 5]

    print(showlist())  # 运行结果: [1, 3, 5]

     

    def showlist():

        return 1, 3, 5

    print(showlist())  # 运行结果: (1, 3, 5)

        注:

           函数不能同时返回多个值.

           return [1,3,5] 即指明返回一个列表,是一个list对象.

           return 1,3,5 看似返回多个值,隐式的被python封装成了一个元组.

       

    1

    2

    3

    4

    def showlist():

        return 1,3,5

    x, y, z = showlist()

    print(x, y, z)  # 运行结果: 1 3 5

    函数嵌套

           函数嵌套:

           在一个函数中定义了另一个函数.

          

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    In [4]: def outer():

       ...:     def inner():

       ...:         print('inner')

       ...:     print('outer')

       ...:     inner()

       ...:

     

    In [5]: outer()

    outer

    inner

     

    In [6]: inner()

    ---------------------------------------------------------------------------

    NameError                                 Traceback (most recent call last)

    <ipython-input-6-159a447ee30d> in <module>()

    ----> 1 inner()

     

    NameError: name 'inner' is not defined

     

    In [7]:

        注:

           函数有可见范围,这就是作用域的概念.

           内部函数不能被外部直接使用,会抛异常NameError,如上代码.

       

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    def outer():

         def inner():

             print('inner')

             return 'xx'  # return None

         print('outer')

         inner()

         return inner(),'p'

    print(outer())

     

    # 执行结果

    outer

    inner

    inner

    ('xx', 'p')  # 两次return的结果组成了一个元组.

     

    作用域

           作用域: 一个标识符的可见范围即标识符的作用域.一般常说的是变量的作用域.

       

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    In [9]: x = 5

       ...: def foo():

       ...:     print(x)

       ...:

     

    In [10]: foo()

    5

     

    In [11]:

    In [13]: x = 5

        ...: def foo():

        ...:     x += 1   # x = x + 1

        ...:     print(x)

        ...:

     

    In [14]: foo()

     

    # 运行报错:

    UnboundLocalError: local variable 'x' referenced before assignment

     

    In [15]:

       

        全局作用域:

           在整个程序运行环境中都可见.

        局部作用域:

           在函数,类等内部可见.

           局部变量使用范围不能超过其所在的局部作用域.

       

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    In [31]: def fn1():

        ...:     x = 1  # 局部作用域,在fn1内.

        ...: def fn2():

        ...:     print(x)

        ...:

     

    In [32]: print(x)

    ---------------------------------------------------------------------------

    NameError                                 Traceback (most recent call last)

    <ipython-input-32-57a99df8cebf> in <module>()

    ----> 1 print(x)

     

    NameError: name 'x' is not defined

     

    In [33]:

        嵌套结构:

       

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    def outer1():

        o = 65

        def inner():

            print('inner {}'.format(o))

            print(chr(o))

        print('outer {}'.format(o))

        inner()

     

    outer1()

     

    # 运行结果:

    outer 65

    inner 65

    A

     

    def outer2():

        o = 65

        def inner():

            o = 97

            print('inner {}'.format(o))

            print(chr(o))

        print('outer {}'.format(o))

        inner()

     

    outer2()

     

    # 运行结果:

    outer 65

    inner 97

    a

     

        从嵌套结构例子看出:

           外层变量作用域在内层作用域可见.

           内层作用域inner中,如果定义了o=97,相当于当前作用域中重新定义了一个新的变量o,但是这个o并没有覆盖外层作用域outer中的o.

       

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    In [1]: x = 5

     

    In [2]: def foo():

       ...:     y = x + 1

       ...:     x += 1

       ...:     print(x)

       ...:

     

    In [3]: foo()

    ---------------------------------------------------------------------------

    UnboundLocalError                         Traceback (most recent call last)

    <ipython-input-3-624891b0d01a> in <module>()

    ----> 1 foo()

     

    <ipython-input-2-4a190c7f9e12> in foo()

          1 def foo():

    ---> 2     y = x + 1

          3     x += 1

          4     print(x)

          5

     

    UnboundLocalError: local variable 'x' referenced before assignment

     

    In [4]: def foo():

       ...:     y = x + 1

       ...:     # x += 1

       ...:     print(x)

       ...:

     

    In [5]:

     

    In [5]: foo()

    5

     

    In [6]:

       

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    In [14]: x = 5

     

    In [15]: def foo():

        ...:     x += 1

        ...:     return x

        ...:

     

    In [16]: foo()

    ---------------------------------------------------------------------------

    UnboundLocalError                         Traceback (most recent call last)

    <ipython-input-16-624891b0d01a> in <module>()

    ----> 1 foo()

     

    <ipython-input-15-36b35103f745> in foo()

          1 def foo():

    ----> 2     x += 1

          3     return x

     

    UnboundLocalError: local variable 'x' referenced before assignment

     

    In [17]:

     

    注意:

    x += 1 其实是 x = x + 1

    相当于在foo内部定义一个局部变量x,那么foo内部所有x都是这个局部变量x了.

    但是这个x还没完成复制,就被右边拿来做加1操作了,故报错.

     

        全局变量: global

       

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    In [20]: x = 5

     

    In [21]: def foo():

        ...:     global x

        ...:     x += 1

        ...:     return x

        ...:

     

    In [22]: foo()

    Out[22]: 6

     

    In [23]:

        使用global关键字的变量,将foo内的x声明为使用外部的全局作用域中定义的x.

        全局作用域中必须有x的定义.

        如果全局作用域中没有x定义:

       

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    In [1]: def foo():

       ...:     global x

       ...:     x = 10

       ...:     x += 1

       ...:     print(x)

       ...:

     

    In [2]: foo()

    11

     

    In [3]: print(x)

    11

     

    In [4]:

        使用global关键字的变量,将foo内的x声明为使用外部的全局作用域中定义的x.

        但是,x = 10赋值即定义,x在内部作用域为一个外部作用域的变量赋值,所以x += 1不会报错.注意这里的x的作用域还是全局的.

        global总结:

          x+=1这种是特殊形式产生的错误的原因,先引用后赋值,而python动态语言是赋值才算定义,才能被引用.解决办法是在这条语句前增加x=0之类的赋值语句,或者使用global告诉内部作用域,去全局作用域查找变量定义.

           内部作用域使用x=5之类的赋值语句会重新定义局部作用域使用的变量x,但是一旦这个作用域中使用的global声明为全局的,那么x=5相当于在为全局作用域的变量x赋值.

       

        global使用原则:

           外部作用域变量在内部作用域可见,但也不要在这个内部的局部作用域中直接使用,因为函数的目的就是为了封装,尽量与外界隔离.

           如果函数需要使用外部全局变量,使用函数的形参传参解决.

    闭包

           自由变量: 未在本地作用域中定义的变量.例如定义在内存函数外的外层函数的作用域中的变量.

        闭包: 就是一个概念,出现在嵌套函数中,指内层函数引用到外层函数的自由变量,就形成了闭包,JavaScript就是如此.

       

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    In [8]: def counter():

       ...:     c = [0]

       ...:     def inc():

       ...:         c[0] += 1

       ...:         return c[0]

       ...:     return inc

       ...:

     

    In [9]: foo = counter()

     

    In [10]: print(foo(), foo())

    1 2

     

    In [11]: c = 100

     

    In [12]: print(foo())

    3

     

    In [13]:

        代码解析:

           第四行没有报错,因为c已经在counter函数中定义过了,而且inc中的使用方式是为c的元素修改值,而不是重新定义变量.

           第十四行定义的c和counter中的c不一样,而inc引用的是自由变量而不是counter的变量c.

           这是python2中实现闭包的方式,python3还可以使用nonlocal关键字.

       

        如下代码:

       

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    In [15]: def counter():

        ...:     count = 0

        ...:     def inc():

        ...:         count += 1

        ...:         return count

        ...:     return inc

        ...:

     

    In [16]: foo = counter()

     

    In [17]: foo()

    ---------------------------------------------------------------------------

    UnboundLocalError                         Traceback (most recent call last)

    <ipython-input-17-624891b0d01a> in <module>()

    ----> 1 foo()

     

    <ipython-input-15-b7d707b2e550> in inc()

          2     count = 0

          3     def inc():

    ----> 4         count += 1

          5         return count

          6     return inc

     

    UnboundLocalError: local variable 'count' referenced before assignment

     

    In [18]: foo()

        以上报错,使用global可以解决,但是这样使用的是全局变量,而不是闭包.

        如果要使用普通变量的闭包,python3可以使用nonlocal.

    nonlocal关键字

           使用了nonlocal关键字,将变量标记为在上级的局部作用域中定义,但不能是全局作用域中定义.

       

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    In [20]: def counter():

        ...:     count = 0

        ...:     def inc():

        ...:         nonlocal count

        ...:         count += 1

        ...:         return count

        ...:     return inc

        ...:

     

    In [21]: foo = counter()

     

    In [22]: foo()

    Out[22]: 1

     

    In [23]: foo()

    Out[23]: 2

     

    In [24]:

    In [26]: a = 50

        ...: def counter():

        ...:     nonlocal a

        ...:     a += 1

        ...:     print(a)

        ...:     count = 0

        ...:     def inc():

        ...:         nonlocal count

        ...:         count += 1

        ...:         return count

        ...:     return inc

      File "<ipython-input-26-a29a1eb62faa>", line 3

        nonlocal a

        ^

    SyntaxError: no binding for nonlocal 'a' found

    # nonlocal 要绑定一个局部变量.

     

    In [27]:

       

        count是外层函数的局部变量,被内层函数引用.

        内部函数使用nonlocal关键字声明count变量在上一级作用域中.

        上面左边代码可以正常使用,且形成闭包;右边代码不能正常运行,变量a不能在全局作用域中.

    默认值的作用域

           默认值举例:

       

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    In [27]: def foo(xyz=1):

        ...:     print(xyz)

        ...:

     

    In [28]: foo()

    1

     

    In [29]: foo()

    1

     

    In [30]: print(xyz)

    ------------------------------------------------------------NameError                  Traceback (most recent call last)<ipython-input-30-a2bcd278357a> in <module>()

    ----> 1 print(xyz)

     

    NameError: name 'xyz' is not defined

     

    In [31]:

       

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    In [32]: def foo(xyz=[]):

        ...:     xyz.append(1)

        ...:     print(xyz)

        ...:

     

    In [33]: foo()

    [1]

     

    In [34]: foo()

    [1, 1]

     

    In [35]: print(xyz)

    ------------------------------------------------------------NameError                  Traceback (most recent call last)<ipython-input-35-2cf093bba2e6> in <module>()

    ----> 1 print(xyz)

     

    NameError: name 'xyz' is not defined

     

    In [36]:

        以上示例第二次调用foo函数打印[1, 1],是因为函数也是对象,python把函数的默认值放在了属性中,这个属性就伴随着这个函数对象的整个生命周期.

        查看属性: foo.__defaults__

       

        如下例子:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    In [39]: def foo(xyz=[], u='abc', z=123):

        ...:     xyz.append(1)

        ...:     return xyz

        ...:

     

    In [40]: foo(), id(foo)

    Out[40]: ([1], 2239176151928)

     

    In [41]: foo.__defaults__

    Out[41]: ([1], 'abc', 123)

     

    In [42]: foo(), id(foo)

    Out[42]: ([1, 1], 2239176151928)

     

    In [43]: foo.__defaults__

    Out[43]: ([1, 1], 'abc', 123)

     

    In [44]:

        函数地址并没有变,就是说函数这个对象的地址没有变,调用它,它的属性__defaults__中使用元组保存所有默认值.

        xyz默认值是引用类型,引用类型的元素变动,并不是元组的变化.

       

        非引用类型例子:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    In [48]: def foo(w,u='abc',z=123):

        ...:     u='xyz'

        ...:     z=789

        ...:     print(w,u,z)

        ...:

     

    In [49]: print(foo.__defaults__)

    ('abc', 123)

     

    In [50]: foo('ames')

    ames xyz 789

     

    In [51]: print(foo.__defaults__)

    ('abc', 123)

     

    In [52]:

        属性__defaults__中使用元组保存所有默认值,它不会因为在函数体内使用了它而发生改变.

        可变类型默认值,如果使用默认值,就可能修改这个默认值.

        有时候这个特性是好的,有时候这种特性是不好的,有副作用.

       

        如下例子做到了按需改变:

       

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    In [1]: def foo(xyz=[], u='abc', z=123):

       ...:     xyz = xyz[:] # 影子拷贝.

       ...:     xyz.append(1)

       ...:     print(xyz)

       ...:

     

    In [2]: foo()

    [1]

     

    In [3]: print(foo.__defaults__)

    ([], 'abc', 123)

     

    In [4]: foo()

    [1]

     

    In [5]: print(foo.__defaults__)

    ([], 'abc', 123)

     

    In [6]: foo([10])

    [10, 1]

     

    In [7]: print(foo.__defaults__)

    ([], 'abc', 123)

     

    In [8]: foo([10,5])

    [10, 5, 1]

     

    In [9]: print(foo.__defaults__)

    ([], 'abc', 123)

     

    In [10]:

     

    # 注:

    1)函数体内,不改变默认值.

    2)xyz都是传入参数或者默认参数的副本,如果想修改原参数,则无能为力.

    In [10]: def foo(xyz=None, u='abc', z=123):

        ...:     if xyz is None:

        ...:         xyz = []

        ...:     xyz.append(1)

        ...:     print(xyz)

        ...:

     

    In [11]: foo()

    [1]

     

    In [12]: foo.__defaults__

    Out[12]: (None, 'abc', 123)

     

    In [13]: foo()

    [1]

     

    In [14]: foo.__defaults__

    Out[14]: (None, 'abc', 123)

     

    In [15]: foo([10])

    [10, 1]

     

    In [16]: foo.__defaults__

    Out[16]: (None, 'abc', 123)

     

    In [17]: foo([10,5])

    [10, 5, 1]

     

    In [18]: foo.__defaults__

    Out[18]: (None, 'abc', 123)

     

    In [19]:

     

    # 注:

    使用不可变类型默认值:

      如果使用缺省值None就创建一个列表. 

      如果传入一个列表,就修改这个列表.

        第一种方法: 

           使用影子拷贝创建一个新的对象,永远不能改变传入的参数.

        第二种方法:

           通过值的判断就可以灵活的选择创建或修改传入对象.

           这种方式灵活,应用广泛.

           很多函数的定义,都可以看到使用None这个不可变的值作为默认参数,可以说这是一种惯用法.

    变量名解析原则LEGB

           Local,本地作用域,局部作用域的local命名空间.函数调用时创建,调用结束消亡.

        Enclosing, python2.2时引入了嵌套函数,实现了闭包,这个就是嵌套函数的外部函数的命名空间.

        Global,全局作用域,即一个模块的命名空间.模块被import时创建,解释器退出时消亡.

        Build-in, 内置模块的命名空间,生命周期从python解释器启动时创建到解释器退出时消亡.例如print(open),print和open都是内置的变量.

        所以一个名词的查找顺序就是LEGB.

          

    函数的销毁

           全局函数:  

       

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    In [1]: def foo(xyz=[], u='abc', z=123):

       ...:     xyz.append(1)

       ...:     return xyz

       ...:

     

    In [2]: foo(), id(foo), foo.__defaults__

    Out[2]: ([1], 1585221689688, ([1], 'abc', 123))

     

    In [4]: def foo(jdk=[], h='abc', p=123):

       ...:     jdk.append(1)

       ...:     return jdk

       ...:

     

    In [5]: print(foo(), id(foo), foo.__defaults__)

    [1] 1585222352680 ([1], 'abc', 123)

     

    In [6]: del foo

     

    In [7]: print(foo(), id(foo), foo.__defaults__)

    ------------------------------------------------------------

     

    NameError                  Traceback (most recent call last)

     

    <ipython-input-7-7e639523779a> in <module>()

    ----> 1 print(foo(), id(foo), foo.__defaults__)

     

    NameError: name 'foo' is not defined

     

    In [8]:

       

        全局函数销毁:

           重新定义同名函数.

           del语句删除函数对象.

           程序结束时.

       

        局部函数:

       

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    In [2]: def foo(xyz=[], u='abc', z=123):

       ...:     xyz.append(1)

       ...:     def inner(a=10):

       ...:         pass

       ...:     print(inner)

       ...:     def inner(a=100):

       ...:         print(xyz)

       ...:     print(inner)

       ...:     return inner

       ...:

     

    In [3]: bar = foo()

    <function foo.<locals>.inner at 0x000001F99DA99A60>

    <function foo.<locals>.inner at 0x000001F99DA99AE8>

     

    In [4]: print(id(foo), id(bar), foo.__defaults__, bar.__defaults__)

    2171610546656 2171603622632 ([1], 'abc', 123) (100,)

     

    In [5]: del bar

     

    In [6]: print(id(foo), id(bar), foo.__defaults__, bar.__defaults__)

    ---------------------------------------------------------------------------

    NameError                                 Traceback (most recent call last)

    <ipython-input-6-cbce72c73fb2> in <module>()

    ----> 1 print(id(foo), id(bar), foo.__defaults__, bar.__defaults__)

     

    NameError: name 'bar' is not defined

     

    In [7]:

        局部函数销毁:

           重新在上级作用域定义同名函数.

           del语句删除函数对象.

           上级作用域销毁时.

  • 相关阅读:
    javascript函数的定义和调用(包括方法)
    iterable(遍历)
    循环
    C#基础知识 简单说明泛型的优点
    C#基础知识 yield与foreach
    C#基础知识 结构与类的区别
    Asp.net MVC 生成zip并下载
    Asp.net MVC 填充word并下载
    Asp.net MVC 简单实现生成Excel并下载
    CTF中怎么看phpinfo
  • 原文地址:https://www.cnblogs.com/amesy/p/7722187.html
Copyright © 2020-2023  润新知