Python进阶-II 参数陷阱、命名空间、嵌套、作用域、闭包

一、参数陷阱

在使用默认参数时,可能碰见下列情况
def show_args_trap(i, li = []):
    li.append(100)
    li[i] = 101
    print(li)
show_args_trap(0)
show_args_trap(1)
show_args_trap(2)
# 显示结果为:
# [101]
# [101, 101]
# [101, 101, 101]

分析:
  大家知道:含有默认参数的,不输入该参数就使用默认的值;
在这里,第一次调用函数的时候,只传入一个参数0,这时默认参数起作用,li=[];
接着给这个列表尾部加入元素100, li = [100];
继续修改该列表,将第一元素替换成101,所以li=[101

  我们接着分析第二次调用:传入参数1,此时内存中已经有了li = [101],所以默认参数没起作用! ***此处就是陷阱所在,按照默认参数的使用规则,li此时应该为空的
列表追加元素100,所以 li = [101,100];
列表修改,li = [101,101];
.........

总结:如果默认参数的值是一个可变的数据类型,那么每一次调用函数的时候,如果不传值,就公用这个数据类型的资源

 

二、函数的命名空间和作用域

1、命名空间的引入

a = 1
def func():
    print(a)
func()

  每个名字都有其作用的范围,变量名a,可以在函数定义中使用,这说明a在一定的空间内!而且有其作用的范围!


2、命名空间的分类

  1)、内置命名空间 --- python解释器
     就是python解释器以启动就可以使用的名字,它存储在内置命令空间里;
    内置的名字在启动python解释器的时候就被加载到内存中了!
  2)、全局命名空间 -- 写的代码,但不是函数中的代码
    是在代码从上到下被执行的过程中,依次加入到内存中的,
    它放置了我们设置的所有变量名和函数名!
  3)、局部命名空间 -- 对应函数中的定义的名称
    就是函数内部定义的名字;
    当调用的时候,才会产生这个命名空间,随着函数的执行结束而结束,这个命名空间就消失了!''

 1 #   作用域及其分类
 2 '''       全局作用域 -- 作用在全局 -- 内置和全局命名空间中的名字都属于全局作用域 -- 可使用globals()
 3           局部作用域 -- 作用在局部 -- 基本在函数内部(局部命名空间中的名字属于局部作用域)  -- 可使用locals()'''
 4 a_b = 1
 5 def self_func():
 6     #a_b += 2   # 报错:局部作用域中,不能修改全局作用域中的变量
 7     global a_b  # 加关键字后即可,但是不推荐经常使用,特殊情况下用!
 8     a_b += 2
 9     return a_b
10 self_func()

 

3、名字的使用范围:
  1)、在局部命名空间:可以使用内置命名空间中的名字,也可以使用全局命名空间中的名字;
  2)、在全局命名空间:可以使用内置。
  3)、在内置命名空间:不能使用全局和局部命令空间中的名字,只能使用自己内部的名字。

1 b = 101
2 def func_inner():
3     c = b + 100  # 会划出内置命名空间,存储c,它用到了全局命名空间中的名字b
4     return c
5 print(func_inner())  # 这里print是内置命名空间中的名字
6 
7 def print():
8     return '局部命名空间中的名字print,与内置命名空间的名字print一样'
9 print()  # 在全局命名空间中,无法使用内置的命名空间中的名字,只能使用自己的和内置的!自己有,就不用调用内置的print函数

4、特殊使用范例:

def max(list):
    print('in max func')
print(max([1,2,3]))

说明:

  • 在正常情况下,直接使用内置的名字
  • 当我们在全局定义了和内置名字空间中同名的名字时,会使用全局的名字
  • 当我自己有的时候 我就不找我的上级要了
  • 如果自己没有 就找上一级要 上一级没有再找上一级 如果内置的名字空间都没有 就报错
  • 多个函数应该拥有多个独立的局部名字空间,不互相共享
#   在 “函数名()”中,函数名就是函数在内存中的地址,加()就是说要调用该函数了!
print(min) # <function max at 0x000002A08BCDE620>
print(min('ab11238'))

5、globals()与locals()的使用

