编程范式
编程是程序员用特定的语法+数据结构+算法组成的代码来告诉计算机如何执行任务的过程 ,
不同的编程范式本质上代表对各种类型的任务采取的不同的
解决问题的思路, 大多数语言只支持一种编程范式,当然也有些语言可以同时支持多种编程范式。
两种最重要的编程范式分别是面向过程编程和面向对象编程。
面向对象编程
面向对象的特性:封装,继承,多态
封装:
封装:向外部隐藏不必要的细节,将属性和方法封装到一个抽象的类中。也方便了代码后期的重用。
继承:
实现代码的重用,子类中拥有父类的所有属性和方法。也可以重写父类方法。
通过继承创建的新类称为“子类”或“派生类”。
被继承的类称为“基类”、“父类”或“超类”。
继承的过程,就是从一般到特殊的过程。
要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。
在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。
多态:
不同的对象调用相同的代码,产生不同的效果,提高了代码的灵活性。
Pyhon 很多语法都是支持多态的,比如 len(),sorted(), 你给len传字符串就返回字符串的长度,传列表就返回列表长度。
类
类是创建实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据都不相同;通过在实例变量上调用方法,我们就直接操作了对象内部的数据,但无需知道方法内部的实现细节。
类的__init__方法
注意到__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。
有了__init__方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去:
和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数。
这个方法一般用于初始化一个类,但是当实例化一个类的时候,__init__并不是第一个运行的,第一个被调用的是__new__。
__new__方法
__new__方法是创建实例的方法,创建对象时调用,返回当前对象的一个实例
__init__方法是类实例创建之后调用,对当前对象的实例的一些初始化,没有返回值。
析构函数:实例在释放和自动销毁的时候就会自动执行__del__方法,并不是结束程序。


class Role: def __init__(self, name, role, weapon, life_value=100, money=15000): self.name = name self.role = role self.weapon = weapon def __del__(self): pass #print("%s 彻底死了。。。。" %self.name) def __shot(self): print("shooting...") def got_shot(self): self.__life_value -=50 print("%s:ah...,I got shot..."% self.name) def buy_gun(self, gun_name): print("%s just bought %s" % (self.name,gun_name) ) 例: r1 = Role('Chenronghua', 'police', 'AK47') r1.buy_gun("AK47") r1.got_shot() 结果:chengronghau just boughr AK47 chenronghua:ah...,i got shot... chenronghua 彻底死了。。。。 例: r1 = Role('Chenronghua', 'police', 'AK47') r1.buy_gun("AK47") r1.got_shot() r2 = Role('jack', 'terrorist', 'B22') #生成一个角色 r2.got_shot() 结果: chengronghau just boughr AK47 chenronghua:ah...,i got shot... jack just boughr AK47 chenronghua 彻底死了。。。。 jack 彻底死了。。。。 没有主动删除实例对象r1,r2在执行结束后自动销毁才执行__del__ del r1 可以删除一个实例 r1 = Role('Chenronghua', 'police', 'AK47') r1.buy_gun("AK47") r1.got_shot() del r1
类的私有属性和方法


类的私有属性可以在实例变量前面加‘__’(self.__name).在外面不可以直接访问但是可以在类的内部访问,可以在类中定义一个打印self.__name的函数,调用这个函数得到name的结果。 私有方法也是在方法名称前加"__" class Role: def __init__(self, name, role, weapon, life_value=100, money=15000): self.__name = name #r1.name=name实例变量(静态属性),作用域就是实例本身 self.role = role self.weapon = weapon def __del__(self): pass#print("%s 彻底死了。。。。" %self.name) def __shot(self): # 类的方法,功能 (动态属性) print("shooting...") def get_name(self): print("name is %s" % self.__name) def got_shot(self): #self.life_value -=50 print("%s:ah...,I got shot..."% self.name) def buy_gun(self, gun_name): print("%s just bought %s" % (self.name,gun_name) ) r1 = Role('Chenronghua', 'police', 'AK47') r1.get_name() 结果 name is chenronghua 通过get_name函数内部调用私有属性 class Role(object): def __init__(self, name, role, weapon, life_value=100, money=15000): # 构造函数:在实例化时做一些类的初始化工作 self.name = name # 实例变量(静态属性),作用域就是实例本身 self.role = role self.weapon = weapon self.life_value = life_value self.money = money def __testshot(self): # 类的方法(动态属性)R print("shooting...") def testgot_shot(self): self.__life_value -= 50 print("%s:ah...,I got shot..." % self.name) R1 = Role('Day', 'it', 'ak') R1._Role__testshot() 结果为 shooting.... 双下划线开头的实例变量是不是一定不能从外部访问呢?其实也不是。不能直接访问__name是因为Python解释器对外把__name变量改成了_Student__name,所以,仍然可以通过_Student__name来访问__name变量 Python不像C++、Java、C#等有明确的公共、私有或受保护的关键字来定义成员函数或属性,它使用约定的单下划线“_"和"__"双下划线作为函数或属性的前缀来标识。使用单下划线还是双下划线,是有很大的区别的。 1. 单下划线的函数或属性,在类定义中可以调用和访问,类的实例可以直接访问,子类中可以访问; 2. 双下划线的函数或属性,在类定义中可以调用和访问,类的实例不可以直接访问,子类不可访问。
反射


