python3知识点杂记(三)

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中,装饰器常用于有切面需求的场景,装饰器可以在不改变源码的前提下,增加额外的业务功能,可以抽离出大量函数中与函数功能本身无关的雷同代码,并继续重用。具体细节可参考我的博客

未完待续…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值