七、生成器
1. 生成器定义
生成器可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其他数据类型需要调用自己的内置iter方法)
在Python中,一边循环,一边计算的机制,称为生成器。
2. 生成器的作用
1. 通过列表生成式,我们可以直接创建一个列表,但是,受到内存限制,列表容量肯定是有限的。
2. 而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
3. 所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?
4. 这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
3. 生成器工作原理
1)生成器是这样一个函数,它记住上一次返回时在函数体中的位置。
2)对生成器函数的第二次(或第 n 次)调用跳转至该函数中间,而上次调用的所有局部变量都保持不变。
3)生成器不仅“记住”了它数据状态;生成器还“记住”了它在流控制构造中的位置。
4)生成器是一个函数,而且函数的参数都会保留。
5)迭代到下一次的调用时,所使用的参数都是第一次所保留下的,即是说,在整个所有函数调用的参数都是第一次所调用时保留的,而不是新创建的
4. yield生成器运行机制
在Python中,yield就是这样的一个生成器。
1) 当你问生成器要一个数时,生成器会执行,直至出现 yield 语句,生成器把yield 的参数给你,之后生成器就不会往下继续运行。
2) 当你问他要下一个数时,他会从上次的状态开始运行,直至出现yield语句,把参数给你,之后停下。如此反复
3) 在python中,当你定义一个函数,使用了yield关键字时,这个函数就是一个生成器
4) 它的执行会和其他普通的函数有很多不同,函数返回的是一个对象,而不是你平常所用return语句那样,能得到结果值。如果想取得值,那得调用next()函数
5) 每当调用一次迭代器的next函数,生成器函数运行到yield之处,返回yield后面的值且在这个地方暂停,所有的状态都会被保持住,直到下次next函数被调用,或者碰到异常循环退出。
# yield实现fib数
def fib(max_num):
a,b = 1,1
while a < max_num:
yield b
a,b=b,a+b
g = fib(10) #生成一个生成器:[1,2, 3, 5, 8, 13]
print(g.__next__()) #第一次调用返回:1
print(list(g)) #把剩下元素变成列表:[2, 3, 5, 8, 13]
八迭代器
1. 迭代器定义
1. 迭代器是访问集合内元素的方式,迭代器对象从集合的第一个元素开始访问,直到所有的元素都被访问一遍后结束
2. 迭代器仅是一容器对象,它有两个基本方法
1)next方法:返回容器的下一个元素
2)__iter__方法:返回迭代器自身
a = iter([1,2,]) #生成一个迭代器
print(a.__next__())
print(a.__next__())
print(a.__next__()) #在这一步会引发 “StopIteration” 的异常
2. 生成器和迭代器之间的区别
在使用生成器时,我们创建一个函数;在使用迭代器时,我们使用内置函数iter()和next()。 在生成器中,我们使用关键字‘yield’来每次生成/返回一个对象。 生成器中有多少‘yield’语句,你可以自定义。 每次‘yield’暂停循环时,生成器会保存本地变量的状态。而迭代器并不会使用局部变量,它只需要一个可迭代对象进行迭代。 使用类可以实现你自己的迭代器,但无法实现生成器。 生成器运行速度快,语法简洁,更简单。 迭代器更能节约内存。
九、面向对象
1.12.1 面向对象三大特性: 封装,继承,多态
1. 封装
1.在类中对数据的赋值、内部调用对外部用户是透明的
2. 这使类变成了一个胶囊或容器,里面包含着类的数据和方法
3. 作用:
1)防止数据被随意修改
2)使外部程序不需要关注对象内部的构造,只需要通过对外提供的接口进行直接访问
2.Inheritance 继承(代码重用)
1. 一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承
2. 比如CS中的警察和恐怖分子,可以将两个角色的相同点写到一个父类中,然后同时去继承它
3. 使用经典类: Person.__init__(self,name,age) 并重写写父类Person的构造方法,实现,先覆盖,再继承,再重构
3. Polymorphism 多态(接口重用)
1.多态是面向对象的重要特性,简单点说:“一个接口,多种实现”
2. 指一个基类中派生出了不同的子类,且每个子类在继承同样的方法名的同时又对父类的方法做了不同的实现
3. 这就是同一种事物表现出的多种形态
4. 比如黄种人继承了人talk这个功能,但是他说的是中文,而美国人的talk是英文,但是他们是同样的talk
作用:简单的讲就是允许父类调用子类的方法
十、静态方法、类方法、属性方法
1. 静态方法
1. 作用:静态方法可以更好的组织代码,防止代码变大后变得比较混乱。
2. 特性: 静态方法只是名义上归类管理,实际上在静态方法里访问不了类或则实例中的任何属性
3. 静态方法使用场景:
1)我们要写一个只在类中运行而不在实例中运行的方法.
2)经常有一些跟类有关系的功能但在运行时又不需要实例和类参与的情况下需要用到静态方法.
3)比如更改环境变量或者修改其他类的属性等能用到静态方法.
4)这种情况可以直接用函数解决, 但这样同样会扩散类内部的代码,造成维护困难.
4. 调用方式: 既可以被类直接调用,也可以通过实例调用
class Dog(object):
def __init__(self,name):
self.name = name
@staticmethod
def eat():
print("I am a static method")
d = Dog("ChenRonghua")
d.eat() #方法1:使用实例调用
Dog.eat() #方法2:使用类直接调用
2. 类方法
1. 作用:无需实例化直接被类调用
2. 特性: 类方法只能访问类变量,不能访问实例变量
3. 类方法使用场景: 当我们还未创建实例,但是需要调用类中的方法
4. 调用方式: 既可以被类直接调用,也可以通过实例调用
class Dog(object):
name = '类变量' #在这里如果不定义类变量仅定义实例变量依然报错
def __init__(self,name):
self.name = '实例变量'
self.name = name
@classmethod
def eat(self,food):
print("%s is eating %s"%(self.name,food))
Dog.eat('baozi') #方法1:使用类直接调用
d = Dog("ChenRonghua")
d.eat("包子") #方法2:使用实例d调用
3. 属性方法
作用:属性方法把一个方法变成一个属性,隐藏了实现细节,调用时不必加括号直接d.eat即可调用self.eat()方法
class Dog(object):
def __init__(self, name):
self.name = name
@property
def eat(self):
print(" %s is eating" % self.name)
d = Dog("ChenRonghua")
d.eat()
注意事项:# 调用会出以下错误, 说NoneType is not callable, 因为eat此时已经变成一个静态属性了,
# 不是方法了, 想调用已经不需要加()号了,直接d.eat就可以了