python函数名使用及迭代器

函数名可以进行变量赋值

 1 def fn():
 2     print('我是fn')
 3 fn()
 4 print(fn)
 5 gn = fn
 6 gn()
 7 print(gn)
 8 结果
 9 我是fn
10 <function fn at 0x0000027508E81E18>
11 我是fn
12 <function fn at 0x0000027508E81E18>

函数名可以作为列表的元素进行使用

 1 def func1():
 2     print('我是func1')
 3 def func2():
 4     print('我是func2')
 5 def func3():
 6     print('我是func3')
 7 lst = [func1,func2,func3]
 8 print(lst)
 9 lst[0]()    #根据列表索引取到函数,执行函数
10 for el in lst:
11     el()    #拿到函数,执行函数
12 结果
13 [<function func1 at 0x00000214D5371E18>, <function func2 at 0x00000214D55498C8>, <function func3 at 0x00000214D5549950>]
14 我是func1
15 我是func1
16 我是func2
17 我是func3

函数名可以作为return返回值

 1 def wrapper():
 2     def inner():
 3         print('我是inner')
 4     print(inner)    #打印函数内存地址
 5     inner()         #执行inner函数
 6     return inner    #返回inner函数内存地址
 7 ret = wrapper()     #ret接收return返回的inner函数内存地址
 8 print(ret)          #打印赋值给ret接收过来的inner函数内存地址
 9 ret()               #执行ret函数
10 结果
11 <function wrapper.<locals>.inner at 0x0000014F387998C8>
12 我是inner
13 <function wrapper.<locals>.inner at 0x0000014F387998C8>
14 我是inner
15 
16 事例2
17 def wrapper():
18     def inner():
19         print('我是inner')
20     return inner        #相当于返回inner函数的内存地址
21 ret = wrapper()         #这里接收的是inner内存地址
22 ret()                   #这里相当于可以多次执行inner函数
23 ret()
24 结果
25 我是inner
26 我是inner
27 对比
28 def wrapper():
29     def inner():
30         print('我是inner')
31     return inner()  #如果这里是return inner()相当于在函数内部执行了inner函数
32 ret = wrapper()     #这里接收的是return返回值
33 ret                 #其返函数的回值只能返回一次
34 ret
35 结果
36 我是inner

函数名可以作为参数进行传递

 1 def func1():
 2     print('我是func1')
 3 def func2():
 4     print('我是func2')
 5 def func3():
 6     print('我是func3')
 7 def proxy(a):       #装饰器的雏形
 8     print('test')
 9     a()
10     print('执行完毕')
11 proxy(func1)
12 proxy(func2)
13 proxy(func3)
14 结果
15 test
16 我是func1
17 执行完毕
18 test
19 我是func2
20 执行完毕
21 test
22 我是func3
23 执行完毕

总结
第一类对象-> 函数名 -> 变量名
函数对象对象可以像变量一样进行赋值
可以作为列表的元素进行使用
可以作为返回值返回
可以作为参数进行传递


闭包的定义:内部函数使用了外部函数的变量(而非全局变量),外部函数又把内部函数当做返回值返回,内部函数就是一个闭包
闭包使用场景:函数的嵌套
闭包的优点:
1, 可以保护变量不被其他人侵害
2, 保持一个变量常驻内存

 1 def wrapper():
 2     a = '哈哈'        #是一种不安全的写法
 3     name = 'jack'
 4     def inner():
 5         print(name) #在内层函数中使用了外层函数的局部变量
 6         print(a)
 7     def func():
 8         nonlocal a
 9         a = 108