通过字符串映射或修改程序运行时的状态,属性,方法。把字符串映射到内存中的地址 hasattr(obj,name_str)判断一个对象obj里是否有对应的name_str字符串的方法 getattr(obj,name_str)根据字符串去获取obj对象里的对应的方法的内存地址 setattr(obj,'y',z) 相当于x.y=v.在注释在类里把外面的方法bulk传到类里,类名用第二个元素也就是用户的输入字符串 delattr(obj,name_str) 删除obj类的name_str方法 def bulk(self): print("%s is yelling....."%self.name) class Dog(object): def __init__(self,name): self.name=name def eat(self): print("%s is eating.."% self.name) d=Dog("Day") choice=input(">>:").strip() #用户通过输入调用Dog类的方法,但是返回的是个字符串该如何调用呢? # print(hasattr(d,choice))#获取d对象中有没有choice,返回布尔值。 # getattr(d,choice)()#打印出choice在类中的内存地址 if hasattr(d,choice): #delattr(d,choice) func=getattr(d,choice) func()#如果想传参数可以在eat加个food else: setattr(d,choice,bulk)#动态安装一个方法(分别代表 类 字符串 安装的方法。) d.talk(d) #相当于 d.bulk(self)动态的将一个方法装配到类里。 # 在类里把外面的方法bulk传到类里,类名用第二个元素也就是用户的输入字符串 # 因为bulk函数在类的外面所以要把d传给bulk的self..。 ''' else: setattr(d,choice,22) print(getattr(d,choice)) 动态的安装一个属性 '''
super函数