# 对于不可变数据类型 在局部可是查看全局作用域中的变量
# 但是不能直接修改
# 如果想要修改,需要在程序的一开始添加global声明
# 如果在一个局部(函数)内声明了一个global变量,那么这个变量在局部的所有操作将对全局的变量有效
 1 a = 1
 2 b = 2
 3 def func():
 4     x = 'aaa'
 5     y = 'bbb'
 6     print(locals())     # 输出什么 根据locals所在的位置 --如今在局部中,所有输出的是局部的名称
 7     print(globals())  #  永远打印全局的名字
 8 
 9 func()
10 print(globals())
11 print(locals()) #本地的 现在全局中,所以打印的和globals()的结果一样!

 

三、嵌套和作用域

1、函数的嵌套的示范

def max(a, b):
    return a if a > b  else b

def max(x, y, z):
    c = max(x, y)
    return max(c, z)

2、函数的嵌套定义:
函数中定义另外一个函数,内部函数可以使用外部函数的变量

 1 a = 1 # 全局变量
 2 def outer():
 3     a = 1
 4     def inner():
 5         b = 2
 6        # global a
 7         nonlocal a  # 声明了一个离它最近的局部变量,对全局无效
 8         a += 1 #不可变数据类型的修改,不在一个作用域内,是非法的;需要声明nonlocal,不建议使用global
 9         print(a)    # 一个局部包含另外一个局部
10         print('inner space')
11         def inner2():  # 没有调用,就没输出,所有的函数都是先定义后调用
12             print(a, b)
13             print('inner 2 layer')
14         inner2()
15         print('局部第二层:',a)
16     inner() # 只有在才可以调用
17     print('局部第一层:', a)
18 outer()
19 print('全局',a)
20 
21 # 总结:nonlocal关键字,声明的变量仅仅对离它最近一层的局部变量有效,不管是不是在一个局部域内!
22 
23 # 函数名可以赋值
24 def func(): # 函数名就是内存地址
25     print('wo men')
26 func2 = func # 函数名可以赋值
27 func2()
28 
29 list = [func, func2] # 函数名可以作为容器类型的元素
30 for i in list:
31     print(123)
32 
33 # 函数名可以作为函数的参数
34 def arg_fun(f):
35     f()
36     return f
37 
38 arg_fun(func2)
39 
40 # 函数名可以作为函数的返回值
41 def fun_as_return(f):
42     return f
43 
44 fun_as_return(func2)()
45 
46 #  第一类对象
47 # 1、可以在运行期创建;
48 # 2、可用作参数
49 # 3、可存入变量的实体
View Code

 

四、闭包

1、定义:就是嵌套函数中,内部函数调用外部函数的变量

 1 def outer():
 2     a = 1
 3     def inner():
 4         print(a)
 5     print(inner.__closure__) # 结果:(<cell at 0x0000019EE0F37CA8: int object at 0x00000000763D5320>,)
 6 outer()
 7 print(outer.__closure__) # (<cell at 0x000002A5A6CC7CA8: int object at 0x00000000763D5320>,)   None
 8 
 9 # 运用闭包的常见的方式
10 def outer1():
11     a = 1
12     def inner():
13         print(a)
14     return inner
15 inn = outer1()
16 inn()
View Code

2、闭包的应用 爬虫初步

 1 #   import urllib   #python自带的模块,
 2 from urllib.request import urlopen
 3 #
 4 # # todo 改造下面的函数get_url()为闭包模式,不用每次调用的时候,频繁打开和关闭url变量,占用资源
 5 # def get_url():
 6 #     url = 'https://www.baidu.com/?tn=80035161_1_dg'
 7 #     result = urlopen(url).read()
 8 #     print(result)
 9 # get_url()
10 
11 def get_url():
12     url = 'https://www.baidu.com/?tn=80035161_1_dg'
13     def get_url_inner():
14         res = urlopen(url).read()
15         print(res)
16     return get_url_inner  # 与def 平级,否者报错,TypeError: 'NoneType' object is not callable
17 get = get_url()
18 get()
View Code
posted @ 2019-11-25 15:50  四方游览  阅读(277)  评论(2)    收藏  举报