(一)生成器
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。这个其实是惰性求值,数据不是全部一下子载入到内存中,而是一个一个来。
g = (i for i in range(10)) print(g) # 把列表推导的圆括号换成方括号就变成了一个生成器 # <generator object <genexpr> at 0x000001AA11DD3D68>
我可以通过 next(g) 方法访问元素,在访问完所有的元素后会抛出一个异常。但是这种使用方法几乎不被推荐。正确的做法是用 yield 编写一个生成器函数,最后返回一个可迭代对象,然后用 for 循环来调用。
1 def my_range(n):
2 i = 0
3 while i != n:
4 i += 1
5 yield i # 协程
6
7
8 r = my_range(10) # 返回一个可迭代对象
9 for i in r:
10 print(i)
yield 这种处理方式其实是协程,这个有点像系统终中断。协程,又称微线程,纤程。英文名Coroutine。协程的概念很早就提出来了,但直到最近几年才在某些语言(如Lua)中得到广泛应用。子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B,B在执行过程中又调用了C,C执行完毕返回,B执行完毕返回,最后是A执行完毕。所以子程序调用是通过栈实现的,一个线程就是执行一个子程序。子程序调用总是一个入口,一次返回,调用顺序是明确的。而协程的调用和子程序不同。协程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。——廖雪峰
(二)重要的魔法方法
'''
__getattribute__ # 可以 hook 住所有的读操作,在访问元素的时候 Foo.name 先调用 __getattribute__
__setattribute__
***
# 什么都没有的时候才会被触发
# 这个操作更加的重要,下面两个函数被触发的时候,一定没有这个属性 missing method
__getattr__
__setattr__
'''
class Foo(object):
def __init__(self, name):
self.id = None
self.name = name
def __getsttribute__(self, item):
print(item)
foo = Foo('Jack')
foo.name
# 输出 Jack
# __getattr__
class Any(object):
def __getattr__(self, item):
print(item)
def __setattr__(self, k, v):
print("set", k, v)
a = Any()
a.a
# 输出 a ,这是一个没有的属性
# __setattr__
a.a = 1
# 输出 set a 1
"""
希望有一个类可以接受任何参数和任何函数
"""
(三)装饰器与闭包
"""
使用装饰器实现一个单例模式
"""
def singleton(cls, *args, **kwargs):
instance = {}
def wrapper(*args, **kwargs):
if cls not in instance:
instance[cls] = cls(*args, **kwargs)
return instance[cls]
return wrapper
@singleton
class Foo(object):
pass
foo1 = Foo()
foo2 = Foo()
print(foo1 is foo2)
# 输出: True
(四) 运算符重载
# __repr__
# __str__的区别:
"""
两个方法同时定义的时,调用 print 函数打印对象的时候,会优先调用 __str__ 方法
__str__是面向用户的,而__repr__面向程序员
重构了 __str__方法,交互式调用显示的式地址,直接 print 的时候是显示重构的内容。
__repr__ 改了的话,交互式和 print 都是统一的
"""
class Com(object):
"""
加减法实现运算符重载
"""
def __init__(self, value):
self.value = value
def __add__(self, other):
return self.value + other
def __sub__(self, other):
return self.value - other
com = Com(5)
print(com + 5)
print(com - 10)
"""
索引的取值和复制 __getitem__, __setitem__
通过这两个方法实现 i[index] 的取值、赋值和切片操作
"""
class Index(object):
data = [8,2,5,5,1,5,3,9,6]
def __getitem__(self, index):
return self.data[index]
def __setitem__(self, k, v):
self.data[k] = v
print(self.data)
i = Index()
print(i[3])
i[0] = 8999
print(i[1:])
"""
__iter__,__next__ 实现迭代器
什么是迭代器? 能够把容器中的元素遍历(for loop)访问的对象被称为迭代器
含有 __iter__() 方法或 __getitem__() 方法的对象称之为可迭代对象。
"""