1 在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能,这时,我们就需要调用父类的方法了,可通过使用 super 来实现,比如:(下面对父类的方法和初始化方法应用) 2 定义某个方法覆盖父类的同名方法 3 class Animals(object): 4 def __init__(self,name): 5 self.name = name 6 def greet(self): 7 print("Hello,I am %s" % self.name) 8 class Dog(Animals): 9 def greet(self): 10 super(Dog,self).greet() 11 print("wangwang.....") 12 dog = Dog('mike') 13 dog.greet() 14 15 >>>Hello,I am mike 16 wangwang..... 17 看了上面的使用,你可能会觉得 super 的使用很简单,无非就是获取了父类,并调用父类的方法。其实,在上面的情况下,super 获得的类刚好是父类,但在其他情况就不一定了,super 其实和父类没有实质性的关联。 18 让我们看一个稍微复杂的例子,涉及到多重继承,代码如下: 19 class Base(object): 20 def __init__(self): 21 print "enter Base" 22 print "leave Base" 23 24 class A(Base): 25 def __init__(self): 26 print "enter A" 27 super(A, self).__init__() 28 print "leave A" 29 30 class B(Base): 31 def __init__(self): 32 print "enter B" 33 super(B, self).__init__() 34 print "leave B" 35 36 class C(A, B): 37 def __init__(self): 38 print "enter C" 39 super(C, self).__init__() 40 print "leave C" 41 42 其中,Base 是父类,A, B 继承自 Base, C 继承自 A, B,它们的继承关系是一个典型的『菱形继承』,如下: 43 Base 44 / \ 45 / \ 46 A B 47 \ / 48 \ / 49 C 50 51 现在,让我们看一下使用: 52 >>> c = C() 53 enter C 54 enter A 55 enter B 56 enter Base 57 leave Base 58 leave B 59 leave A 60 leave C 61 62 如果你认为 super 代表『调用父类的方法』,那你很可能会疑惑为什么 enter A 的下一句不是 enter Base 而是 enter B。原因是,super 和父类没有实质性的关联,现在让我们搞清 super 是怎么运作的。 63 64 MRO 列表 65 事实上,对于你定义的每一个类,Python 会计算出一个方法解析顺序(Method Resolution Order, MRO)列表,它代表了类继承的顺序,我们可以使用下面的方式获得某个类的 MRO 列表: 66 >>> C.mro() # or C.__mro__ or C().__class__.mro() 67 [__main__.C, __main__.A, __main__.B, __main__.Base, object] 68 69 那这个 MRO 列表的顺序是怎么定的呢,它是通过一个 C3 线性化算法来实现的,一个类的 MRO 列表就是合并所有父类的 MRO 列表,并遵循以下三条原则: 70 子类永远在父类前面 71 如果有多个父类,会根据它们在列表中的顺序被检查 72 如果对下一个类存在两个合法的选择,选择第一个父类 73 -------------------------------------------------------------------------------- 74 super 原理 75 super 的工作原理如下: 76 def super(cls, inst): 77 mro = inst.__class__.mro() 78 return mro[mro.index(cls) + 1] 79 80 其中,cls 代表类,inst 代表实例,上面的代码做了两件事: 81 获取 inst 的 MRO 列表 82 查找 cls 在当前 MRO 列表中的 index, 并返回它的下一个类,即 mro[index + 1] 83 当你使用 super(cls, inst) 时,Python 会在 inst 的 MRO 列表上搜索 cls 的下一个类。 84 现在,让我们回到前面的例子。 85 首先看类 C 的 __init__ 方法: 86 super(C, self).__init__() 87 88 这里的 self 是当前 C 的实例,self.class.mro() 结果是: 89 [__main__.C, __main__.A, __main__.B, __main__.Base, object] 90 91 可以看到,C 的下一个类是 A,于是,跳到了 A 的 __init__,这时会打印出 enter A,并执行下面一行代码: 92 super(A, self).__init__() 93 94 注意,这里的 self 也是当前 C 的实例,MRO 列表跟上面是一样的,搜索 A 在 MRO 中的下一个类,发现是 B,于是,跳到了 B 的 __init__,这时会打印出 enter B,而不是 enter Base。 95 整个过程还是比较清晰的,关键是要理解 super 的工作方式,而不是想当然地认为 super 调用了父类的方法。 96 小结 97 事实上,super 和父类没有实质性的关联。 98 super(cls, inst) 获得的是 cls 在 inst 的 MRO 列表中的下一个类。
继承
面向对象中的继承就是继承的类直接拥有被继承类的属性而不需要在自己的类体中重新再写一遍,其中被继承的类叫做父类、基类,继承的类叫做派生类、子类。在python3中如果不指定继承哪个类,默认就会继承Object类,而继承了Object类的类就叫做新式类,而在python2中如果不指定继承哪个类也不会默认去继承Object类,而没有继承Object类的类就叫做经典类。经典类和新式类的不同就在于对方法的搜索顺序不同,经典类是深度优先即先找自己类内,如果没有就找左边第一个父类,没找到继续从这个父类的父类中找依次类推直到找到最上一级的父类也没找到再找左边第二个父类,然后再重复之前的过程,直到所有父类找一遍没找到就报错;而新式类是广度优先,当下一个类可以通过其他类找到时就先不去找它,而是找继承关系中与它的子类同级的其他类,依次类推直到最后找到object类没有找到指定方法就报错。新式类搜索顺序图示如下,还可以通过类名.mro()查看新式类继承中的属性搜索顺序。


