一. 函数内容回顾
万能参数:
*args **kwargs
*魔性用法
函数的定义时:* ** 代表聚合
函数的调用时:* ** 代表打散
形参顺序:
位置参数, *args, 默认参数, **kwargs
名称空间
临时名称空间
def func():
name = "alex"
func()
print(name) # 找不到
func() # 再次调用又可以
内置名称空间
作用域:
全局作用域:内置名称空间,全局名称空间
局部作用域:临时名称空间,也称局部名称空间
加载顺序:
内置名称空间 > 全局名称空间 > 临时名称空间
取值顺序:
就近原则, LEGB
临时名称空间 > 全局名称空间 > 临时名称空间
内置函数:
globals(): 返回一个字典, 全局作用域的内容。
locals(): 返回一个字典, 当前作用域的内容。
关键字:
global:
1. 在局部作用域可以声明一个全局变量
2. 在局部作用域可以对全局变量进行修改
nonlocal:
1. 不能对全局变量进行修改
2. 子级对父级函数的变量的修改
二. 函数名的运用
1 # 1. 函数名可以当作一个特殊的变量
2
3 def func():
4 print(666)
5 print(func)
6 <function func at 0x00000203DE321E18>
7
8 # 这里是函数的内存地址
9 # 因此,函数名是一个特殊的变量
1 # 2. 函数名可以当做变量赋值
2 f1 = 2
3 f2 = f1
4 f3 = f2
5 print(f3) # 2
6
7 def func():
8 print(666)
9 func1 = func
10 f2 = func1
11 f3 = f2
12 print(f3)
13 f3()
14 # 这个示例跟上面那个普通的变量的赋值是一样的意思
1 # 3. 函数名可以当作容器类数据类型的元素
2 def func1():
3 print('in func1')
4
5 def func2():
6 print('in func2')
7
8 def func3():
9 print('in func3')
10
11 # 一次执行三个函数的办法
12 # 一开始只能这样
13 # func1()
14 # func2()
15 # func3()
16
17 # 现在可以这样:
18 l1 = [func1, func2, func3]
19 for i in l1:
20 i()
21
22 dic = {1: func1, 2: func2, 3: func3}
1 # 4. 函数名可以当作函数的参数
2 def func(x):
3 print(x)
4 print("in func")
5
6 def func1():
7 print("in func1")
8
9 func(func1)
10 # <function func1 at 0x000002000651AC80>
11 # in func
12
13 def func(x):
14 x()
15 print("in func")
16 def func1():
17 print("in func1")
18
19 func(func1)
20 # in func1
21 # in func
# 5. 函数名可以当作函数的返回值
def func(x):
print("in func")
return x
def func1():
print("in func1")
ret = func(func1) # 把 func1 当成一个变量x传入到func(x)中,打印 in func
ret() # func(func1)返回的是一个指向 in func1 的函数名
# in func
# in func1
# func(func1)() # 这个跟上面的一样
# func(func1()) # 这个是先调用func1,然后再调用 func(),把 func1 当作一个变量
# 函数名可以当作容器类数据类型的元素,函数名可以当作函数的参数,函数名可以当作函数的返回值,这三点得知函数是第一类对象
三. 闭包
1. 内层函数对外层函数(非全局)变量的引用和改变
2. 闭包只存在于内层函数中
3. 函数都要逐层返回,最终返回给最外层函数
1 # 这不是闭包
2 name = "alex"
3 def func():
4 def inner():
5 print(name)
6 return inner
7 func()
8
9 # 这就是标准闭包
10 def func():
11 name = "alex"
12 def inner():
13 print(name)
14 return inner
15 func()
16
17 # 这是闭包
18 def func(n):
19 def inner():
20 print(n)
21 return inner
22 n = "太白"
23 func(n)()
24
25 def func(n):
26 n = name
27 def inner():
28 print(n)
29 return inner
30 name = "太白"
31 func(name)()
32 # 这里和上面的一个意思
33
34 # 判断是不是闭包的方法
35 def func():
36 name = "alex"
36 n = 12
37 def inner():
38 print(name, n)
39 return inner
40 f = func()
41 # 获取闭包引用的外层变量
42 print(f.__closure__[0].cell_contents) # 注意要想判断是不是闭包,必须判断内层函数名的 __closure__ 方法,而不是外层函数名,这里 f=func()即是 inner 这个内层函数名
43 print(f.__closure__[1].cell_contents)
44 # 这里发现,就算先去掉第二个 print 的内容,还是先打印出 12,再打印出 alex
44 print(f.__closure__) 其实原因在 __closure__ 这个方法里,比如再加个 n1 = 0.02, 再 print(f.__closure__)
44 # (<cell at 0x00000264283E6618: int object at 0x0000000056526C60>,
44 # <cell at 0x00000264283E6648: float object at 0x00000264283011C8>, <cell at 0x00000264283E6678: str object at 0x000002642A018030>)
44 # 这里发现它内部就已经有打印数据类型的顺序了
44
45 name = "alex"
46 def func():
47 def inner():
48 print(name)
49 return inner
50 f = func()
51 print(f.__closure__[0].cell_contents) # 报错,说明不是闭包
1 # 闭包的作用:
2 def func(step):
3 num = 1
4 num += step
5 print(num)
6 func(3) # 4
7
8 # 临时名称空间
9 j = 0
10 while j < 5:
11 func(3)
12 j += 1
13 # 4 # 这一步函数执行完后,函数内变量与值的临时空间消失
14 # 4
15 # 4
16 # 4
17 # 4
18
19 # 闭包:解释器在执行程序时,如果遇到函数,随着函数的结束而关闭临时名称空间
20 # 但是遇到闭包,有一个机制:闭包的空间不会随着函数的结束而关闭
21 def wrapper(step):
22 num = 1
23 def inner():
24 # num += step
25 # 单是上面这步是内层函数对外层函数的引用和修改,会出错
26 nonlocal num
27 num += step
28 print(num)
29 return inner
30 f = wrapper(3)
31 j = 0
32 while j < 5:
33 f() # inner()
34 j += 1
35
36 # 4
37 # 7
38 # 10
39 # 13
40 # 16
41 # 因为闭包的机制,num这个变量在函数第一次执行结束后不会马上消失,会在内层中保存一段时间,因为在下一个循环时,它会保存之前num对应的值
42
43 def wrapper(step):
44 num = 1 # 这个外层函数也不会关闭,因为只要内层函数有闭包不会关闭
45 def inner():
46 nonlocal num
47 num += step
48 print(num)
49 return inner # 返回函数的变量名
50 # return inner() 一般不这样写
51 f = wrapper(3)
52 j = 0
53 while j < 5:
54 f() # inner()
55 # 如果这里改成wrapper(3)()
56 # 那就相当于在内层中开辟了5个空间,每个空间调用 wrapper(3)
57 # 因此打印结果是 4 4 4 4 4
58 j += 1
1 # 闭包的应用
2 # 1. 装饰器(它的本质是利用了闭包的原理)
3 # 2. 爬虫
4
5 from urllib.request import urlopen
6
7 content = urlopen("http://www.cnblogs.com/jin-xin/articles/8259929.html")
8 print(id(content)) # 2630731144832
9 # print(content.decode("utf-8"))
10 content = urlopen("http://www.cnblogs.com/jin-xin/articles/8259929.html")
11 print(id(content)) # 2630731219576
12 # 如果不用闭包,爬一次就得创建一个空间,创建多次就得在内层中创建多个空间
13 # 因此要使用闭包形式,爬一次在内层中创建一个空间后,再爬一次还是在同一个内层地址
14 # 如下所示
15
16 from urllib.request import urlopen
17
18 def index():
19 url = "http://www.cnblogs.com/jin-xin/articles/8259929.html"
20 def get():
21 return urlopen(url).read()
22 return get
23
24 taibai = index()
25 content = taibai()
26 # content = index()() 和上面两步一样
27 print(id(content)) # 2550328055264
28 print(id(content)) # 2550328055264
29 print(content.decode("utf-8"))
30
31 # 闭包就是在内层中开一个空间,常贮存一些内容,以便后续程序调用
1 # 迭代器
2
3 # iterable: 可迭代对象——只要内部含有__iter__方法的就是可迭代对象, 它遵循可迭代协议
4 # str list tuple dict range() 文件句柄
5
6 s1 = "abc"
7 print(dic(s1)) # 打印结果中含有__iter__方法
8
9 for i in "abc":
10 print(i) # 可执行
11 for i in 123:
12 print(i) # 报错
13
14 # 判断一个对象是否是可迭代对象的方法
15 # 第一种
16 s1 = "abc"
17 print("__iter__" in dir(s1)) # True
18 print("__iter__" in dir(range(10))) # True
19
20 # 第二种
21 from collections import Iterable
22 from collections import Iterator
23
24 s1 = "abcd"
25 obj = iter(s1)
26 print(obj)
27
28 print(isinstance(obj, Iterator)) # True
29 print(isinstance(obj, Iterable)) # True
30
31 print(isinstance(s1.Iterator)) # False
32 print(isinstance(s1.Iterable)) # False
1 # 可迭代对象不能直接取值,除了索引和切片
2 # 比如for...in...是将可迭代对象先转换成迭代器,再从迭代器里面通过next()方法取值的
3 # 因此迭代器的含义:内部含有"__iter__"并且含有"__next__"方法的就是迭代器,遵循迭代器协议
4
5 # 可迭代对象转换成迭代器的两种方法
6 # 可迭代对象.__iter__() 或者iter(可迭代对象)
7 s1 = "abcd"
8 obj = s1.__iter__()
9 print(obj) # <str_iterator object at 0x000001828C618400>
10 print(obj.__next__()) # a
11 print(obj.__next__()) # b
12 print(obj.__next__()) # c
13 print(obj.__next__()) # d
14 print(obj.__next__()) # 报错——StopIteration,因为一个next对应一个值,多一个就会报错
15
16 # 可迭代对象转换成迭代器
17 # 可迭代对象.__iter__() 或者iter(可迭代对象)
18 s1 = "abcd"
19 obj = iter(s1)
20 print(obj) # <str_iterator object at 0x0000010F49D58048>
21 print("__iter__" in dir(obj) and "__next__" in dir(obj)) # True
22
23
24 # 判断文件句柄是否是可迭代对象,是否是迭代器
25 from collections import Iterable
26 from collections import Iterator
27
28 f = open("s", encoding="utf-8", mode="w")
29 print(isinstance(f,Iterator))
30
31 # s2 = [1, 2, 3]
32 # 将s2转换成迭代器进行取值
33
34 # 第一种
35 s2 = [1, 2, 3]
36 obj = s2.__iter__()
37 obj2 = iter(s2)
38 # print(obj2.__next__())
39 print(next(obj2))
40
41 # 第二种
42 s2 = [1, 2, 3]
43 obj = iter(s2)
44 # print(obj2.__next__())
45 print(next(obj2))
1 # isinstance() 和 type() 的区别
2
3 s1 = "abcd"
4 print(isinstance(s1, str))
5 print(type(s1))
6
7 # True
8 # <class 'str'>
9
10 # type() 只是判断该对象的数据类型
11 # 而 isinstance() 不仅可以判断该对象的数据类型,还可以判断它的其他类型,比如它是否是迭代器之类的
1 # 迭代器的作用:
2
3 # 1. 节省内存
4 # 比如有1000个元素的列表,在内存中开辟1000个内存地址,而迭代器中的元素不管有多少,在内存中只占一个位置
5
6 # 2. 惰性机制
7 # next()就取一个值,不next()就不取值
8
9 # 3. 一条路走到黑,不走回头路
10 s2 = [1, 2, 3, 4, 5]
11 obj2 = iter(s2) # 迭代器
12 print(next(obj2)) # 前面的不能再取,只能运行完又从头开始运行
13
14 # 里面元素少的时候使用列表
15 # 元素很多的时候使用迭代器
16 # 比如上面提到的文件句柄,就是迭代器,文件里面有很多内容都可以很快打开读取
1 # while循环模拟for循环机制(很重要!)
2 l1 = [i for i in range(20)]
3 obj = iter(l1)
4 while 1:
5 print(next(obj)) # 会取出但是会报错,因为next()方法,不走回头路
6
7 # 因此可以使用异常处理
8 l1 = [i for i in range(20)]
9 obj = iter(l1)
10 while 1:
11 try:
12 print(next(obj))
13 except StopIteration:
14 break
15 # 这样程序就能正常运行