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

函数,参数及参数解构

函数

       数学定义: 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语句删除函数对象.

       上级作用域销毁时.

原文地址:https://www.cnblogs.com/amesy/p/7722187.html