要确定一个类是否是另一个类的子类,可使用内置方法issubclass。 >>> issubclass(SPAMFilter, Filter) True >>> issubclass(Filter, SPAMFilter) False 如果你有一个类,并想知道它的基类,可访问其特殊属性__bases__。 >>> SPAMFilter.__bases__ (<class __main__.Filter at 0x171e40>,) >>> Filter.__bases__ (<class 'object'>,) 同样,要确定对象是否是特定类的实例,可使用isinstance。 >>> s = SPAMFilter() >>> isinstance(s, SPAMFilter) True >>> isinstance(s, Filter) True >>> isinstance(s, str) False 如你所见, s是SPAMFilter类的(直接)实例,但它也是Filter类的间接实例,因为SPAMFilter 是Filter的子类。换而言之,所有SPAMFilter对象都是Filter对象。从前一个示例可知, isinstance 也可用于类型,如字符串类型(str)。 如果你要获悉对象属于哪个类,可使用属性__class__。 >>> s.__class__ <class __main__.SPAMFilter at 0x1707c0> 注意 对于新式类(无论是通过使用__metaclass__ = type还是通过从object继承创建的)的实例,还可使用type(s)来获悉其所属的类。对于所有旧式类的实例, type都只是返回instance。 多重继承 在前一节,你肯定注意到了一个有点奇怪的细节:复数形式的__bases__。前面说过,你可 使用它来获悉类的基类,而基类可能有多个。为说明如何继承多个类,下面来创建几个类。 class Calculator: def calculate(self, expression): self.value = eval(expression) class Talker: def talk(self): print('Hi, my value is', self.value) class TalkingCalculator(Calculator, Talker): pass 子类TalkingCalculator本身无所作为,其所有的行为都是从超类那里继承的。关键是通过从 Calculator那里继承calculate,并从Talker那里继承talk,它成了会说话的计算器。 >>> tc = TalkingCalculator() >>> tc.calculate('1 + 2 * 3') >>> tc.talk() Hi, my value is 7 这被称为多重继承,是一个功能强大的工具。然而,除非万不得已,否则应避免使用多重继 承,因为在有些情况下,它可能带来意外的“并发症”。 使用多重继承时,有一点务必注意:如果多个超类以不同的方式实现了同一个方法(即有多 个同名方法),必须在class语句中小心排列这些超类,因为位于前面的类的方法将覆盖位于后面 的类的方法。因此,在前面的示例中,如果Calculator类包含方法talk,那么这个方法将覆盖Talker 类的方法talk(导致它不可访问)。如果像下面这样反转超类的排列顺序: class TalkingCalculator(Talker, Calculator): pass 将导致Talker的方法talk是可以访问的。多个超类的超类相同时,查找特定方法或属性时访 问超类的顺序称为方法解析顺序(MRO),它使用的算法非常复杂。所幸其效果很好,你可能根 本无需担心。
-
对象:对象由属性和方法组成。属性不过是属于对象的变量,而方法是存储在属性中的
函数。相比于其他函数,(关联的)方法有一个不同之处,那就是它总是将其所属的对象
作为第一个参数,而这个参数通常被命名为self。 - 类:类表示一组(或一类)对象,而每个对象都属于特定的类。类的主要任务是定义其实例将包含的方。
- 多态:多态指的是能够同样地对待不同类型和类的对象,即无需知道对象属于哪个类就可调用其方法。
-
封装:对象可能隐藏(封装)其内部状态。在有些语言中,这意味着对象的状态(属性)
只能通过其方法来访问。在Python中,所有的属性都是公有的,但直接访问对象的状态时
程序员应谨慎行事,因为这可能在不经意间导致状态不一致。 -
继承:一个类可以是一个或多个类的子类,在这种情况下,子类将继承超类的所有方法。
你可指定多个超类,通过这样做可组合正交(独立且不相关)的功能。为此,一种常见
的做法是使用一个核心超类以及一个或多个混合超类。 -
接口和内省:一般而言,你无需过于深入地研究对象,而只依赖于多态来调用所需的方
法。然而,如果要确定对象包含哪些方法或属性,有一些函数可供你用来完成这种工作。 - 抽象基类:使用模块abc可创建抽象基类。抽象基类用于指定子类必须提供哪些功能,却不实现这些功能。
-
面向对象设计:关于该如何进行面向对象设计以及是否该采用面向对象设计,有很多不
同的观点。无论你持什么样的观点,都必须深入理解问题,进而创建出易于理解的设计。
callable(object) 判断对象是否是可调用的(如是否是函数或方法)
getattr(object,name[,default]) 获取属性的值,还可提供默认值
hasattr(object, name) 确定对象是否有指定的属性
isinstance(object, class) 确定对象是否是指定类的实例
issubclass(A, B) 确定A是否是B的子类
random.choice(sequence) 从一个非空序列中随机地选择一个元素
setattr(object, name, value) 将对象的指定属性设置为指定的值
type(object) 返回对象的类型
静态方法和类方法


