Python小白到老司机,快跟我上车!基础篇(十四)
Posted coder-pig
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python小白到老司机,快跟我上车!基础篇(十四)相关的知识,希望对你有一定的参考价值。
函数(下)
1、迭代器
迭代器是Python提供的 用于访问集合
,是一种 可以记住遍历位置的对象
,会从第一个元素开始访问,直到结束。可以通过内置的 iter()函数
来获取对应的 迭代器对象
,然后直接循环遍历这个迭代器对象;或者通过另外一个内置的 next()函数
,返回 容器的下一个元素
,不过如果超过结尾会报 StopIteration异常
,使用代码示例如下:
import sys
a = [1, 2, 3, 4, 5]
it1 = iter(a)
# 直接遍历迭代器对象
for x in it1:
print(x, end='\\t')
else:
print()
# 每调用一次next向后访问一个元素,超过元素会报StopIteration异常
it2 = iter(a)
print(next(it2), end='\\t')
print(next(it2), end='\\t')
print(next(it2), end='\\t')
print(next(it2), end='\\t')
print(next(it2), end='\\t')
print()
# 带异常捕获方法
it3 = iter(a)
while True:
try:
print(next(it3), end='\\t')
except StopIteration:
sys.exit()
运行结果如下:
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
另外有一点要进行区分:iterable
和 iterator
,前者只实现了 iter函数
,而后者同时实现了 iter和next函数
。比如List只是一个iterable而不是iterator,所以只支持iter(),不支持next(),需要调用iter()函数获得一个iterator对象。如果调用下type(it1),会发现数据类型是: <class 'list_iterator'>
,这是Python为list提供的默认迭代器对象。我们也可以自己来实现一个iterator,代码示例如下:
class MyIterator():
def __init__(self, names):
self.names = names
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.names):
raise StopIteration("已到达末尾")
else:
self.index += 1
return self.names[self.index - 1]
iterator = MyIterator([1, 2, 3, 4, 5])
for i in iterator:
print(i, end='\\t')
运行结果如下:
1 2 3 4 5
2、生成器
叫「生成器函数」会更贴切一些,一种特别的函数,用 yield关键字
来返回一个生成器对象,本质上还是迭代器,只是更加简洁,yield对应的值在函数调用时候不会立即返回,只有去调用 next()函数
的时候才会返回,而使用for xxx in xxx的时候其实调用的还是 next()函数
,最简单的生成器代码示例如下:
def func(n):
yield n * n
if __name__ == '__main__':
print(func(10))
print(next(func(10)))
for i in func(10):
print(i)
运行结果如下:
<generator object func at 0x0000015D06436308>
100
100
可以看到返回的对象类型是 generator
,相比起迭代器,生成器显得更加优雅简洁,比如最经典的实现斐波那契数列的例子:
def func(n):
a, b = 0, 1
while n > 0:
n -= 1
yield b
a, b = b, a + b
for i in func(10):
print(i, end="\\t")
运行结果如下:
1 1 2 3 5 8 13 21 34 55
3、装饰器
本质上也是「函数」,作用是:帮助其他函数在不改动代码的情况下增加额外的功能,装饰器函数的返回值也是一个函数。
① 一步步了解装饰器
装饰器对于很多初学者来说有点难以理解,举个简单的奶茶例子慢慢引入吧(因为手边刚好有一杯一点点奶茶,另外函数名不要用中文,这里用中文只是想让读者更容易理解,还有命名习惯:类名驼峰命名,函数名下划线分割命名!)
import time
def 波霸奶茶():
time.sleep(1)
print("制作一杯波霸奶茶")
if __name__ == '__main__':
波霸奶茶()
运行结果如下:
制作一杯波霸奶茶
现在笔者想知道制作一杯波霸奶茶花费的时间,可以改动下波霸奶茶这个函数:
def 波霸奶茶():
start = time.clock()
time.sleep(2)
print("制作一杯波霸奶茶")
end = time.clock()
print("耗时", end - start)
if __name__ == '__main__':
波霸奶茶()
运行结果如下:
制作一杯波霸奶茶
耗时 1.004335880279541
这个时候问题来了,除了波霸奶茶还有四季奶绿这款奶茶:
def 四季奶绿():
time.sleep(2)
print("制作一杯四季奶绿")
而我也想知道四季奶绿的制作时间,需要把统计时间那部分代码粘贴复制一下,有点繁琐,如果还有一款格雷三兄弟,也是要复制下,好繁琐啊。有没有什么方法能简化这个过程呢?答案肯定是有的:「Python支持函数作为参数
」,我们可以把计时部分剥离成一个单独的函数,然后把需要查看制作时间的奶茶函数传入即可,所以有了下面这样的代码:
def 波霸奶茶():
time.sleep(1)
print("制作一杯波霸奶茶")
def 四季奶绿():
time.sleep(2)
print("制作一杯四季奶绿")
def 计时(func):
start = time.time()
func()
end = time.time()
print("耗时", end - start)
if __name__ == '__main__':
计时(波霸奶茶)
计时(四季奶绿)
运行结果如下:
制作一杯波霸奶茶
耗时 1.0002198219299316
制作一杯四季奶绿
耗时 2.0016701221466064
可以,这个时候问题又来了,上面的代码意味着:所有调用到制作奶茶方法的地方都要改成计时(奶茶),如果奶茶有N多种的话,每个调用制作奶茶的函数都要改成这样的形式,有没有更简单的方法啊?答案肯定是有的,Python支持返回一个函数。我们要做的是通过一个内嵌的包装函数,为传入的函数附加功能,然后返回这个函数,具体的代码如下:
def 计时(func):
def decorator():
start = time.time()
func()
end = time.time()
print("耗时", end - start)
return decorator
if __name__ == '__main__':
波霸奶茶 = 计时(波霸奶茶)
波霸奶茶()
四季奶绿 = 计时(四季奶绿)
四季奶绿()
运行结果如下:
制作一杯波霸奶茶
耗时 1.0033059120178223
制作一杯四季奶绿
耗时 2.0044848918914795
可以,没毛病,而且只有在我们需要查询某种奶茶的制作时间时才需要这样写,平常的函数还是正常使用。嗯,你以为到这里就完了,对于装饰器函数,Python还为我们提供了一枚语法糖,语法糖可以理解成一种便捷的写法吧,在Python中通过一个@ 就可以简化我们上面的代码,具体代码如下:
def 计时(func):
def decorator():
start = time.time()
func()
end = time.time()
print("耗时", end - start)
return decorator
@计时
def 波霸奶茶():
time.sleep(1)
print("制作一杯波霸奶茶")
@计时
def 四季奶绿():
time.sleep(2)
print("制作一杯四季奶绿")
if __name__ == '__main__':
波霸奶茶()
四季奶绿()
一样的运行结果,更加精简的代码,相信读者都体验到了语法糖的便利,但是要注意一点:大部分的语法糖只是减少我们写繁琐代码的时间,让我们把更多的时间花在逻辑流程上,代码写少了不一定就会带来性能上的提升,底层的代码可能还是那些东西!
② 带参数的装饰器
上面演示的是无参数的装饰器的用法,有时我们可能需要传入一些参数,需要在 原闭包的基础上再加一层闭包
,具体流程与代码示例如下:
import time
def 计时(配料="珍珠"):
def decorator(func):
def inner():
print("加入配料:%s" % 配料 )
start = time.time()
func()
end = time.time()
print("耗时", end - start)
return func
return inner
return decorator
@计时(配料='波霸')
def 波霸奶茶():
time.sleep(1)
print("制作一杯波霸奶茶")
@计时(配料='椰果')
def 四季奶绿():
time.sleep(2)
print("制作一杯四季奶绿")
if __name__ == '__main__':
波霸奶茶()
print()
四季奶绿()
运行结果如下:
加入配料:波霸
制作一杯波霸奶茶
耗时 1.0022242069244385
加入配料:椰果
制作一杯四季奶绿
耗时 2.0034420490264893
③ 多个装饰器的执行顺序
一个函数可以搭配多个修饰器使用,执行的顺序:「从下往上,就近原则,靠近函数定义的先执行
」。验证代码示例如下:
def func_a(func):
def decorator():
func()
print("Call func_a")
return decorator
def func_b(func):
def decorator():
func()
print("Call func_b")
return decorator
def func_c(func):
def decorator():
func()
print("Call func_c")
return decorator
@func_a
@func_b
@func_c
def func_t():
pass
if __name__ == '__main__':
func_t()
运行结果如下:
Call func_c
Call func_b
Call func_a
④ 四种不同类型的装饰器
Python中有四种不同类型的装饰器,分别是:「函数装饰函数」「函数装饰类」「类装饰函数」
「类装饰类」, 和类有关的装饰器可以等后面学完类和对象再回头看,不用死记,用到的时候
照葫芦画瓢改改就好。另外类装饰函数或类主要依赖类的 __call__
函数,当使用@形式将装饰器
附加到函数上时,就会调用该函数。
- 「函数装饰函数」,使用代码示例如下:
def func_func(func):
def decorator(a, b):
print("函数装饰的函数名:", func.__name__)
result = func(a, b)
return result
return decorator
@func_func
def func_add(a, b):
return a + b
if __name__ == '__main__':
print(func_add(1, 2))
运行结果如下:
函数装饰的函数名: func_add
3
- 「函数装饰类」,使用代码示例如下:
def func_class(cls):
def decorator(name):
print("函数装饰的类:", cls.__name__, name)
return cls(name)
return decorator
@func_class
class A:
def __init__(self, n):
self.n = n
def show(self):
print("a = " + self.n)
a = A('123')
a.show()
运行结果如下:
函数装饰的类: A 123
a = 123
- 「类装饰函数」,使用代码示例如下:
class class_func:
def __init__(self, _func):
self._func = _func
def __call__(self, name):
print("类装饰的函数名:", self._func.__name__, name)
return self._func(name)
@class_func
def func(a):
return a
if __name__ == '__main__':
print(func('123'))
运行结果如下:
类装饰的函数名: func 123
123
- 「类装饰类」,使用代码示例如下:
class class_class:
def __init__(self, _cls):
self._cls = _cls
def __call__(self, name):
print("类装饰的类的类名:", self._cls.__name__, name)
return self._cls(name)
@class_class
class A:
def __init__(self, a):
self.a = a
def show(self):
print('self.a = ', self.a)
if __name__ == '__main__':
a = A('123')
a.show()
运行结果如下:
类装饰的类的类名: A 123
self.a = 123
以上是关于Python小白到老司机,快跟我上车!基础篇(十四)的主要内容,如果未能解决你的问题,请参考以下文章