21、字典推导式
dict_ = {key: value for (key, value) in [("zhangsan", 18), ("lisi", 20)]}
dict_
{'lisi': 20, 'zhangsan': 18}
我们见过列表推导式,现在又了解到了字典推导式,它们的格式都是**[x for x in xx]** 或 {x for x in xx} ,但是,不要以为(x for x in xx)就是元组推导式了,其实这是一个生成器, 下面会讲!!
22、字符串格式化
- % 与 .format的使用
s = "My name is %s, I am %d years old." % ("zhangsan", 18)
print(s)
s1 = "My name is {}, I am {} years old.".format("zhangsan", 18) # 默认是按顺序对应匹配,你也可以加编号
# s1 = "My name is {0}, I am {1} years old.".format("zhangsan", 18)
print(s1)
My name is zhangsan, I am 18 years old.
My name is zhangsan, I am 18 years old.
使用%进行字符串格式化的话,格式为 “xxx %d xxx %s…” % (arg1, arg2…),但是,如果我的arg就是元组的形式,且只有一个,如下例:
name = (1, 2, 3)
# s2 = "My name is %s" % name # 会抛出异常 not all arguments converted during string formatting
# 为了保证它总是正确的,你必须这样做:
s2 = "My name is %s" % (name, )
print(s2)
# 使用format就不会出现这样的情况:
s3 = "My name is {}".format(name)
print(s3)
My name is (1, 2, 3)
My name is (1, 2, 3)
23、迭代器
迭代 是访问元素的一种方式,迭代器 则是一个可以记住遍历位置的对象,它从集合的第一个元素开始访问,直到所有元素都访问完结束。
- 1、可迭代对象:简单来说,可以通过for … in … 这类语句迭代读取一条数据供我们使用的对象,称为可迭代对象(iterable)。本质上:可迭代对象可以向我们提供一个中间人,即迭代器,帮助我们进行迭代遍历使用。可迭代对象通过__iter__方法为我们提供一个迭代器,当我们遍历一个可迭代对象时,实际上就是先获取这个对象提供的一个迭代器,然后通过这个迭代器一次访问对象中的每一个元素。也就是说:一个具备了__iter__方法的对象,就是一个可迭代对象。
from collections import Iterable
print("判断一个对象是不是可迭代对象:")
print("[]是不是可迭代对象? ", isinstance([], Iterable))
print("'abcd'是不是可迭代对象? ", isinstance("abcd", Iterable))
print("100是不是可迭代对象? ", isinstance(100, Iterable))
判断一个对象是不是可迭代对象:
[]是不是可迭代对象? True
'abcd'是不是可迭代对象? True
100是不是可迭代对象? False
class A():
def __iter__(self): # 一个对象实现了__iter__方法,即是可迭代对象
pass
a = A()
print("a是不是可迭代对象? ", isinstance(a, Iterable))
a是不是可迭代对象? True
iter()和next()函数: 像list、tuple等可迭代对象,均可以通过iter()获取它们的迭代器,然后对获取到的迭代器不断的next()获取下一条数据。注:iter()函数实际上就是调用了可迭代对象的__iter__方法。
a = [1, 2, 3]
iter_ = iter(a)
print(iter_) # list的迭代器
print(next(iter_)) # 对迭代器进行next获取下一条数据
print(next(iter_))
print(next(iter_))
print(next(iter_)) # 当迭代完最后一个数据之后,再次调用next()函数时会抛出一个StopIteration的异常提醒我们所有数据迭代完了。
<list_iterator object at 0x00000235FE404CC0>
1
2
3
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-6-cf53863bae90> in <module>()
5 print(next(iter_))
6 print(next(iter_))
----> 7 print(next(iter_)) # 当迭代完最后一个数据之后,再次调用next()函数时会抛出一个StopIteration的异常提醒我们所有数据迭代完了。
StopIteration:
# 判断一个对象是不是一个迭代器
from collections import Iterator
print("[]是迭代器吗? ", isinstance([], Iterator))
print("iter([])是迭代器吗? ", isinstance(iter([]), Iterator))
[]是迭代器吗? False
iter([])是迭代器吗? True
通过上面的分析,我们知道:迭代器是用来帮助我们记录每一次迭代访问到的位置,当我们使用next()方法时,迭代器会向我们返回它所记录位置的下一个位置。 实际上,我们使用next()方法时,就是调用迭代器对象的__next__方法,所以我们想要构造一个迭代器,就要实现它的__next__方法。python要求迭代器本身也是可迭代的,所以我们为迭代器实现__iter__方法,而__iter__方法要返回一个迭代器,迭代器自身是一个迭代器,所以返回自身就可以了。总结:一个实现了__iter__方法和__next__方法的对象,就是迭代器。
from collections import Iterable, Iterator
class Mylist():
def __iter__(self):
return self
def __next__(self):
pass
mylist = Mylist()
print("mylist是可迭代对象吗? ",isinstance(mylist, Iterable))
print("mylist是迭代器吗? ",isinstance(mylist, Iterator))
mylist是可迭代对象吗? True
mylist是迭代器吗? True
class Mylist():
"""自定义一个可迭代对象"""
def __init__(self):
self.items = []
def add(self, val):
self.items.append(val)
def __iter__(self): # 实现__iter__方法,返回一个迭代器
mylist_iterator = Mylist_iterator(self) # 创建一个迭代器对象,传入的参数是可迭代对象的实例对象self
print("我在Mylist()的__iter__(self)方法这......")
return mylist_iterator # 返回这个迭代器
class Mylist_iterator():
"""自定义一个供上面可迭代对象使用的迭代器"""
def __init__(self, mylist):
self.mylist = mylist
self.cur = 0 # 记录当前位置
def __next__(self):
print("我在Mylist_iterator()的__next__(self)方法这......")
if self.cur < len(self.mylist.items):
item = self.mylist.items[self.cur]
self.cur += 1
return item
else:
raise StopIteration
def __iter__(self): # 返回迭代器本身
return self
mylist = Mylist()
mylist.add(0)
mylist.add(1)
mylist.add(2)
mylist.add(3)
mylist.add(4)
for i in mylist:
print(i)
我在Mylist()的__iter__(self)方法这......
我在Mylist_iterator()的__next__(self)方法这......
0
我在Mylist_iterator()的__next__(self)方法这......
1
我在Mylist_iterator()的__next__(self)方法这......
2
我在Mylist_iterator()的__next__(self)方法这......
3
我在Mylist_iterator()的__next__(self)方法这......
4
我在Mylist_iterator()的__next__(self)方法这......
for item in xx_obj:运行的本质:
- 1、先判断xx_obj是不是一个可迭代对象,即判断这个对象里面有没有__iter__方法
- 2、在上面条件成立的情况下,调用iter()方法,得到xx_obj的__iter__方法的返回值,即一个迭代器
- 3、迭代器不断调用next()方法,即迭代器里的__next__方法,获取下一个值给item,直到抛出StopIteration异常,结束。
上面的方法可以集成为一个
class Mylist():
"""自定义一个可迭代对象,也是一个迭代器"""
def __init__(self):
self.items = []
self.cur = 0
def add(self, val):
self.items.append(val)
def __iter__(self):
return self
def __next__(self):
if self.cur < len(self.items):
item = self.items[self.cur]
self.cur += 1
return item
else:
raise StopIteration
mylist = Mylist()
mylist.add(0)
mylist.add(1)
mylist.add(2)
mylist.add(3)
mylist.add(4)
for i in mylist:
print(i, end=",")
0,1,2,3,4,
迭代器的应用: 实现一个斐波那契数列:1, 1, 2, 3, 5, 8, …
class Fib():
def __init__(self, n):
self.n = n
self.a = 0
self.b = 1
self.cur = 0
def __iter__(self):
return self
def __next__(self):
if self.cur < self.n:
self.a, self.b = self.b, self.a + self.b
self.cur += 1
return self.a
else:
raise StopIteration
for i in Fib(5):
print(i, end=" ")
1 1 2 3 5
此外:并不是只有for循环可以接受可迭代对象,list、tuple等也能接受可迭代对象
print(list(Fib(5)), tuple(Fib(5)))
[1, 1, 2, 3, 5] (1, 1, 2, 3, 5)
24、生成器
利用迭代器,我们可以在每次迭代获取数据(通过next()方法)时,按照特定的规律进行生成,但是我们需要自己手动记录当前的状态,如上例的self.cur,然后根据当前状态生成下一个数据。
为了达到记录当前状态并配合next()函数进行迭代使用,可以采用一种更加简洁的方式:生成器。生成器是一种特殊的迭代器。
1、创建生成器的方法(一)
L = [i for i in range(5)] # 列表推导式
print(L)
G = (i for i in range(5)) # 注意,不是元组推导式,这是生成器
print(G)
[0, 1, 2, 3, 4]
<generator object <genexpr> at 0x00000235FE47FF68>
使用next()获取下一个值,或者使用for循环、list等方法遍历。
G = (i for i in range(5))
print(next(G))
0
G = (i for i in range(5))
print(list(G))
[0, 1, 2, 3, 4]
G = (i for i in range(5))
for i in G:
print(i, end=" ")
0 1 2 3 4
2、创建生成器的方法(二):yield
简单的说:一个函数中定义了yield关键字,定义的函数不在是函数,而是一个生成器。
def f():
yield
print(f())
<generator object f at 0x00000235FE3E39E8>
# 使用yield实现斐波那契数列
def fib(n):
a = 0
b = 1
cur = 0
while cur < n:
a, b = b, a + b
cur += 1
yield a
return "ok"
F = fib(5)
print(next(F))
print(next(F))
print(next(F))
print(next(F))
print(next(F))
print(next(F)) # 当迭代完最后一个数据之后,再次调用next()函数时会抛出一个StopIteration的异常提醒我们所有数据迭代完了。
1
1
2
3
5
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-18-1cee030fd9a7> in <module>()
17 print(next(F))
18 print(next(F))
---> 19 print(next(F)) # 当迭代完最后一个数据之后,再次调用next()函数时会抛出一个StopIteration的异常提醒我们所有数据迭代完了。
StopIteration: ok
发现:return值拿不到,这个值在StopIteration异常里
def fib(n):
a = 0
b = 1
cur = 0
while cur < n:
a, b = b, a + b
cur += 1
yield a
return "ok"
F = fib(5)
try:
while True:
print(next(F))
except StopIteration as e:
print("return值:", e.value)
1
1
2
3
5
return值: ok
小结:
- 1、使用yield关键字的函数不再是函数,而是生成器
- 2、yield关键字的作用:
- 保存当前运行状态(断点,有点像单片机里的中断),然后挂起生成器
- 将yield关键字后面的表达式的值作为返回值返回,作用相当于return
- 3、当再次唤醒生成器时,会从断点处继续执行
- 4、python3中可以使用return返回最终的运行返回结果,但是这个值是保存在StopIteration异常里,而python2中不允许使用return返回一个返回值,最允许使用return从生成器中退出,不能加任何表达式。
除了使用next()唤醒生成器之外,还有一个send()方法,此外使用send的另一个优点是可以在唤醒的同时附带一个数据
def generate():
i = 0
while i < 5:
xx = yield i
print(xx)
i += 1
g = generate()
next(g) # 第一次,唤醒生成器,执行到xx = yield i,返回一个i(i=0)后退出,等待下一次唤醒
next(g) # 第二次唤醒,从上一个断点处继续往下执行,然后再次执行xx = yield i,返回,这里使用next()唤醒,xx没有接收参数,默认为None
next(g)
None
None
2
def generate():
i = 0
while i < 5:
xx = yield i
print(xx)
i += 1
g = generate()
g.send(None) # <==> next(g) 第一次启动
g.send("lalala")
next(g) # <==>g.__next__()不常用
lalala
None
2
生成器是一种一边循环一遍计算的机制,他不会一次性全部计算出来,大量地节省了内存空间
25、面向切面编程AOP
AOP:在软件系统的实现过程中,在很多模块操作中都会用到一些相同的固定的逻辑操作,如:用户验证,面向切面编程就是将公共的逻辑和系统本身核心业务逻辑分离开,集中管理。一方面减轻系统本身的业务逻辑,另一方面降低了耦合,提高了可重用性,便于后期扩展维护。
在python中,装饰器常用于有切面需求的场景,装饰器可以在不改变源码的前提下,增加额外的业务功能,可以抽离出大量函数中与函数功能本身无关的雷同代码,并继续重用。具体细节可参考我的博客。