1 class Dog(object): 2 def __init__(self,name): 3 self.name=name 4 @staticmethod#静态方法让下面的函数和类没什么关系,但是和普通的函数区别就是调用函数需要类名或实例变量,同时也无法访问类和实例的任何属性 5 def eat(): 6 print("%s is eating %s"%("ss","aa")) 7 d=Dog("chenronghua") 8 d.eat() 9 >>> "ss is eating aa" 10 -------------------------------------------------------------------------------- 11 class Dog(object): 12 name="Day" 13 n=33 14 def __init__(self,name): 15 self.name=name 16 self.n=22 17 @classmethod#类方法只能访问类变量不能访问实例变量 18 def eat(self): 19 print("%s is eating %s"%(self.name,self.n)) 20 d=Dog("chenronghua") 21 d.eat() 22 >>>Day is eating 33 23 -------------------------------------------------------------------------------- 24 class Dog(object): 25 name="Day" 26 n=33 27 def __init__(self,name): 28 self.name=name 29 self.n=22 30 @property#属性方法就是把一个函数方法变成一个静态属性,当然也不能加“()”调用 31 def eat(self): 32 print("%s is eating %s"%(self.name,self.n)) 33 d=Dog("chenronghua") 34 d.eat 35 >>>chenronghua is eating 22 36 注:那如何给eat赋值呢 37 --------------------------------------------------------------------------------- 38 class Dog(object): 39 def __init__(self,name): 40 self.name=name 41 42 self.__food=None 43 @property#属性方法就是把一个函数方法变成一个静态属性,当然也不能加“()”调用 44 def eat(self): 45 print("%s is eating %s"%(self.name,self.__food)) 46 @eat.setter 47 def eat(self,food): 48 print("set to eat is",food) 49 self.__food=food#food值添加到实例属性 50 d=Dog("chenronghua") 51 d.eat 52 d.eat="baozi"#会自动调用属性方法 53 d.eat 54 >>> 55 chenronghua is eating None 56 set to eat is baozi 57 chenronghua is eating baozi 58 可以在@eat.setter属性方法中给实例赋值 59 新加的属性food如何删除呢? 60 ---------------------------------------------------------------------------- 61 class Dog(object): 62 def __init__(self,name): 63 self.name=name 64 self.n=22 65 66 @property 67 def eat(self): 68 print("%s is eating %s"%(self.name,self.n)) 69 @eat.setter 70 def eat(self,food): 71 print("set to eat is",food) 72 self.food=food 73 @eat.deleter 74 def eat(self): 75 del d.food 76 d=Dog("chenronghua") 77 d.eat="xiangjaio" 78 print(d.__dict__) 79 del d.eat 80 print(d.__dict__) 81 结果 82 set to eat is xiangjaio 83 {'food': 'xiangjaio', 'name': 'chenronghua', 'n': 22} 84 {'name': 'chenronghua', 'n': 22} 85 属性方法例子 86 class Flight(object): 87 def __init__(self,name): 88 self.flight_name = name 89 def checking_status(self): 90 print("checking flight %s status " % self.flight_name) 91 return 0 92 @property 93 def flight_status(self): 94 status = self.checking_status()#调取航空公司的API接口 95 if status == 0 : 96 print("flight got canceled...") 97 elif status == 1 : 98 print("flight is arrived...") 99 elif status == 2: 100 print("flight has departured already...") 101 else: 102 print("cannot confirm the flight status...,please check later") 103 @flight_status.setter 104 def flight_status(self,status): 105 print("flight %s has changed status to %s" %(self.flight_name,status)) 106 f = Flight("CA980") 107 f.flight_status 108 f.flight_status="beijing" 109 因为checking_status的数据是保密也可以说隐藏了实现细节,对用户只是返回了一个代表航班状态的标识 110 可以通过@flight_status.setter来更改航班的返回状态
类的特殊成员方法


