Python_oldboy_自动化运维之路_面向对象2(十)
本节内容:
- 面向对象程序设计的由来
- 什么是面向对象的程序设计及为什么要有它
- 类和对象
- 继承与派生
- 多的态与多态性
- 封装
- 静态方法和类方法
- 面向对象的软件开发
- 反射
- 类的特殊成员方法
- 异常处理
1.面向对象程序设计的由来
见概述:http://www.cnblogs.com/linhaifeng/articles/6428835.html
2.什么是面向对象的程序设计及为什么要有它
面向过程的程序设计的核心是过程,过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西。
优点是:极大的降低了程序的复杂度
缺点是:一套流水线或者流程就是用来解决一个问题,生产汽水的流水线无法生产汽车,即便是能,也得是大改,改一个组件,牵一发而动全身。
应用场景:一旦完成基本很少改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等。
面向对象的程序设计的核心是对象,要理解对象为何物,必须把自己当成上帝,上帝眼里世间存在的万物皆为对象,不存在的也可以创造出来。面向对象的程序设计好比如来设计西游记,如来要解决的问题是把经书传给东土大唐,如来想了想解决这个问题需要四个人:唐僧,沙和尚,猪八戒,孙悟空,每个人都有各自的特征和技能(这就是对象的概念,特征和技能分别对应对象的数据属性和方法属性),然而这并不好玩,于是如来又安排了一群妖魔鬼怪,为了防止师徒四人在取经路上被搞死,又安排了一群神仙保驾护航,这些都是对象。然后取经开始,师徒四人与妖魔鬼怪神仙交互着直到最后取得真经。如来根本不会管师徒四人按照什么流程去取。
面向对象的程序设计的
优点是:解决了程序的扩展性。对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易。
缺点:可控性差,无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即便是上帝也无法预测最终结果。于是我们经常看到一个游戏人某一参数的修改极有可能导致阴霸的技能出现,一刀砍死3个人,这个游戏就失去平衡。
应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方
面向对象的程序设计并不是全部。对于一个软件质量来说,面向对象的程序设计只是用来解决扩展性。
3.类和对象
python中一切皆为对象,且python3统一了类与类型的概念,类型就是类,所以,不管你信不信,你已经使用了很长时间的类了
>>> dict #类型dict就是类dict <class 'dict'> >>> d=dict(name='lijun') #实例化 >>> d {'name': 'lijun'} >>> d.pop('name') #向d发一条消息,执行d的方法pop,相当于调用dict类的一个属性,其中pop这个函数就是删除字典的指定内容并且回显 'lijun'
基于面向对象设计一个款游戏:英雄联盟,每个玩家选一个英雄,每个英雄都有自己的特征和和技能,特征即数据属性,技能即方法属性,特征与技能的结合体就一个对象。
从一组对象中提取相似的部分就是类,类也是特征与技能的结合体,特征即数据并且是所有对象共享的数据,技能即函数属性并且是所有对象共享的函数属性。
garen_hero.Q()称为向garen_hero这个对象发送了一条消息,让他去执行Q这个函数,完成一个功能,类似的有:
garen_hero.W()
garen_hero.E()
garen_hero.R()
一个英雄可以攻击另外一个英雄,这就是对象之间的交互
garen_hero.attack(Riven)
#类有两个功能:实例化和属性引用 class Garen: #lol游戏人物盖伦 camp='Demacia' #阵营:德玛西亚 def __init__(self,nickname,aggresivity,life_value): #每个玩家都有共同的属性:名字,攻击力,生命值 self.nickname = nickname self.aggresivity = aggresivity self.life_value = life_value def attack(self,enemy): #定义一个攻击的属性 print('is attacking....',self,enemy) #类的第一个功能:实例化 g1 = Garen('草丛伦',82,100) #实例化,相当于生成一个游戏玩家 g2 = Garen('hahaha',12,1020) #类的第二个功能:属性引用,包含数据属性和函数属性 print(Garen.camp) print(Garen.__init__) print(Garen.attack) #对于一个实例来说,只有一种功能:属性引用 print(g1.nickname,g1.aggresivity,g1.life_value) # print(g1.attack,id(g1.attack)) #通过实例来调用类中的函数,根据显示的内容就是绑定方法 print(Garen.attack,id(Garen.attack)) #通过类来调用自己的函数属性 #总结: #对于类来说就是数据(camp)和函数(attack)的结合体 #对于类的数据属性来说,共享给所以实例 #对于类的函数来说,绑定给实例用,绑定了就是为了给实例用的,实例可以传自己的参数 Garen.attack(1,2) g1.attack('lijun') #相当于:Garen.attack(g1,'lijun') 这就是绑定方法的牛逼用处,自动传值 print(g1.camp,id(g1.camp)) print(g2.camp,id(g2.camp)) print(Garen.camp,id(Garen.camp))
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ # #类体现出对象与对象交互的概念 class Garen: #lol游戏人物盖伦 camp='Demacia' #阵营:德玛西亚 def __init__(self,nickname,aggresivity,life_value): #每个玩家都有共同的属性:名字,攻击力,生命值 self.nickname = nickname self.aggresivity = aggresivity self.life_value = life_value def attack(self,enemy): #定义一个攻击的属性 print('is attacking....',self,enemy) class Riven: camp='Noxus' def __init__(self, nickname, aggresivity, life_value): # 每个玩家都有共同的属性:名字,攻击力,生命值 self.nickname = nickname self.aggresivity = aggresivity self.life_value = life_value def attack(self, enemy): # 定义一个攻击的属性 print('is attacking....', self, enemy) enemy.life_value -= self.aggresivity #g1的最后生命值就是g1现在的生命值减去r1的攻击力 g1 = Garen('草丛伦',82,100) r1 = Riven('hahah',20,1000) #模拟riven英雄攻击garen英雄 r1.attack(g1) print(g1.life_value) #所以最后g1的生命值就是100-20
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ # #写一个类的过程:举列,女娲造人 #第一步:先想下人(实例)有什么通用的属性,私有的属性,共同的技能,先站住实例的角度去想 #第二步:写的时候在开始写class,然后在生成实例 class Chinese: dang = 'gongchandang' #通用的属性 def __init__(self,name,age,gender): #私有的属性 self.name = name self.age = age self.gender = gender def talk(self): print('chifan shuijiao dadoudou......') #共同的技能 d=Chinese('lijun',18,'F')
查找方法:
在obj.name会先从obj自己的名称空间(__init__)里找name,找不到则去类中找,类也找不到就找父类...最后都找不到就抛出异常
4.继承和派生
4.1什么是继承
继承是一种创建新的类的方式,在python中,新建的类可以继承自一个或者多个父类,原始类称为基类或超类,新建的类称为派生类或子类。
python中类的继承分为:单继承和多继承
class ParentClass1: #定义父类 pass class ParentClass2: #定义父类 pass class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生是指subclass1类里自己独有的功能 pass class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类 pass
查看继承
>>> SubClass1.__bases__ (<class '__main__.ParentClass1'>,) >>> SubClass2.__bases__ (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
提示:如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现。
只要继承为object,就被称为新式类
>>> ParentClass1.__bases__ (<class 'object'>,) >>> ParentClass2.__bases__ (<class 'object'>,)
4.2 继承与抽象
抽象即抽取类似或者说比较像的部分。
抽象分成两个层次:
1.将奥巴马和梅西这俩对象比较像的部分抽取成类;
2.将人,猪,狗这三个类比较像的部分抽取成父类。
抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)
继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。
抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类
4.3 继承的重要性
在开发程序的过程中,如果我们定义了一个类A,然后又想新建立另外一个类B,但是类B的大部分内容与类A的相同时
我们不可能从头开始写一个类B,这就用到了类的继承的概念。
通过继承的方式新建类B,让B继承A,B会‘遗传’A的所有属性(数据属性和函数属性),实现代码重用
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ class Hero: def __init__(self,nickname,aggresivity,life_value): #每个玩家都有共同的属性:名字,攻击力,生命值 self.nickname = nickname self.aggresivity = aggresivity self.life_value = life_value def attack(self,enemy): #定义一个攻击的属性 print('is attacking....',self,enemy) enemy.life_value -= self.aggresivity class Garen(Hero): camp='Demacia' def fly(self): #盖伦自己的功能,会飞 print('is frying......') def attack(self): print('这个优先') class Riven(Hero): camp='Noxus' g1 = Garen('草丛伦',82,100) g1.fly() g1.attack() #假如重名会先到自己类里面找,找不到在去找父类
4.4 组合与重用性
组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ #组合 #几个类之间没有共同之处,在一个类中以另外一个类的对象作为数据属性 class Teacher: def __init__(self,name): self.name = name self.birth = Date(1994,8,18) self.course = Course('python','11000','5mons') class Course: def __init__(self,name,price,period): self.name = name self.price = price self.period = period class Date: def __init__(self,year,month,day): self.year = year self.month = month self.day = day t1 = Teacher('alex') print(t1.birth.year) #假如只调用出生的年
4.5 接口与归一化设计
继承有两种用途:
一:继承基类的方法,并且做出自己的改变或者扩展(代码重用)
二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ #运用场景:你去驾校考驾照,学会了就可以开宝马,开奥迪,学会了一种方法,其他的就都会了 #案例:inux一切皆文件 class AllFile: #接口类,接口类只定义要有什么功能,不去真实的实现 def read(self): #接口函数 pass def write(self): pass class Text(AllFile): def read(self): print('文件的读功能!') def write(self): print('文件的写功能!') class Sata(AllFile): def read(self): print('sata的读功能!') def write(self): print('sata的写功能!') t=Text() s=Sata()
为什么要用接口:(这是一种设计思路)
接口提取了一群类共同的函数,可以把接口当做一个函数的集合。
然后让子类去实现接口中的函数。
这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。
归一化,让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
比如:我们定义一个动物接口,接口里定义了有跑、吃、呼吸等接口函数,这样老鼠的类去实现了该接口,松鼠的类也去实现了该接口,由二者分别产生一只老鼠和一只松鼠送到你面前,即便是你分别不到底哪只是什么鼠你肯定知道他俩都会跑,都会吃,都能呼吸。
再比如:我们有一个汽车接口,里面定义了汽车所有的功能,然后由本田汽车的类,奥迪汽车的类,大众汽车的类,他们都实现了汽车接口,这样就好办了,大家只需要学会了怎么开汽车,那么无论是本田,还是奥迪,还是大众我们都会开了,开的时候根本无需关心我开的是哪一类车,操作手法(函数调用)都一样
4.6 抽象类和接口
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ # # #刚才的那个接口例子,子类不写具体实现的功能也是可以的 # class AllFile: #接口类,接口类只定义要有什么功能,不去真实的实现 # def read(self): #接口函数 # pass # # def write(self): # pass # # class Text(AllFile): # pass # # # # t=Text() #假如我想控制子类必须要实现功能,必须重写read和write的函数,否则就报错 #这种方法就叫做抽象类 #概念:抽象类只是用来继承的,继承的人负责实现父类的功能 import abc class AllFile(metaclass=abc.ABCMeta): #抽象类 @abc.abstractclassmethod def read(self): pass @abc.abstractclassmethod def write(self): pass class Text(AllFile): def read(self): pass def write(self): pass t=Text()
4.7 继承顺序
class A(object): def test(self): print('from A') class B(A): def test(self): print('from B') class C(A): def test(self): print('from C') class D(B): def test(self): print('from D') class E(C): def test(self): print('from E') class F(D,E): # def test(self): # print('from F') pass f1=F() f1.test() print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性 #新式类继承顺序:F->D->B->E->C->A #经典类继承顺序:F->D->B->A->E->C #python3中统一都是新式类 #pyhon2中才分新式类与经典类
继承原理:
python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如
>>> F.mro() #等同于F.__mro__ [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
4.8 子类调用父类的方法
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ #假如子类想要调用父类中的test功能 # # class A: # def test(self): # print('from a.test') # # class B(A): # def test(self): # A.test(self) # print('from b.test') # # b1=B() # b1.test() #运用场景 class People: def __init__(self,name,age,gender): self.name = name self.age = age self.gender = gender def test(self): print('from a.test') class Teacher(People): def __init__(self,name,age,gender,level): # People.__init__(self,name,age,gender) super().__init__(name,age,gender) self.level = level t = Teacher('lijun',18,'female','高级讲师') print(t.level)
5.多态与多态性
多态性的好处:
1.增加了程序的灵活性
以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal)
2.增加了程序额可扩展性
通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ #多态指的是一类事物有多种形态,(一个抽象类有多个子类,因而多态的概念依赖于继承) class Animal: def talk(self): pass class People(Animal): def talk(self): print('say hello') class Pig(Animal): def talk(self): print('say ao ao ao') class Dog(Animal): def talk(self): print('say wang wang wang') p1=People() pig=Pig() d1=Dog() #以下就是多态性,牛逼之处,假如在来个猫的类,下面的函数无需更改,直接传值 def func(obj): obj.talk() func(p1) func(pig) func(d1)
6.封装
6.1 封装要装什么
你钱包的有多少钱(数据的封装)
你的性取向(数据的封装)
你撒尿的具体功能是怎么实现的(方法的封装)
6.2 为什么要用封装
封装数据的主要原因是:保护隐私(作为男人的你,脸上就写着:我喜欢男人,你害怕么?)
封装方法的主要原因是:隔离复杂度(快门就是傻瓜相机为傻瓜们提供的方法,该方法将内部复杂的照相功能都隐藏起来了,比如你不必知道你自己的尿是怎么流出来的,你直接掏出自己的接口就能用尿这个功能)
你的身体没有一处不体现着封装的概念:你的身体把膀胱尿道等等这些尿的功能隐藏了起来,然后为你提供一个尿的接口就可以了(接口就是你的。。。,),你总不能把膀胱挂在身体外面,上厕所的时候就跟别人炫耀:hi,man,你瞅我的膀胱,看看我是怎么尿的。还有你的头把你的脑子封装到了脑壳里,然后提供了眼睛这个接口....
提示:在编程语言里,对外提供的接口(接口可理解为了一个入口),就是函数,称为接口函数,这与接口的概念还不一样,接口代表一组接口函数的集合体。
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ #封装有两个层面 #第一个层面:假入从另外一个脚本执行这个函数,通过导入模块的方式,对于用户来说这个函数是看不出来是怎么写的,直接调用就好了 # class Foo: # x=1 # def __init__(self,name): # self.name = name #第二个层面:self.__money class Foo: x=1 def __init__(self,name,money): self.name = name self.__money = money #实际就是变形成了_Foo__money def tell_info(self): print(self.__money) #在内部可以调用外部不能调用, def __spam(self): print('testtesttest') f = Foo('alex',2000000000) #print(f.__money) #若直接调用封装了的money的参数会报错 #print(f.__dict__) #用__dict__可以具体查看变了形的结果,其实类就是将里面的数据变成了字典的形式(通过.来从字典里获取数据)。 f.tell_info() #这时就可以调用money的函数了 print(f.__dict__) #对比实例和类的字典 print(Foo.__dict__)
插一嘴,类在定义的时候会执行一遍类里的内容,变形只会在定义的时候发生一次,以后都不会变形了。
class Foo: print('》》》》》》》》》》') def __init__(self,name,money): self.name = name self.__money = money #实际就是变形成了_Foo__money f = Foo('alex',2000000000) print(f.__dict__) f.__x = 1 #新加一个变量 print(f.__dict__) print(f.__x) #然后就可以调用了
6.3 特性protected
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ #property作用就是将类的函数转换成类的属性,实例调用的时候就当做属性来用 #注意:property场景不能穿参数 # # import math # class Circle: # def __init__(self,radius): #圆的半径radius # self.radius=radius # # @property # def area(self): # return math.pi * self.radius**2 #计算面积 # # @property # def perimeter(self): # return 2*math.pi*self.radius #计算周长 # # c=Circle(10) # print(c.radius) # print(c.area) #可以向访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值 # print(c.perimeter) #同上 import math class Circle: def __init__(self,radius): self.radius =radius def area(self): return math.pi * self.radius**2 def perimeter(self): return 2*math.pi*self.radius area = property(area) perimeter = property(perimeter) c = Circle(10) #若想要计算员的周长和面积,对于使用者来说,必须调用两个函数 #print(c.area()) #print(c.perimeter()) #但是用了priperty后,就相当于将函数变成了变量,转换成了数据属性,对于使用者来说就是数据属性 print(c.area) print(c.perimeter)
为什么用protected?
将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则
python并没有在语法上把它们三个内建到自己的class机制中,在C++里一般会将所有的所有的数据都设置为私有的,然后提供set和get方法(接口)去设置和获取,在python中通过property方法可以实现
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ # # class A: # def __init__(self,name): # self.__name = name # # @property #类似c++ get功能 # def name(self): # return self.__name # # @name.setter #类似C++ set功能 # def name(self,value): # self.__name=value # # @name.deleter # def name(self): # print('------') # # a=A('lijun') # print(a.name) # # a.name=2 # print(a.name) # # del a.name #用处:假如我穿个参数,a.name = 111,但是我的姓名必须是字符串,增加了对属性操作的灵活性 class A: def __init__(self,name): self.__name = name @property #类似c++ get功能 def name(self): return self.__name @name.setter #类似C++ set功能 def name(self,value): if not isinstance(value,str): #假如不是字符串我就抛出个异常 raise TypeError('%s must be str' %value) self.__name=value @name.deleter def name(self): #不让删除的提示 print('------') raise AttributeError(' bu neng shan chu ') a=A('lijun') print(a.name) # a.name=111111 # print(a.name) del a.name
7.静态方法和类方法
通常情况下,在类中定义的所有函数(注意了,这里说的就是所有,跟self啥的没关系,self也只是一个再普通不过的参数而已)都是对象的绑定方法,对象在调用绑定方法时会自动将自己作为参数传递给方法的第一个参数。除此之外还有两种常见的方法:静态方法和类方法,二者是为类量身定制的,但是实例非要使用,也不会报错,后续将介绍。
7.1 静态方法
是一种普通函数,位于类定义的命名空间中,不会对任何实例类型进行操作,python为我们内置了函数staticmethod来把类中的函数定义成静态方法
class Foo: def spam(x,y,z): #类中的一个函数,千万不要懵逼,self和x啥的没有不同都是参数名 print(x,y,z) spam=staticmethod(spam) #把spam函数做成静态方法
基于之前所学装饰器的知识,@staticmethod 等同于spam=staticmethod(spam),于是
class Foo: @staticmethod #装饰器 def spam(x,y,z): print(x,y,z)
使用演示
print(type(Foo.spam)) #类型本质就是函数 Foo.spam(1,2,3) #调用函数应该有几个参数就传几个参数 f1=Foo() f1.spam(3,3,3) #实例也可以使用,但通常静态方法都是给类用的,实例在使用时丧失了自动传值的机制 ''' <class 'function'> 1 2 3 3 3 3 '''
应用场景:
import time class Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day @staticmethod def now(): #用Date.now()的形式去产生实例,该实例用的是当前时间 t=time.localtime() #获取结构化的时间格式 return Date(t.tm_year,t.tm_mon,t.tm_mday) #新建实例并且返回 @staticmethod def tomorrow():#用Date.tomorrow()的形式去产生实例,该实例用的是明天的时间 t=time.localtime(time.time()+86400) return Date(t.tm_year,t.tm_mon,t.tm_mday) a=Date('1987',11,27) #自己定义时间 b=Date.now() #采用当前时间 c=Date.tomorrow() #采用明天的时间 print(a.year,a.month,a.day) print(b.year,b.month,b.day) print(c.year,c.month,c.day)
7.2 类方法
类方法是给类用的,类在使用时会将类本身当做参数传给类方法的第一个参数,python为我们内置了函数classmethod来把类中的函数定义成类方法
#类方法 # class A: # x=1 # @classmethod # def test(cls): # print(cls,cls.x) #cls就是类A # # A.test() #补充 # class A: # def __init__(self,name): # self.name = name # # def __str__(self): # return '%s' %self.name # # a=A('lijun') # print(a) #如果不定义__str__内置函数,那么打印的是a的内存地址 #类方法的应用场景,相比静态方法,比较灵活 import time class Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day @classmethod def now(cls): t=time.localtime() return cls(t.tm_year,t.tm_mon,t.tm_mday) d1=Date.now() print(d1.year,d1.month,d1.day)
两种类的区别和用处:
import time class Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day @staticmethod def now(): t=time.localtime() return Date(t.tm_year,t.tm_mon,t.tm_mday) class EuroDate(Date): def __str__(self): return 'year:%s month:%s day:%s' %(self.year,self.month,self.day) e=EuroDate.now() print(e) #我们的意图是想触发EuroDate.__str__,但是结果为 ''' 输出结果: <__main__.Date object at 0x1013f9d68> '''
因为e就是用Date类产生的,所以根本不会触发EuroDate.__str__,解决方法就是用classmethod
import time class Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day # @staticmethod # def now(): # t=time.localtime() # return Date(t.tm_year,t.tm_mon,t.tm_mday) @classmethod #改成类方法 def now(cls): t=time.localtime() return cls(t.tm_year,t.tm_mon,t.tm_mday) #哪个类来调用,即用哪个类cls来实例化 class EuroDate(Date): def __str__(self): return 'year:%s month:%s day:%s' %(self.year,self.month,self.day) e=EuroDate.now() print(e) #我们的意图是想触发EuroDate.__str__,此时e就是由EuroDate产生的,所以会如我们所愿 ''' 输出结果: year:2017 month:3 day:3 '''
强调,注意注意注意:静态方法和类方法虽然是给类准备的,但是如果实例去用,也是可以用的,只不过实例去调用的时候容易让人混淆,不知道你要干啥
x=e.now() #通过实例e去调用类方法也一样可以使用,静态方法也一样 print(x) ''' 输出结果: year:2017 month:3 day:3 '''
8.面向对象的软件开发
http://www.cnblogs.com/linhaifeng/articles/6182264.html#_label27
9.反射
hasattr(容器,‘名称’) #以字符串的形式判断某个对象中是否含有指定的属性
getattr(容器,‘名称’) #以字符串的形式去某个对象中获取指定的属性
setattr(容器,‘名称’,值) #以字符串的形式去某个对象中设置指定的属性
delattr(容器,‘名称’) #以字符串的形式去某个对象中删除指定的属性
案例1:当前目录下有个test目录,test目录下有个account的模块,输入不同的路径,执行不同的结果,但是我的这些功能都在统一的目录下
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ #案例:假如有一个网站,输入不同的路径,执行不同的结果,但是我的这些功能都在统一的目录下 #用户输入:account/login acccount/logout li = ['account/login','acccount/logout'] inp = input('》》》') m,n = inp.split('/') import sys,os sys.path.append(r'D:\pycharm\s16\day7\反射\test') import account if inp in li: if n == "login": res = account.login() elif n == 'logout': res = account.logout() else: print('没有这个功能。。。') print(res)
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ def login(): return '请输入用户名和密码' def logout(): return '跳转页面'
案例2:以上的方法用反射来实现,getattr()
#反射:getattr() 专门去某个地方获取他的内部的东西,PS:获取的东西是字符串的形式 #用反射去实现以上的内容 import sys,os sys.path.append(r'D:\pycharm\s16\day7\反射\test') import account # v=getattr(account,'login') # print(v) action = input('>>') v = getattr(account,action) result = v() print(result)
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ def login(): return '请输入用户名和密码' def logout(): return '跳转页面'
C:\Python35\python3.exe D:/pycharm/s16/day7/反射/反射.py >>login 请输入用户名和密码
案例3:hasattr()函数的用法,先判断我这个模块里有没有函数,有的话就执行,没有就报404
#反射:hasattr() 专门去某个地方获取他的内部的东西,PS:获取的东西是字符串的形式 import sys,os sys.path.append(r'D:\pycharm\s16\day7\反射\test') import account action = input('>>') # a = hasattr(account,action) # print(a) #假如account文件里有action那么就输出True否则false if(hasattr(account,action)): func = getattr(account,action) result = func() else: result = '404' print(result)
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ def login(): return '请输入用户名和密码' def logout(): return '跳转页面'
C:\Python35\python3.exe D:/pycharm/s16/day7/反射/反射.py >>aaa 404 C:\Python35\python3.exe D:/pycharm/s16/day7/反射/反射.py >>logout 跳转页面
案例4:
#setattr和delattr的用法 import sys,os sys.path.append(r'D:\pycharm\s16\day7\反射\test') import account h = hasattr(account,'findpwd') print(h) setattr(account,'findpwd',lambda x:x+1) #增加findpwd函数 h = hasattr(account,'findpwd') #这时值就为True print(h) delattr(account,'findpwd') #删除 h = hasattr(account,'findpwd') print(h)
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ def login(): return '请输入用户名和密码' def logout(): return '跳转页面'
C:\Python35\python3.exe D:/pycharm/s16/day7/反射/反射.py
False
True
False
案例5:深入运用,假如同一个目录下有好的功能存在不同的模块中(环境是在包中演示)
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ #以字符串的形式导入模块 #import time #v = __import__('time') while True: # account/login # home/index inp=input('请输入url:') m,n = inp.split('/') module = __import__('controller.%s' %m,fromlist=True) # print(module) if hasattr(module,n): func = getattr(module,n) result = func() else: print('404') print(result)
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ def login(): print('longin......') return 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' def logout(): print('longout......') return 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ def index(): print('aaaaaa') return '这个是index函数' print('honme..........')
C:\Python35\python3.exe D:/pycharm/s16/day7/反射_package/反射.py 请输入url:home/index honme.......... aaaaaa 这个是index函数 请输入url:account/login longin...... aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 请输入url:account/laaaaaaaaaaaaaaaaaaaaaaaaa 404 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 请输入url:
案例6:继续优化脚本,假如输入没有的模块,也报错,但是程序不退出
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ #以字符串的形式导入模块 #import time #v = __import__('time') while True: # account/login # home/index inp=input('请输入url:') m,n = inp.split('/') try: #假如导入没有的模块,就报401 module = __import__('controller.%s' %m,fromlist=True) # print(module) if hasattr(module,n): func = getattr(module,n) result = func() else: print('404') except Exception as e: result = 401 print(result)
C:\Python35\python3.exe D:/pycharm/s16/day7/反射_package/反射.py 请输入url:sss/sssss 401 请输入url:
10.类的特殊成员方法:
1.__doc__ 表示类的描述信息
class Foo: """ 描述类信息,这是用于看片的神奇 """ def func(self): pass print(Foo.__doc__) # 输出: 描述类信息,这是用于看片的神奇
2.__module__ 和 __class__
__module__ 表示当前操作的对象在那个模块
__class__ 表示当前操作的对象的类是什么
class C: def __init__(self): self.name = 'wupeiqi'
from lib.aaa import C obj = C() print(obj.__module__) # 输出 lib.aaa,即:输出模块,字符串的形式 print(obj.__class__ ) # 输出 lib.aa.C,即:输出类
3. __call__ 对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print '__call__' obj = Foo() # 执行 __init__ obj() # 执行 __call__
4. __new__ \ __metaclass__
class Foo(object): def __init__(self, name): self.name = name f = Foo("alex") print(type(f)) # 输出:<class '__main__.Foo'> 表示,obj 对象由Foo类创建 print(type(Foo)) # 输出:<type 'type'> 表示,Foo类对象都是由 type 类创建
上述代码中,obj 是通过 Foo 类实例化的对象,其实,不仅 obj 是一个对象,Foo类本身也是一个对象,因为在Python中一切事物都是对象。
如果按照一切事物都是对象的理论:obj对象是通过执行Foo类的构造方法创建,那么Foo类对象应该也是通过执行某个类的 构造方法 创建。
所以,f对象是Foo类的一个实例,Foo类对象是 type 类的一个实例,即:Foo类对象 是通过type类的构造方法创建。
那么,创建类就可以有两种方式:
a)普通方式:
class Foo(object): def sayhi(self,name): print('hello ', name)
b)特殊方式:
def sayhi(self,name): print('hello ',name) f= type('Foo', (object,), {'func': sayhi}) # type第一个参数:类名 # type第二个参数:当前类的基类 # type第三个参数:类的成员 # # 上面f=的格式就和普通格式一样 print(f) print(type(f)) print(dir(f)) #查看类名下有哪些函数 #f.func('ddd') #若想要使用sayhi的函数 f_obj=f() #实例化 f_obj.func('lijun') #调用函数
def func(self): print("hello %s"%self.name) def __init__(self,name,age): self.name = name self.age = age Foo = type('Foo',(object,),{'func':func,'__init__':__init__}) f = Foo("jack",22) f.func()
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ # class Foo(object): # def __init__(self, name): # self.name = name # print('Foo __init__') # # def __new__(cls, *args, **kwargs): #init的触发都是由new来实现的,原来默认里有个new,你在写new方法会顶替 # print('Foo __new__') # # f = Foo("alex") #这时实例化后显示的不是'Foo __initclass Foo(object): #利用new方法实现调用init #new只是生成了一块内存空间,并没有赋值,交给init赋值 class Foo(object): def __init__(self, name): self.name = name print('Foo __init__') def __new__(cls, *args, **kwargs): #整个类的实例化其实都是通过new来实现的 print('Foo __new__') obj = object.__new__(cls) print('obj:::',obj) return obj f = Foo("alex") print(f.name)
1 class MyType(type): 2 def __init__(self,*args,**kwargs): 3 4 print("Mytype __init__",*args,**kwargs) 5 6 def __call__(self, *args, **kwargs): 7 print("Mytype __call__", *args, **kwargs) 8 obj = self.__new__(self) 9 print("obj ",obj,*args, **kwargs) 10 print(self) 11 self.__init__(obj,*args, **kwargs) 12 return obj 13 14 def __new__(cls, *args, **kwargs): 15 print("Mytype __new__",*args,**kwargs) 16 return type.__new__(cls, *args, **kwargs) 17 18 print('here...') 19 class Foo(object,metaclass=MyType): 20 21 22 def __init__(self,name): 23 self.name = name 24 25 print("Foo __init__") 26 27 def __new__(cls, *args, **kwargs): 28 print("Foo __new__",cls, *args, **kwargs) 29 return object.__new__(cls) 30 31 f = Foo("Alex") 32 print("f",f) 33 print("fname",f.name)
动态导入模块
import importlib __import__('import_lib.metaclass') #这是解释器自己内部用的 #importlib.import_module('import_lib.metaclass') #与上面这句效果一样,官方建议用这个
11.异常处理
http://www.cnblogs.com/wupeiqi/articles/5017742.html
1.异常基础
案例1:最简单的语法,假如一个程序出错了,不是立马就报错退出程序,下面的程序还会执行
#打印一个不存在的变量 try: print(name) except NameError as e: print(e) print('keep going.....') #输出 name 'name' is not defined #抓到的错误信息 keep going.....
2.异常种类
python中的异常种类非常多,每个异常专门用于处理某一项异常!!!
AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x IOError 输入/输出异常;基本上是无法打开文件 ImportError 无法引入模块或包;基本上是路径问题或名称错误 IndentationError 语法错误(的子类) ;代码没有正确对齐 IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5] KeyError 试图访问字典里不存在的键 KeyboardInterrupt Ctrl+C被按下 NameError 使用一个还未被赋予对象的变量 SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了) TypeError 传入对象类型与要求的不符合 UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量, 导致你以为正在访问它 ValueError 传入一个调用者不期望的值,即使值的类型是正确的
ArithmeticError
AssertionError
AttributeError
BaseException
BufferError
BytesWarning
DeprecationWarning
EnvironmentError
EOFError
Exception
FloatingPointError
FutureWarning
GeneratorExit
ImportError
ImportWarning
IndentationError
IndexError
IOError
KeyboardInterrupt
KeyError
LookupError
MemoryError
NameError
NotImplementedError
OSError
OverflowError
PendingDeprecationWarning
ReferenceError
RuntimeError
RuntimeWarning
StandardError
StopIteration
SyntaxError
SyntaxWarning
SystemError
SystemExit
TabError
TypeError
UnboundLocalError
UnicodeDecodeError
UnicodeEncodeError
UnicodeError
UnicodeTranslateError
UnicodeWarning
UserWarning
ValueError
Warning
ZeroDivisionError
names = ['a','b'] try: print(name) #走到这里就报错了,下面的就不执行了,直接跳到except print(names[3]) except NameError as e: print(e) print('keep going.....')
#可以抓到其中一个错误,但是不能同时抓住 names = ['a','b'] try: print(names[3]) print(name) except NameError as e: print(e) except IndexError as e: print(e) print('keep going.....')
#可以抓到其中一个错误,但是不能同时抓住 names = ['a','b'] try: print(names[3]) print(name) except (NameError,IndexError) as e: print(e) except IndexError as e: print(e) print('keep going.....')
备注:IndentationError和SyntaxError的错误信息是抓不到的,因为解释器根本就执行不了这两类的代码。
#Exception万能异常,一般不用,不方便调试 names = ['a','b'] try: print(names[3]) print(name) except Exception as e: print(e) print('keep going.....')
#else的用法 names = ['a','b'] try: print(names[1]) # print(name) except IndexError as e: print(e) except Exception as e: print(e) else: print('什么错都没有,就会执行')
#finally的用法 names = ['a','b'] try: print(names[1]) print(name) except IndexError as e: print(e) except Exception as e: print(e) else: print('什么错都没有,就会执行') finally: print('无论有没有错都执行')
3.自定义异常
class WupeiqiException(Exception): def __init__(self, msg): self.message = msg def __str__(self): return self.message try: raise WupeiqiException('我的异常') except WupeiqiException,e: print e
4.断言
# assert 条件 assert 1 == 1 assert 1 == 2