10         print(a)
11     return inner   #返回inner函数内存地址
12 ret = wrapper()
13 ret()
14 结果
15 jack
16 哈哈
17 
18 事例2
19 def wrapper():
20     name = 'jack'   #局部变量常驻内存
21     def inner():
22         print(name) #在内层函数中使用了外层函数的局部变量
23     return inner    #返回inner函数内存地址
24 ret = wrapper()
25 ret()               #ret是内层函数inner
26 结果
27 jack
28 
29 事例3
30 def line_config(content,length):
31     def line():
32         print('-'*(length // 2) + content + '-'*(length // 2))
33     return line
34 line1 = line_config('闭包',10)
35 line1()
36 line1()
37 line2 = line_config('闭包',20)
38 line2()
39 结果
40 -----闭包-----
41 -----闭包-----
42 ----------闭包----------
43 
44 事例4
45 def test():
46     funcs = []
47     for i in range(1,4):
48         def test2():
49             print(i)
50         funcs.append(test2)
51     return funcs
52 new_func = test()
53 print(new_func)
54 new_func[0]()
55 new_func[1]()
56 new_func[2]()
57 结果
58 [<function test.<locals>.test2 at 0x000001EE87C298C8>, 
59 <function test.<locals>.test2 at 0x000001EE87C29950>, 
60 <function test.<locals>.test2 at 0x000001EE87C299D8>]
61 3
62 3
63 3
64 
65 事例5
66 def test():
67     funcs = []
68     for i in range(1,4):
69         def test2(num):
70             # num = 1
71             def inner():
72                 print(num)
73             return inner
74         funcs.append(test2(i))
75     return funcs
76 new_func = test()
77 print(new_func)
78 new_func[0]()
79 new_func[1]()
80 new_func[2]()
81 结果
82 [<function test.<locals>.test2.<locals>.inner at 0x000002BEAA039950>, 
83 <function test.<locals>.test2.<locals>.inner at 0x000002BEAA0398C8>, 
84 <function test.<locals>.test2.<locals>.inner at 0x000002BEAA0399D8>]
85 1
86 2
87 3

#如果全局没有设置变量a,但函数内部使用了一个global,就等于在全局定义了一个变量a,因为使用了global,所以函数内部修改此变量值,全局生效

1 def func():
2     global a
3     a = 20
4     print(a)
5 func()
6 print(a)
7 结果
8 20
9 20

闭包嵌套

 1 def f1():                       #1  #3
 2     num = 1000                  #4
 3     def f2():                   #5  #9
 4         name = 'bob'            #10
 5         def f3():               #11 #15
 6             print(name,num)     #16
 7         return f3               #12
 8     return f2                   #6  
 9 f = f1()                        #2  #7
10 i = f()                         #8  #13
11 i()                             #14 #17
12 结果
13 bob 1000

案例:超级简易版爬虫

 1 from urllib.request import urlopen
 2 # import ssl
 3 # ssl._create_default_https_context = ssl._create_unverified_context()
 4 def func():
 5     conntent = urlopen('https://www.dytt8.net/').read()
 6     def inner():
 7         return conntent.decode('gbk')
 8     return inner
 9 print('开始网络请求')
10 ret = func()
11 print('网络请求完毕')
12 print('第一次',ret()[:5])
13 print('第二次',ret()[:5])
14 
15 案例二
16 def index():
17     url = "http://www.baidu.com/index.html"
18     def get():
19         return urlopen(url).read()
20     return get
21 xiaohua = index()
22 content = xiaohua()
23 print(content)

__closure__查看是否是闭包,有内容就是闭包,没有内容就不是闭包

 1 def wrapper():
 2     name = 'bob'
 3     def inner():
 4         print('测试')
 5     print(inner.__closure__)
 6     inner()
 7 wrapper()
 8 结果
 9 None
10 测试
11 
12 事例2
13 def wrapper():
14     name = 'bob'
15     def inner():
16         print(name)
17         print('测试')
18     print(inner.__closure__)
19     inner()
20 wrapper()
21 结果
22 (<cell at 0x0000025A45EB75B8: str object at 0x0000025A45F47180>,)
23 bob
24 测试

dir() 可以帮我们查看指定数据能够执行的操作

1 print(dir(str)) # 有__iter__
2 print(dir(int)) # 没有__iter__
3 print(dir(list)) # 有__iter__
4 print(dir(dict)) # 有__iter__
5 print(dir(bool)) # 没有__iter__
6 共性:所有带有__iter__的东西都可以进行for循环,带有__iter__的东西就是可迭代对象

因为数字是不可迭代的,所以for循环报错

1 for i in 123:
2     print(i)
3 结果报错
4 TypeError: 'int' object is not iterable

迭代器
1. 只能向下执行,不能反复
2. 结束的时候会给报一个错误 StopIteration
3. 整合所有的数据类型进行遍历(int,bool除外)

 1 lst.__iter__()等于iter()
 2 it.__next__()等于next(it)
 3 
 4 lst = ['jary','bob','jack']
 5 print('__iter__' in dir(lst))   #判断lst里边中是否带有__iter__方法
 6 it = iter(lst)
 7 print(it)       #返回一个迭代器地址<list_iterator object at 0x000002C5429D7400>
 8 print(next(it))
 9 print(next(it))
10 print(next(it))
11 print(next(it)) #因为执行到这一行,已经把列表的元素执行完了,取不到元素了就报错 
12 结果
13 True
14 <list_iterator object at 0x000002C5429D7400>
15 jary
16 bob
17 jack
18 Traceback (most recent call last):
19   File "E:/Py3Practise/day01/test.py", line 10, in <module>
20     print(next(it))
21 StopIteration
22 如果想重复上次的值,需要重新定义迭代器

用迭代器模拟for循环:for el in lst

 1 lst = ['jary','bob','jack']
 2 it = iter(lst)  #获取到迭代器
 3 while 1:
 4     try:
 5         el = next(it)   #拿数据
 6         print(el)
 7     except StopIteration:   #出了错误,意味着数据拿完了
 8         break           结束循环
 9 
10 特征:
11 1. 省内存(生成器)
12 2. 惰性机制
13 3. 只能向前. 不能后退

判断迭代器和可迭代对象的方案

 1 __iter__            可迭代的
 2 __iter__  __next__  迭代器
 3 
 4 官方通过代码判断是否是迭代器
 5 借助于两个模块 Iterator迭代器, Iterable可迭代的
 6 from collections import Iterable,Iterator
 7 lst = [1,2,3]
 8 print(isinstance(lst,Iterable))  #lst是否是Iterable(可迭代的)类型的
 9 print(isinstance(lst,Iterator))  #lst是否是Iterator(迭代器)类型的
10 
11 it = iter(lst)  #迭代器一定可迭代,可迭代的不一定是迭代器
12 print(isinstance(it,Iterable))  #it是否是Iterable(可迭代的)类型的
13 print(isinstance(it,Iterator))  #it是否是Iterator(迭代器)类型的
14 for el in it:
15     print(el)
16 结果
17 True
18 False
19 True
20 True
21 1
22 2



迭代器
看下__iter__方法做了什么事情
    print([1,2].__iter__())
结果返回
    <list_iterator object at 0x000001F7F2F3EA58>
list_iterator(迭代器)

dir([1,2].__iter__()):是列表迭代器中实现的所有方法
dir([1,2]):是列表中实现的所有方法
都是以列表的形式返回给我们的,为了看的更清楚,分别把他们转换成集合,然后取差集
    #print(dir([1,2].__iter__()))
    #print(dir([1,2]))
    print(set(dir([1,2].__iter__()))-set(dir([1,2])))
结果:
    {'__next__', '__setstate__', '__length_hint__'}

    l = [1,2,3,4]
    res1 = l.__iter__()
    res2 = l.__iter__()
    print(res1)
    print(res2)
结果:
    <list_iterator object at 0x000002268CEAEAC8>
    <list_iterator object at 0x000002268CEAEBA8>
# res1和res2都是迭代器,是两个完全不同的迭代器

__length_hint__迭代器中有多少个元素
l = [1,2,3,4]
res = l.__iter__()
print(res.__length_hint__())
for i in res:
    print(i)

控制迭代器从哪儿开始迭代
    l = [1,2,3,4]
    res = l.__iter__()
    res.__setstate__(2)
    for i in res:
        print(i)
结果:
    3
    4


事例二
for循环自带报错异常处理
    l = [1,2,3]
    res1 = l.__iter__()
    res2 = l.__iter__()
    for i in res1:
        print(i)
    print('------------')
    for i in res1:
        print(i)
    print('^^^^^^^^^^^^')
结果:
    1
    2
    3
    ------------
    ^^^^^^^^^^^^
第一个for把迭代器中的值已经取完了,所以第二个for循环没有值

事例三
for i in l.__iter__():
    print(i)
print('**************')
for i in l.__iter__():
    print(i)
结果:
    1
    2
    3
    **************
    1
    2
    3
因为这两个for循环的迭代器都是现用现定义的,并没有赋值给变量,所以两个迭代器各不影响,都能打印出来值

迭代器 - 概念
  可迭代协议 : 内部含有__iter__方法的值/变量都是可迭代的
  怎么通过可迭代变成迭代器 : 可迭代变量.__iter__()返回一个迭代器
  迭代器协议 : 内部含有__iter__方法和__next__方法的值/变量都是迭代器
  迭代器的特点:
      具有next和iter方法
      通过一个next多次执行就可以获得所有这个容器中的值
      迭代器中的值只能取一次
      不取的时候值不出现
 
  可迭代和迭代器的关系:
      所有的迭代器都是可迭代的,反之不成立
      迭代器是特殊的存在
迭代器的作用就是节省内存,for循环就是利用了迭代器节省内存的特点来对python当中的变量来进行操作的

for循环取值
    for循环一个列表的时候必须用的__next__取下一个值
    for循环内部的机制就是迭代器取值的机制
    在for循环执行的过程中 : 先把可迭代的变成一个迭代器,然后再从中一个一个的取值
for循环和迭代器的关系:
    无论是可迭代的还是迭代器都可以被for循环
    如果直接循环迭代器,那么循环一次就没有了
    如果循环的非迭代器,那么每一次循环都相当于从头到尾的循环,是因为在for循环中的所有非迭代器都会通过iter生成一个新的迭代器

    print('__next__' in dir(range(6)))  #查看'__next__'是不是在range()方法执行之后内部是否有__next__
    print('__iter__' in dir(range(6)))  #查看'__next__'是不是在range()方法执行之后内部是否有__next__
结果
    False
    True

    from collections import Iterator
    print(isinstance(range(100000000),Iterator))  #验证range执行之后得到的结果不是一个迭代器
range生成的就是一个迭代器,创建这个迭代器并不会真的把迭代器中的所有数据一次性生成,只有通过next取值的时候才会生成
记住你要多少个值 ,当前该给你什么,并且记住我下一个该给你什么,下一个和当前这个数的关系


# 创建一个迭代器:每次执行iter方法就创建一个迭代器,每个迭代器都只能取值一次,循环这个迭代器
    dic = {'k':'v','k2':'v2'}
    dic_iter = dic.__iter__()
    print(dic_iter)
结果:
    <dict_keyiterator object at 0x000001E3FB0333B8>

    print(dic_iter.__next__())
    for k in dic_iter:
        print(k)
结果:
    k
    k2

事例二:定义一个计数器来循环
count = 0
l_iter = l.__iter__()
while count < len(l):
    print(l_iter.__next__())
    count += 1


判断一个变量/值是否是迭代器的方法
    lst_iterator = [].__iter__()
    print('__iter__' in dir(lst_iterator))
    print('__next__' in dir(lst_iterator))
    print('__iter__' in dir(lst_iterator) and '__next__' in dir(lst_iterator))
结果:
    True
    True
    True

file文件是一个迭代器还是一个可迭代的?
f = open('file')
print('__iter__' in dir(f) and '__next__' in dir(f))    #表示是一个迭代器,因为既有__iter__方法也有__next__方法
结果:
True

range是一个迭代器还是一个可迭代的?
a = range(10)
print('__iter__' in dir(a))     #可迭代的
print('__iter__' in dir(a) and '__next__' in dir(a))    #可迭代的但是并不是迭代器因为没有__next__方法
结果:
True
False


第二种方法
from collections import Iterator
f = open('file')
print(isinstance(f,Iterator))
print(isinstance(range(10),Iterator))
结果:
True
False

 

总结
可迭代对象:
    拥有__iter__方法
    特点:惰性运算
    例如:range(),str,list,tuple,dict,set

迭代器Iterator:
  拥有__iter__方法和__next__方法
  例如:iter(range()),iter(str),iter(list),iter(tuple),iter(dict),iter(set),reversed(list_o),map(func,list_o),filter(func,list_o),file_o

posted @ 2018-11-02 21:59  LinuxCBB  阅读(386)  评论(0)    收藏  举报