1 1.__doc__ 表示类的描述信息 2 class Foo(): 3 '''描述类的信息''' 4 def dunc(self): 5 pass 6 print(Foo.__doc__) 7 >>>描述类的信息 8 2__module__和__class__ 9 class C: 10 def __init__(self): 11 self.name="Day" 12 from lib.aa import C 13 obj=C() 14 print(obj.__module__)#输出lib.aa,查看类C是在那个模块导出来的。 15 print(obj.__class__)#输出lib.aa.c,查看C自己的位置 16 3.__call__方法 17 class C(object): 18 def __init__(self): 19 self.name="Day" 20 def __call__(self,*args,**kwargs): 21 print("running call",args,kwargs) 22 d=C() 23 d(1,2,3,name=333) 24 >>>running call (1, 2, 3) {'name': 333} 25 实例后的对象d后面可以加()调用__call__函数的内容 26 4.__dict__查看类或对象的所有成员 27 通过类打印:以一个字典的形式打印出类里所有的属性方法以字典的形式打印出来不包括实例属性 28 通过变量打印:以字典的形式打印出变量的属性,不包括类的属性方法 29 5.__getitem__,__setitem__,__delitem__ 30 用于索引操作,如字典。以上分别代表获取,设置,删除数据。 31 class Foo(object): 32 def __init__(self): 33 self.data={} 34 def __getitem__(self,key): 35 print("__getitem__",key) 36 return self.data.get(key) 37 def __setitem__(self,key,value): 38 print("__setitem__",key,value) 39 self.data[key]=value 40 def __delitem__(self,key): 41 print("__delitem__",key) 42 obj=Foo() 43 obj["name"]="Day"#调用__setitem__方法传入到self.data 44 print(obj['name'])#获取字典的值 45 print(obj.data)#把类实例设置成一个字典结果是{'name': 'Day'} 46 del obj["name"]#注意触发__delitem__函数内的语句但是没有删除name属性,只是走到当前位置 47 -------------------------------------------------------------------------------- 48 def func(self): 49 print("hello %s"% self.name) 50 def __init__(self,name,age): 51 self.name=name 52 self.age=age 53 54 Foo=type("Foo",(object,),{"talk":func,"__init__":__init__}) 55 f=Foo("alex",22) 56 f.talk() 57 #Foo相当于Type的实例但它本身也是一个类,而f又是Foo的一个实例。类通过Type生成的,常用的定义类就是对Type的封装。一切皆对象 58 -------------------------------------------------------------------------------- 59 class Foo(object): 60 def __init__(self,name): 61 self.name=name 62 print("Foo---init__") 63 def __new__(cls,*args,**kwargs): 64 print("Foo --new__") 65 #return object.__new__(cls)#继承父类的__NEW__方法.cls=Foo 66 f=Foo("Day") 67 >>> 68 Foo --new__ 69 #Foo---init__ 70 注:__new__也会在实例化的时候执行而且是先于__init__,如果__new__没有return那么就不能实例化。 71 类的实例其实先执行的__new__之后才执行的__init__,也就是实例后自动执行__init__,其实就是__new__触发的。 72 因为父类里默认有__new__方法所以不用写,否则就会覆盖。用处是可以在类实例化之前定制一些方法
迭代器(iterator)
概述
迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
延迟计算或惰性求值 (Lazy evaluation)
迭代器不要求你事先准备好整个迭代过程中所有的元素。仅仅是在迭代至某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合。
可迭代对象
迭代器提供了一个统一的访问集合的接口。只要是实现了__iter__()或__getitem__()方法的对象,就可以使用迭代器进行访问。
序列:字符串、列表、元组
非序列:字典、文件
自定义类:用户自定义的类实现了__iter__()或__getitem__()方法的对象
创建迭代器对象
使用内建的工厂函数iter(iterable)可以获取迭代器对象:
语法:
iter(collection) -> iterator
iter(callable,sentinel) -> iterator
说明:
Get an iterator from an object.
In the first form, the argument must supply its own iterator, or be a sequence.
In the second form, the callable is called until it returns the sentinel.
实例展示:


