函数名的应用
我们都知道,函数名的命名规则与变量基本相同,那么函数名可以做那些事呢?
首先,函数是通过 函数名()调用的,那么如果直接输出函数名会是什么情况呢?
def func():
return 666
print(func())
print(func)
666
<function func at 0x0000021AC539AD90>
可以看到,如果直接输出函数名,那么打印的是函数在内存中的地址,所以函数名的第一个用法是:
函数名表示函数的内存地址
同时,函数名不仅命名方式与变量相同,函数名还可以直接当作变量使用
见以下代码:
def func1():
return 666
c = func
c()
666
不仅如此,函数名还可以作为函数的参数以及返回值
见下:
def func2(a):
print(a)
return a()
def func3():
print(666)
func2(func3)
<function func3 at 0x0000021AC4785158>
666
最后,函数名还可以作为容器类类型的元素
a = 6
b = 4
c = 6
l = [a,c,c]
print(l)
def func4():
print(666)
def func5():
print(777)
def func6():
print(888)
l1 = [func4,func5,func6]
for i in l1:
i()
[6, 6, 6]
666
777
888
以上便是函数名的应用,总结一下,函数名共有以下作用:
- 函数名可以表示函数的内存地址
- 函数名可以作为变量使用
- 函数名可以作为函数的参数和返回值
- 函数名可以作为容器类类型的元素
闭包
什么是函数的闭包?首先是首先让我们看看维基百科中关于闭包的概念:
在一些语言中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。闭包可以用来在一个函数与一组“私有”变量之间创建关联关系。在给定函数被多次调用的过程中,这些私有变量能够保持其持久性
通过这个定义,我们可以得出函数中形成闭包需要满足以下条件:
- 必须要有函数的嵌套
- 内层函数必须要引用外层函数中的变量(不能是全局变量)
- 外层函数必须返回内部函数的引用
def func(): #定义一个外部函数
x = 10
def func7(): #定义一个内部函数
nonlocal x
x += 1 #内部函数中引用外部函数
return x
return func7 #外部函数返回内部函数
f = func() # f = func7
print(f())
print(f())
print(f())
print(x)
11
12
13
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-27-0478bb8088ab> in <module>()
11 print(f())
12 print(f())
---> 13 print(x)
NameError: name 'x' is not defined
那么闭包有什么作用呢?
当程序执行时,遇到了函数执行,他会在内存中开辟一个空间,局部名称空间,如果这个函数内部形成了闭包,那么他就不会随着函数的结束而消失。
something more:closure方法
闭包函数相对于普通函数会多出一个closure属性里面定义了一个元组用于存放所有的cell对象,每个cell对象一一保存了这个闭包中所有的外部变量。
def func8():
pass
print(func8.__closure__)
def func9():
i = 10
j = 'yang'
def func10():
print(i,j)
print(func10.__closure__)
return func10
l = func9()
l()
None
(<cell at 0x0000021AC5447EB8: int object at 0x0000000056A96200>, <cell at 0x0000021AC5447498: str object at 0x0000021AC5471F10>)
10 yang
可迭代对象和迭代器
首先抛出结论:
- 可迭代对象包含迭代器。
- 如果一个对象拥有iter方法,其是可迭代对象;如果一个对象拥有next方法,其是迭代器。
- 定义可迭代对象,必须实现iter方法;定义迭代器,必须实现iter和next方法。
在介绍迭代器之前,我们先来回顾一下之前提到的一个名词:可迭代对象(iterable)
在之前的的介绍中,我们知道字符串、列表、字典等都是可迭代对象,我们可以对可迭代对象进行for循环,如下:
s = '123'
li = [1,2,3]
dic = {1:2,2:3,3:4}
for i in s:
print(i)
for i in li:
print(i)
for i in dic:
print(i)
for i in 123: #报错信息提示:'int' object is not iterable int型不可迭代
print(i)
1
2
3
1
2
3
1
2
3
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-36-9241d29cd0b3> in <module>()
8 for i in dic:
9 print(i)
---> 10 for i in 123:
11 print(i)
TypeError: 'int' object is not iterable
那么,究竟什么是可迭代对象呢?在理解这个概念之前,我们必须先了解一个方法:__iter__
对象内部含有__iter__方法就是可迭代对象.我们通过代码来验证:
a = 123
s = '123'
l = [1,2,3]
dic = {1:2,2:3,3:4}
print('__iter__' in dir(a)) #dir方法可以获得调用对象的所有属性和方法
print('__iter__' in dir(s))
print('__iter__' in dir(l))
print('__iter__' in dir(dic))
False
True
True
True
判断对象是不是可迭代对象的方法除了iter方法外,还有isinstance方法:
from collections import Iterable
print(isinstance(a,Iterable))
print(isinstance(s,Iterable))
print(isinstance(l,Iterable))
print(isinstance(dic,Iterable))
False
True
True
True
那么什么是迭代器呢?在最开始的结论中我们提到,迭代器属于可迭代对象,但比可迭代对象多了一个__next__方法,那么问题来了,what's __next__方法?手动黑人问号脸
最常见的迭代器是文件句柄,见下:
f = open('register', encoding='utf-8')
print('__iter__' in dir(f))
print('__next__' in dir(f))
print('__iter__' in dir(dict))
print('__next__' in dir(dict))
True
True
True
False
那么__next__方法如何使用呢?每next一次,取一次值
print(f.__next__())
print(f.__next__())
yang|123
jin|456
可迭代对象与迭代器的区别:
- 可迭代对象不能取值,迭代器是可以取值的。(不借助索引或key等方法)
- 迭代器非常节省内存。
- 迭代器每次只会取一个值。
- 迭代器单向的,一条路走到头。
可迭代对象是可以通过__iter__()转为迭代器的
lis = [1, 2, 3] # 可迭代对象
ite1 = lis.__iter__() # 迭代器 <list_iterator object at 0x0000027A183BFFD0>
ite1 = iter(lis) # 迭代器 <list_iterator object at 0x0000027A183BFFD0>
print(ite1)
<list_iterator object at 0x0000021AC5499588>
for循环对可迭代对象进行遍历的背后实现原理:
- 将可迭代对象转化成迭代器。
- 调用__next__方法取值。
- 利用异常处理停止报错。
while 1:
try:
print(ite1.__next__())
except StopIteration:
break
1
2
3
网友评论