主要内容:
1、函数名的使用以及第一类对象
2、闭包
3、迭代器
一、函数名的运用
函数名是一个变量,但它是一个特殊的变量,与括号配合可以执行函数的变量。
- 函数名的内存地址
def func():
print(123)
print(func)
结果:
<function func at 0x00000000020ECB70>
- 函数名可以赋值给其他变量
def func():
print(123)
f = func #把函数当作一个变量赋值给另一个变量
f() #函数调用
- 函数可以当作容器类的元素
def func1():
print(123)
def func2():
print(456)
def func3():
print(789)
list = [func1, func2, func3]
for i in list:
i()
- 函数名可以当作函数的参数
def func():
print(123)
def func1(f):
f()
print(456)
func1(func)
- 函数名可以作为函数的返回值
def func():
print(123)
def func1():
print(456)
return func1
f = func()
f()
二、闭包
闭包就是内层函数对外层函数(非全局)变量的引用
def func():
a = 10
def func1():
print(a)
func1()
func() #结果:10
我们可以使用__closure__来检测函数是否是闭包,函数名.__closure__返回cell就是闭包,返回None就不是闭包
def func():
a = 10
def func1():
print(a)
func1()
print(func1.__closure__)
func()
结果:
10
(<cell at 0x00000000004202E8: int object at 0x000000001DFE81E0>,)
- 在函数外边调用内部函数
def func():
a = 10
def func1():
print(a)
return func1
f = func()
f() #调用内部func1函数
- 多层内部函数嵌套的调用
def func():
a = 10
def func1():
def func2():
print(a)
return func2
return func1
func()()() #加一个()就是调用一层函数,一层一层往里取
由它我们可以引出闭包的好处,由于我们在外界可以访问内部函数那这个时候内部函数访问的时间和时机就不一定了, 因为在外部, 我可以选择在任意的时间去访问内部函数,这个时候,前面的博客讲解过, 如果一个函数执行完毕, 则这个函数中的变量以及局部命名空间中的内容都将会被销毁,在闭包中,如果变量被销毁了,那内部函数将不能正常执行,所以,python规定,如果你在内部函数中访问了外层函数中的变量,那么这个变量将不会被销毁。将会常驻在内存中,也就是说,使用闭包, 可以保证外层函数中的变量在内存中常驻。
这样就可以提高我们处理一些耗时过长网络连接操作的效率
from urllib.request import urlopen
def but():
content = urlopen("http://www.python666.cn/index.html").read()
def get_content():
return content
return get_content
fn = but() # 这个时候就开始加载python666的内容
# 后面需要用到这里面的内容就不需要在执行非常耗时的网络连接操作了,因为已经被加载到内存中。
content = fn() # 获取内容
print(content)
content2 = fn() # 重新获取内容
print(content2)
总结:闭包的作用就是让函数的变量能够常驻内存,供后面的程序使用
三、迭代器
- 我们之前一直在用可迭代对象进行迭代操作. 那么到底什么是可迭代对象. 本小节主要讨论可迭代对象. 首先我们先回顾一下目前我们所熟知的可迭代对象有哪些:
- str, list, tuple, dict, set. 那为什么我们可以称他们为可迭代对象呢? 因为他们都遵循了可迭代协议. 什么是可迭代协议. 首先我们先看一段错误代码:
# 对的
s = "abc"
for c in s:
print(c)
# 错的
for i in 123:
print(i)
结果:
Traceback (most recent call last):
File "/Users/sylar/PycharmProjects/oldboy/iterator.py", line 8, in
<module>
for i in 123:
TypeError: 'int' object is not iterable
注意看报错信息中有这样一句话. ‘int’ object is not iterable . 翻译过来就是整数类型对象是不可迭代的.。iterable表示可迭代的. 表示可迭代协议. 那么如何进行验证你的数据类型是否符合可迭代协议. 我们可以通过dir函数来查看类中定义好的所有方法
s = "我的哈哈哈"
print(dir(s)) # 可以打印对象中的方法和函数
print(dir(str)) # 也可以打印类中声明的方法和函数
#发现在字符串中可以找到__iter__. 继续看一下list, tuple, dict, set
print(dir(tuple))
print(dir(list))
print(dir(open("a.txt"))) # 文件对象
print(dir(set))
print(dir(dict))
- 在打印结果中. 寻找__iter__ 如果能找到. 那么这个类的对象就是一个可迭代对象。
- 我们发现这几个可以进行for循环的东西都有__iter__函数, 包括range也有. 可以自己试试
- 我们还可以通过isinstence()函数来查看一个对象是什么类型的
l = [1,2,3]
l_iter = l.__iter__()
from collections import Iterable
from collections import Iterator
print(isinstance(l,Iterable)) #True
print(isinstance(l,Iterator)) #False
print(isinstance(l_iter,Iterator)) #True
print(isinstance(l_iter,Iterable)) #True
- 综上:我们可以确定,如果对象中有__iter__函数,那么我们认为这个对象遵守了可迭代协议,就可以获取到相应的迭代器,这里的__iter__是帮助我们获取到对象的迭代器, 我们使用迭代器中的__next__()来获取到一个迭代器中的元素。
s = '你好啊'
a = s.__iter__() #获取迭代器
print(a.__next__()) #使用迭代器进行迭代,获取第一个元素‘你’
print(a.__next__()) #‘好’
print(a.__next__()) #‘啊’
print(a.__next__()) #StopIteration
- for 循环机制:
for i in [1,2,3]:
print(i)
- 使用while循环+迭代器来模拟for循环
lst = [1, 2, 3]
lst_iter = lst.__iter__()
while True:
try:
next = lst_iter.__next__()
print(next)
except StopIteration:
break
- 总结:
- Iterable:可迭代对象:内部包含__iter__()函数
- ltertor:迭代器:内部包含__iter__()函数的同时包含__next__()
- 迭代器的特点:
- 节省内存
- 惰性机制
- 不能反复,只能向下执行
我们可以把要迭代的内容当成子弹. 然后呢. 获取到迭代器__iter__(), 就把子弹都装在弹夹中. 然后发射就是__next__()把每一个子弹(元素)打出来. 也就是说, for循环的时候. 一开始的时候是__iter__()来获取迭代器. 后面每次获取元素都是通过__next__()来完成的. 当程序遇到StopIteration将结束循环.