1 使用对象内置的__iter__()方法生成迭代器 2 >>>L1 = [1,2,3,4,5,6] 3 >>>I1 = L1.__iter__() 4 >>>print I1 5 <listiterator object at 0x7fe4fd0ef550> 6 >>> I1.next() 7 1 8 >>> I1.next() 9 2 10 >>> I1.next() 11 3 12 ---------------------------------------------------------------------- 13 使用内置工厂函数生成迭代器 14 >>> L1 = [1,2,3,4,5,6] 15 >>> I2 = iter(L1) 16 >>> print I2 17 <listiterator object at 0x7fe4fd0ef610> 18 >>> I2.next() 19 1 20 >>> I2.next() 21 2 22 >>> I2.next() 23 3
Python的生成器
生成器(generator)概念
创建python迭代器的过程虽然强大,但是很多时候使用不方便。生成器是一个简单的方式来完成迭代。简单来说,Python的生成器是一个返回可以迭代对象的函数。
如何创建生成器
在一个一般函数中使用yield关键字,可以实现一个最简单的生成器,此时这个函数变成一个生成器函数。yield与return返回相同的值,区别在于return返回后,函数状态终止,而yield会保存当前函数的执行状态,在返回后,函数又回到之前保存的状态继续执行。
生成器函数与一般函数的不同
生成器函数包含一个或者多个yield
当调用生成器函数时,函数将返回一个对象,但是不会立刻向下执行
像__iter__()和__next__()方法等是自动实现的,所以我们可以通过next()方法对对象进行迭代
一旦函数被yield,函数会暂停,控制权返回调用者
局部变量和它们的状态会被保存,直到下一次调用
函数终止的时候,StopIteraion会被自动抛出


1 def my_gen(): 2 n=1 3 print("first") 4 # yield区域 5 yield n 6 7 n+=1 8 print("second") 9 yield n 10 11 n+=1 12 print("third") 13 yield n 14 15 a=my_gen() 16 print("next method:") 17 # 每次调用a的时候,函数都从之前保存的状态执行 18 print(next(a)) 19 print(next(a)) 20 print(next(a)) 21 22 print("for loop:") 23 # 与调用next等价的 24 b=my_gen() 25 for elem in my_gen(): 26 print(elem)
使用循环的生成器
1 # 逆序yield出对象的元素 2 def rev_str(my_str): 3 length=len(my_str) 4 for i in range(length-1,-1,-1): 5 yield my_str[i] 6 7 for char in rev_str("hello"): 8 print(char)
生成器表达式
Python中,列表生成方法,比如
#产出1,2,3,4,5的一个列表
[x for x in range(5)]
如果换成(),那么会成为生成器表达式
(x for x in range(5))
具体使用方式:
1 a=(x for x in range(10)) 2 b=[x for x in range(10)] 3 # 这是错误的,因为生成器不能直接给出长度 4 # print("length a:",len(a)) 5 6 # 输出列表的长度 7 print("length b:",len(b)) 8 9 b=iter(b) 10 # 二者输出等价,不过b是在运行时开辟内存,而a是直接开辟内存 11 print(next(a)) 12 print(next(b))
为什么使用生成器
1.更容易使用,代码量较小
2.内存使用更加高效。比如列表是在建立的时候就分配所有的内存空间,而生成器仅仅是需要的时候才使用,更像一个记录
3.代表了一个无限的流。如果我们要读取并使用的内容远远超过内存,但是需要对所有的流中的内容进行处理,那么生成器是一个很好的选择,比如可以让生成器返回当前的处理状态,由于它可以保存状态,那么下一次直接处理即可。
4.流水线生成器。假设我们有一个快餐记录,这个记录的地4行记录了过去五年每小时售出的食品数量,并且我们要把所有的数量加在一起,求解过去5年的售出的总数。假设所有的数据都是字符串,并且不可用的数字被标记成N/A。那么可以使用下面的方式处理:
1 with open('sells.log') as file: 2 pizza_col = (line[3] for line in file) 3 per_hour = (int(x) for x in pizza_col if x != 'N/A') # 使用生成器进行自动迭代 4 print("Total pizzas sold = ",sum(per_hour))
面向对象编程详解
本文深入讲解面向对象编程的核心概念,包括封装、继承、多态等特性,并探讨了类的特殊成员方法、静态方法、类方法及属性方法的使用。此外,还介绍了Python中的迭代器与生成器的概念和应用。
3874

被折叠的 条评论
为什么被折叠?



