一 面向对象的程序设计的由来
二 什么是面向对象的程序设计及为何要有它
三 类和对象
3.1 什么是对象,什么是类
3.2 类相关知识
3.3 对象相关知识
3.4 对象之间的交互
3.5 类名称空间与对象/实例名称空间
3.6 小结
四 继承与派生
4.1 什么是继承
4.2 什么是派生
4.3 继承与抽象(先抽象再继承)
4.4 继承与重用性
4.5 组合与重用性
4.6 接口与归一化设计
4.7 抽象类
4.8 继承实现的原理(继承顺序)
4.9 子类中调用父类方法
五 多态与多态性
5.1 多态
5.2 多态性
六 封装
6.1 要封装什么
6.2 为何要封装
6.3 封装分为两个层面
6.4 特性(property)
6.5 封装与扩展性
七 静态方法和类方法
八 面向对象的软件开发
九 小白容易犯的错误
十 python中关于OOP的经常使用术语html
参考林海峰老师总结: http://www.cnblogs.com/linhaifeng/articles/6428835.htmljava
面向过程的程序设计的核心是过程(流水线式思惟),过程即解决问题的步骤,面向过程的设计就比如精心设计好一条流水线,考虑周全何时处理什么东西。python
优势: 极大的下降了程序的复杂度。linux
缺点: 一套流水线或者流程就是用来解决一个问题,生产汽水的流水线没法生产汽车,即使是能,也得是大改,改一个组件,牵一发而动全身。c++
应用场景: 一旦完成基本不多改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等。git
面向对象的程序设计的核心是对象(上帝式思惟),要理解对象为什么物,必须把本身当成上帝,上帝眼里世间存在的万物皆为对象,不存在的也能够创造出来。面向对象的程序设计比如如来设计西游记,如来要解决的问题是把经书传给东土大唐,如来想了想解决这个问题须要四我的:唐僧,沙和尚,猪八戒,孙悟空,每一个人都有各自的特征和技能(这就是对象的概念,特征和技能分别对应对象的数据属性和方法属性),然而这并很差玩,因而如来又安排了一群妖魔鬼怪,为了防止师徒四人在取经路上被搞死,又安排了一群神仙保驾护航,这些都是对象。而后取经开始,师徒四人与妖魔鬼怪神仙交互着直到最后取得真经。如来根本不会管师徒四人按照什么流程去取。程序员
优势: 解决了程序的扩展性。对某一个对象单独修改,会马上反映到整个体系中,如对游戏中一我的物参数的特征和技能修改都很容易。github
缺点: 可控性差,没法向面向过程的程序设计流水线式的能够很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即使是上帝也没法预测最终结果。因而咱们常常看到一个游戏人某一参数的修改极有可能致使阴霸的技能出现,一刀砍死3我的,这个游戏就失去平衡。算法
应用场景: 需求常常变化的软件,通常需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方编程
面向对象的程序设计并非所有。对于一个软件质量来讲,面向对象的程序设计只是用来解决扩展性。
提示: python的class术语与c++有必定区别,与 Modula-3更像。
python中一切皆为对象,且python3统一了类与类型的概念,类型就是类,因此,无论你信不信,你已经使用了很长时间的类了
>>> dict #类型dict就是类dict <class 'dict'> >>> d=dict(name='shuke') #实例化 >>> d.pop('name') #向d发一条消息,执行d的方法pop 'shuke'
基于面向对象设计一个款游戏:英雄联盟,每一个玩家选一个英雄,每一个英雄都有本身的特征和和技能,特征即数据属性,技能即方法属性,特征与技能的结合体就是一个对象。
从一组对象中提取类似的部分就是类,类是全部对象都具备的特征和技能的结合体。
注: 在python中,用变量表示特征,用函数表示技能,于是类是变量与函数的结合体,对象是变量与方法(类中的函数)的结合体.
在python中声明函数与声明类很类似
声明函数
def functionName(args): '函数文档字符串' 函数体
声明类
''' 语法格式: class 类名: '类的文档字符串' 类体 ''' #咱们建立一个类 class Data: pass
前提: 1.只有在python2中才分新式类和经典类,python3中统一都是新式类 2.新式类和经典类声明的最大不一样在于,全部新式类必须继承至少一个父类 3.全部类无论是否显式声明父类,都有一个默认继承object父类 在python2中的区分 经典类: class 类名: pass 新式类: class 类名(父类): pass 在python3中,上述两种定义方式全都是新式类
类是数据与函数的结合体,两者称为类的属性
class Chinese: country='China' def __init__(self,name,age,sex): self.Name=name # 类的数据属性 self.Age=age self.Sex=sex def talk(self): # 类的函数属性 print('talking',self)
例如:
class Garen: #定义英雄盖伦的类,不一样的玩家能够用它实例出本身英雄; camp='Demacia' #全部玩家的英雄(盖伦)的阵营都是Demacia; def attack(self,enemy): #普通攻击技能,enemy是敌人; enemy.life_value-=self.aggressivity #根据本身的攻击力,攻击敌人就减掉敌人的生命值。
1. 属性引用(类名.属性)
>>> Garen.camp #引用类的数据属性,该属性与全部对象/实例共享 'Demacia' >>> Garen.attack #引用类的函数属性,该属性也共享 <function Garen.attack at 0x101356510> >>> Garen.name='Garen' #增长属性 >>> del Garen.name #删除属性
2. 实例化
类名加括号就是实例化,会自动触发__init__函数的运行,能够用它来为每一个对象实例定制本身的特征
class Garen: #定义英雄盖伦的类,不一样的玩家能够用它实例出本身英雄; camp='Demacia' #全部玩家的英雄(盖伦)的阵营都是Demacia; def __init__(self,nickname,aggressivity=58,life_value=455): #英雄的初始攻击力58...; self.nickname=nickname #为本身的盖伦起个别名; self.aggressivity=aggressivity #英雄都有本身的攻击力; self.life_value=life_value #英雄都有本身的生命值; def attack(self,enemy): #普通攻击技能,enemy是敌人; enemy.life_value-=self.aggressivity #根据本身的攻击力,攻击敌人就减掉敌人的生命值。
实例化:类名+括号
>>> g1=Garen('草丛伦') #就是在执行Garen.__init__(g1,'草丛伦'),而后执行__init__内的代码g1.nickname=‘草丛伦’等
self的做用: 在实例化的同时自动将对象/实例自己传给__init__的第一个参数,self能够是任意名字,建议使用self,编码规范。
# 实例化 class Chinese: country = 'China' # 类变量 def __init__(self, name, age, sex): # Chinese.__init__(p1,'shuke','18','male') self.Name = name # p1.Name=name; p1.Age=age; p1.Sex=sex self.Age = age self.Sex = sex def talk(self): print('%s is talking' % self.Name) # 实例化 p1 = Chinese('shuke', '18', 'male') # Chinese.__init__(p1,'shuke','18','male') p2 = Chinese('jack', '25', 'female') # Chinese.__init__(p2,'jack','25','female')
3.类属性的补充
1. 咱们定义的类的属性到底存到哪里了?有两种方式查看 dir(类名):查出的是一个名字列表 类名.__dict__:查出的是一个字典,key为属性名,value为属性值 2. 特殊的类属性 类名.__name__# 类的名字(字符串) 类名.__doc__# 类的文档字符串 类名.__base__# 类的第一个父类 类名.__bases__# 类全部父类构成的元组 类名.__dict__# 类的字典属性 类名.__module__# 类定义所在的模块 类名.__class__# 实例对应的类(仅新式类中)
对象是关于类而实际存在的一个例子,即实例,类实例化以后获得的就是一个具体的对象。
>>> g1=Garen('草丛伦') #类实例化获得g1这个实例,基于该实例咱们讲解实例相关知识 >>> type(g1) #查看g1的类型就是类Garen <class '__main__.Garen'> >>> isinstance(g1,Garen) #g1就是Garen的实例 True
#对象/实例自己其实只有数据属性 >>> g1.nickname '草丛伦' >>> g1.aggressivity >>> g1.life_value ''' 查看实例属性 一样是dir和内置__dict__两种方式 特殊实例属性 __class__ __dict__ .... '''
对象/实例自己只有数据属性,可是python的class机制会将类的函数绑定到对象上,称为对象的方法,或者叫绑定方法,绑定方法惟一绑定一个对象,同一个类的方法绑定到不一样的对象上,属于不一样的方法,会存在于不一样的内存地址中...
>>> g1.attack #对象的绑定方法 <bound method Garen.attack of <__main__.Garen object at 0x101348dd8>> >>> Garen.attack #对象的绑定方法attack本质就是调用类的函数attack的功能,两者是一种绑定关系 <function Garen.attack at 0x101356620>
对象的绑定方法的特别之处: obj.func()会把obj传给func的第一个参数。
建立一个Riven类
class Riven: camp='Noxus' #全部玩家的英雄(锐雯)的阵营都是Noxus; def __init__(self,nickname,aggressivity=54,life_value=414): #英雄的初始攻击力54; self.nickname=nickname #为本身的锐雯起个别名; self.aggressivity=aggressivity #英雄都有本身的攻击力; self.life_value=life_value #英雄都有本身的生命值; def attack(self,enemy): #普通攻击技能,enemy是敌人; enemy.life_value-=self.aggressivity #根据本身的攻击力,攻击敌人就减掉敌人的生命值。
实例一个Riven对象
>>> r1=Riven('锐雯雯')
交互:锐雯雯攻击草丛伦,反之同样
>>> g1.life_value 455 >>> r1.attack(g1) >>> g1.life_value 401
建立一个类就会建立一个类的名称空间,用来存储类中定义的全部名字,这些名字称为类的属性。
类有两种属性:
>>> id(r1.camp) #本质就是在引用类的camp属性,两者id同样 4315241024 >>> id(Riven.camp) 4315241024
而类的函数属性是绑定到全部对象的:
>>> id(r1.attack) >>> id(Riven.attack) ''' r1.attack就是在执行Riven.attack的功能,python的class机制会将Riven的函数属性attack绑定给r1,r1至关于拿到了一个指针,指向Riven类的attack功能 除此以外r1.attack()会将r1传给attack的第一个参数 '''
建立一个对象/实例就会建立一个对象/实例的名称空间,存放对象/实例的名字,称为对象/实例的属性。
obj.name会先从obj本身的名称空间里查找name,找不到则去类中查找name,类也找不到就找父类...最后都找不到就抛出异常 。
定义瑞雯类:
定义一个瑞雯类:
class Riven: camp='Noxus' def __init__(self,nickname, aggressivity=54, life_value=414, money=1001, armor=3): self.nickname=nickname self.aggressivity=aggressivity self.life_value=life_value self.money=money self.armor=armor def attack(self,enemy): damage_value=self.aggressivity-enemy.armor enemy.life_value-=damage_value
定义一个盖伦类:
定义一个盖伦类:
class Garen: camp='Demacia' def __init__(self,nickname, aggressivity=58, life_value=455, money=100, armor=10): self.nickname=nickname self.aggressivity=aggressivity self.life_value=life_value self.money=money self.armor=armor def attack(self,enemy): damage_value=self.aggressivity-enemy.armor enemy.life_value-=damage_value
定义装备:
class BlackCleaver: def __init__(self,price=475,aggrev=9,life_value=100): self.price=price self.aggrev=aggrev self.life_value=life_value def update(self,obj): obj.money-=self.price #减钱 obj.aggressivity+=self.aggrev #加攻击 obj.life_value+=self.life_value #加生命值 def fire(self,obj): #这是该装备的主动技能,喷火,烧死对方 obj.life_value-=1000 #假设火烧的攻击力是1000
类之间的交互:
r1=Riven('草丛伦') g1=Garen('盖文') b1=BlackCleaver() print(r1.aggressivity,r1.life_value,r1.money) #r1的攻击力,生命值,护甲 if r1.money > b1.price: r1.b1=b1 b1.update(r1) print(r1.aggressivity,r1.life_value,r1.money) #r1的攻击力,生命值,护甲 print(g1.life_value) r1.attack(g1) #普通攻击 print(g1.life_value) r1.b1.fire(g1) #用装备攻击 print(g1.life_value) #g1的生命值小于0就死了
继承是一种建立新类的方式,在python中,新建的类能够继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类。
python中类的继承分为:单继承和多继承
class ParentClass1: #定义父类 pass class ParentClass2: #定义父类 pass class SubClass(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass pass class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类 pass
查看继承关系
>>> SubClass1.__bases__ #__base__只查看从左到右继承的第一个子类,__bases__则是查看全部继承的父类 (<class '__main__.ParentClass1'>,) >>> SubClass2.__bases__ (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
提示:若是没有指定基类,python的类会默认继承object类,object是全部python类的基类,它提供了一些常见方法(如__str__)的实现。
>>> ParentClass1.__bases__ (<class 'object'>,) >>> ParentClass2.__bases__ (<class 'object'>,)
继承的优势:
在子类定义新的属性,覆盖掉父类的属性,称为派生。
class Animal: def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex def talk(self): print('%s 正在叫' %self.name) class People(Animal): # 继承了Animal类 def __init__(self, name, age, sex,education): Animal.__init__(self,name,age,sex) self.education=education def talk(self): # 子类覆盖了父类的talk方法,即派生 Animal.talk(self) print('%s say hello' %self.name)
抽象即抽取相似或者说提取共同存在的部分。
抽象分红两个层次:
1.将奥巴马和梅西两个对象共同的部分抽取成类,好比:会说话,能够吃饭,男人等等一系列特征...
2.将人,猪,狗这三个对象共同的部分抽取成父类。
抽象最主要的做用是划分类别(能够隔离关注点,下降复杂度)
继承: 基于抽象的结果,经过编程语言去实现它,确定是先经历抽象这个过程,才能经过继承的方式去表达出抽象的结构。
抽象只是分析和设计的过程当中,一个动做或者说一种技巧,经过抽象能够获得类。
在开发程序的过程当中,若是咱们定义了一个类A,而后又想新创建另一个类B,可是类B的大部份内容与类A的相同时
咱们不可能从头开始写一个类B,这就用到了类的继承的概念。
经过继承的方式新建类B,让B继承A,B会‘遗传’A的全部属性(数据属性和函数属性),实现代码重用
==========================第一部分 例如 猫能够:喵喵叫、吃、喝、拉、撒 狗能够:汪汪叫、吃、喝、拉、撒 若是咱们要分别为猫和狗建立一个类,那么就须要为 猫 和 狗 实现他们全部的功能,伪代码以下: #猫和狗有大量相同的内容 class 猫: def 喵喵叫(self): print '喵喵叫' def 吃(self): # do something def 喝(self): # do something def 拉(self): # do something def 撒(self): # do something class 狗: def 汪汪叫(self): print '喵喵叫' def 吃(self): # do something def 喝(self): # do something def 拉(self): # do something def 撒(self): # do something ==========================第二部分 上述代码不难看出,吃、喝、拉、撒是猫和狗都具备的功能,而咱们却分别的猫和狗的类中编写了两次。若是使用 继承 的思想,以下实现: 动物:吃、喝、拉、撒 猫:喵喵叫(猫继承动物的功能) 狗:汪汪叫(狗继承动物的功能) 伪代码以下: class 动物: def 吃(self): # do something def 喝(self): # do something def 拉(self): # do something def 撒(self): # do something # 在类后面括号中写入另一个类名,表示当前类继承另一个类 class 猫(动物): def 喵喵叫(self): print '喵喵叫' # 在类后面括号中写入另一个类名,表示当前类继承另一个类 class 狗(动物): def 汪汪叫(self): print '喵喵叫' ==========================第三部分 #继承的代码实现 class Animal: def eat(self): print("%s 吃 " %self.name) def drink(self): print ("%s 喝 " %self.name) def shit(self): print ("%s 拉 " %self.name) def pee(self): print ("%s 撒 " %self.name) class Cat(Animal): def __init__(self, name): self.name = name self.breed = '猫' def cry(self): print('喵喵叫') class Dog(Animal): def __init__(self, name): self.name = name self.breed='狗' def cry(self): print('汪汪叫') # ######### 执行 ######### c1 = Cat('小白家的小黑猫') c1.eat() c2 = Cat('小黑的小白猫') c2.drink() d1 = Dog('胖子家的小瘦狗') d1.eat() 使用继承来重用代码比较好的例子
class Hero: def __init__(self,nickname,aggressivity,life_value): self.nickname=nickname self.aggressivity=aggressivity self.life_value=life_value def move_forward(self): print('%s move forward' %self.nickname) def move_backward(self): print('%s move backward' %self.nickname) def move_left(self): print('%s move forward' %self.nickname) def move_right(self): print('%s move forward' %self.nickname) def attack(self,enemy): enemy.life_value-=self.aggressivity class Garen(Hero): pass class Riven(Hero): pass g1=Garen('草丛伦',100,300) r1=Riven('锐雯雯',57,200) print(g1.life_value) r1.attack(g1) print(g1.life_value) ''' 运行结果 243 '''
提示: 使用已经建立的的类创建一个新的类,这样就重用了已有类的代码中的一大部分功能,大大减小了编程工做量,这就是常说的软件重用,不只能够重用本身的类,也能够继承别人的,好比标准库,来定制新的数据类型,这样就是大大缩短了软件开发周期,对大型软件开发来讲,意义重大.
注意: 像g1.life_value之类的属性引用,会先从实例中找life_value而后去类中找,而后再去父类中找...直到最顶级的父类。
固然子类也能够添加本身新的属性或者在本身的类体中从新定义这些属性(不会影响到父类),须要注意的是,一旦从新定义了本身的属性且与父类重名,那么调用新增的属性时,就以本身为准了。
class Riven(Hero): camp='Noxus' def attack(self,enemy): #在子类中定义新的attack,再也不使用父类的attack,且不会影响父类 print('from riven') def fly(self): #在子类中定义新的 print('%s is flying' %self.nickname)
在子类中,新建的重名的函数属性,在编辑函数内功能的时候,有可能须要重用父类中重名的那个函数功能,应该是用调用普通函数的方式,即:类名.func(),此时就与调用普通函数无异了,所以即使是self参数也要为其传值。
class Riven(Hero): camp='Noxus' def __init__(self,nickname,aggressivity,life_value,skin): Hero.__init__(self,nickname,aggressivity,life_value) #调用父类功能 self.skin=skin #新属性 def attack(self,enemy): #在本身这里定义新的attack,再也不使用父类的attack,且不会影响父类 Hero.attack(self,enemy) #调用功能 print('from riven') def fly(self): #在本身这里定义新的 print('%s is flying' %self.nickname) r1=Riven('锐雯雯',57,200,'比基尼') r1.fly() print(r1.skin) ''' 运行结果 锐雯雯 is flying 比基尼 '''
软件重用的重要方式除了继承以外还有另一种方式,即:组合
组合: 指的是,在一个类中以另一个类的对象做为数据属性,称为类的组合
>>> class Equip: #武器装备类 ... def fire(self): ... print('release Fire skill') ... >>> class Riven: #英雄Riven的类,一个英雄须要有装备,于是须要组合Equip类 ... camp='Noxus' ... def __init__(self,nickname): ... self.nickname=nickname ... self.equip=Equip() #组合的使用: 用Equip类产生一个装备,赋值给实例的equip属性 ... >>> r1=Riven('锐雯雯') >>> r1.equip.fire() # 使用组合的类产生的对象所特有的方法 release Fire skill
注: 组合与继承都是有效地利用已有类的资源的重要方式,可是两者的概念和使用场景皆不一样。
经过继承创建了派生类与基类之间的关系,它们之间是一种'是'的关系,好比白马是马,人是动物,西瓜是水果....
当类之间有不少相同的功能,咱们能够提取这些共同的功能作成基类,此时使用继承比较好,好比教授是老师
class Teacher: def __init__(self,name,gender): self.name=name self.gender=gender def teach(self): print('teaching') class Professor(Teacher): # 继承Teacher类 pass p1=Professor('shuke','male') p1.teach() ''' 执行结果: teaching '''
用组合的方式创建了类与组合的类之间的关系,它是一种‘有’的关系,好比教授有生日,教授教python课程
class BirthDate: def __init__(self,year,month,day): self.year=year self.month=month self.day=day class Couse: def __init__(self,name,price,period): self.name=name self.price=price self.period=period class Teacher: def __init__(self,name,gender): self.name=name self.gender=gender def teach(self): print('teaching') class Professor(Teacher): def __init__(self,name,gender,birth,course): Teacher.__init__(self,name,gender) self.birth=birth self.course=course p1=Professor('shuke','male', BirthDate('1995','1','27'), # 组合类的使用 Couse('python','28000','4 months')) # 组合类的使用 print(p1.birth.year,p1.birth.month,p1.birth.day) print(p1.course.name,p1.course.price,p1.course.period) ''' 运行结果: 1 27 python 28000 4 months '''
class People: def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex class Date: def __init__(self,year,mon,day): self.year=year self.mon=mon self.day=day def tell(self): print('%s-%s-%s' %(self.year,self.mon,self.day)) class Teacher(People): def __init__(self,name,age,sex,salary,year,mon,day): self.name=name self.age=age self.sex=sex self.salary=salary self.birth=Date(year,mon,day) class Student(People): def __init__(self,name,age,sex,year,mon,day): self.name=name self.age=age self.sex=sex self.birth=Date(year,mon,day) t=Teacher('shuke',18,'male',3000,1995,12,31) t.birth.tell() ''' 执行结果: 1995-12-31 '''
注: 当类之间有显著不一样,而且较小的类是较大的类所须要的组件时,用组合比较好。
=================第一部分:Java 语言中的接口很好的展示了接口的含义: IAnimal.java /* * Java的Interface很好的体现了咱们前面分析的接口的特征: * 1)是一组功能的集合,而不是一个功能 * 2)接口的功能用于交互,全部的功能都是public,即别的对象可操做 * 3)接口只定义函数,但不涉及函数实现 * 4)这些功能是相关的,都是动物相关的功能,但光合做用就不适宜放到IAnimal里面了 */ package com.oo.demo; public interface IAnimal { public void eat(); public void run(); public void sleep(); public void speak(); } =================第二部分:Pig.java:猪”的类设计,实现了IAnnimal接口 package com.oo.demo; public class Pig implements IAnimal{ //以下每一个函数都须要详细实现 public void eat(){ System.out.println("Pig like to eat grass"); } public void run(){ System.out.println("Pig run: front legs, back legs"); } public void sleep(){ System.out.println("Pig sleep 16 hours every day"); } public void speak(){ System.out.println("Pig can not speak"); } } =================第三部分:Person2.java /* *实现了IAnimal的“人”,有几点说明一下: * 1)一样都实现了IAnimal的接口,但“人”和“猪”的实现不同,为了不太多代码致使影响阅读,这里的代码简化成一行,但输出的内容不同,实际项目中同一接口的同一功能点,不一样的类实现彻底不同 * 2)这里一样是“人”这个类,但和前面介绍类时给的类“Person”彻底不同,这是由于一样的逻辑概念,在不一样的应用场景下,具有的属性和功能是彻底不同的 */ package com.oo.demo; public class Person2 implements IAnimal { public void eat(){ System.out.println("Person like to eat meat"); } public void run(){ System.out.println("Person run: left leg, right leg"); } public void sleep(){ System.out.println("Person sleep 8 hours every dat"); } public void speak(){ System.out.println("Hellow world, I am a person"); } } =================第四部分:Tester03.java package com.oo.demo; public class Tester03 { public static void main(String[] args) { System.out.println("===This is a person==="); IAnimal person = new Person2(); person.eat(); person.run(); person.sleep(); person.speak(); System.out.println("\n===This is a pig==="); IAnimal pig = new Pig(); pig.eat(); pig.run(); pig.sleep(); pig.speak(); } } java中的interface
继承的两种用途:
class Interface:#定义接口Interface类来模仿接口的概念,python中压根就没有interface关键字来定义一个接口。 def read(self): #定接口函数read,未实现 pass def write(self): #定义接口函数write,未实现 pass class Txt(Interface): #文本,具体实现read和write def read(self): print('文本数据的读取方法') def write(self): print('文本数据的读取方法') class Sata(Interface): #磁盘,具体实现read和write def read(self): print('硬盘数据的读取方法') def write(self): print('硬盘数据的读取方法') class Process(Interface): def read(self): print('进程数据的读取方法') def write(self): print('进程数据的读取方法')
实践中,继承的第一种含义意义并非很大,甚至经常是有害的。由于它使得子类与基类出现强耦合。
继承的第二种含义很是重要。它又叫“接口继承”。
接口继承实质上是要求“作出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的全部对象”——这在程序设计上,叫作归一化。
归一化使得高层的外部使用者能够不加区分的处理全部接口兼容的对象集合——就好象linux的泛文件概念同样,全部东西均可以当文件处理,没必要关心它是内存、磁盘、网络仍是屏幕(固然,对底层设计者,固然也能够区分出“字符设备”和“块设备”,而后作出针对性的设计:细致到什么程度,视需求而定)。
在python中根本就没有一个叫作interface的关键字,上面的代码只是看起来像接口,其实并无起到接口的做用,子类彻底能够不用去实现接口 ,若是非要去模仿接口的概念,能够借助第三方模块:
http://pypi.python.org/pypi/zope.interface
twisted的twisted\internet\interface.py里使用zope.interface
文档: https://zopeinterface.readthedocs.io/en/latest/
设计模式: https://github.com/faif/python-patterns
接口是提取了一些函数共同的功能,能够把接口当作一个函数的集合。
在子类中去实现接口中定义的的函数。
这么作的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么全部的这些类产生的对象在使用时,从用法上来讲都同样。
归一化,让使用者无需关心对象的类是什么,只须要的知道这些对象都具有某些功能就能够了,这极大地下降了使用者的使用难度。
好比: 咱们定义一个动物接口,接口里定义了有跑、吃、呼吸等接口函数,这样老鼠的类去实现了该接口,松鼠的类也去实现了该接口,由两者分别产生一只老鼠和一只松鼠送到你面前,即使是你分别不到底哪只是什么鼠你确定知道他俩都会跑,都会吃,都能呼吸。
再好比: 咱们有一个汽车接口,里面定义了汽车全部的功能,而后由本田汽车的类,奥迪汽车的类,大众汽车的类,他们都实现了汽车接口,这样就好办了,你们只须要学会了怎么开汽车,那么不管是本田,仍是奥迪,仍是大众咱们都会开了,开的时候根本无需关心我开的是哪一类车,操做手法(函数调用)都同样。
与java同样,python也有抽象类的概念可是一样须要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化。
若是说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。
好比咱们有香蕉的类,有苹果的类,有桃子的类,从这些类抽取相同的内容就是水果这个抽象的类,你吃水果时,要么是吃一个具体的香蕉,要么是吃一个具体的桃子。。。。。。你永远没法吃到一个叫作水果的东西。
从设计角度去看,若是类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。
从实现角度来看,抽象类与普通类的不一样之处在于:抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点相似,但实际上是不一样的。
注: 抽象方法是基类中定义的方法,但却没有任何实现。
#!/usr/bin/env python #-*- coding:utf-8 -*- __author__ = 'Shuke' #一切皆文件 import abc #利用abc模块实现抽象类 class All_file(metaclass=abc.ABCMeta): all_type='file' @abc.abstractmethod #定义抽象方法,无需实现功能 def read(self): '子类必须定义读功能' pass @abc.abstractmethod #定义抽象方法,无需实现功能 def write(self): '子类必须定义写功能' pass # 验证一 # class Txt(All_file): # pass # # t1=Txt() #报错,子类没有定义抽象方法 # 验证二 class Txt(All_file): #子类继承抽象类,可是必须定义read和write方法 def read(self): print('文本数据的读取方法') def write(self): print('文本数据的读取方法') class Sata(All_file): #子类继承抽象类,可是必须定义read和write方法 def read(self): print('硬盘数据的读取方法') def write(self): print('硬盘数据的读取方法') class Process(All_file): #子类继承抽象类,可是必须定义read和write方法 def read(self): print('进程数据的读取方法') def write(self): print('进程数据的读取方法') wenbenwenjian=Txt() # 实例化 yingpanwenjian=Sata() jinchengwenjian=Process() #这样你们都是被归一化了,也就是一切皆文件的思想 wenbenwenjian.read() yingpanwenjian.write() jinchengwenjian.read() print(wenbenwenjian.all_type) print(yingpanwenjian.all_type) print(jinchengwenjian.all_type) ''' 执行结果: 文本数据的读取方法 硬盘数据的读取方法 进程数据的读取方法 file file file '''
抽象类的本质仍是类,指的是一组类的类似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的类似性。
抽象类是一个介于类和接口之间的一个概念,同时具有类和接口的部分特性,能够用来实现归一化设计。
1. python中的类能够继承多个类,java和c#中则只能继承一个类。
2. python中的类若是继承了多个类,那么其寻找方法的方式有两种,分别是深度优先和广度优先。
注:
经典类和新式类,从字面上面能够看出一个新一个旧,新的必然包含了更多的功能,也是以后推荐的用法,从写法上区分,若是当前类或者父类继承了object类,那么当前类即是新式类,不然即是经典类。
pyhon2中才分新式类与经典类。python3中统一都是新式类。
class C1: # C1是经典类 pass class C2(C1): # C2是经典类 pass
class N1(object): # N1是新式类 pass class N2(N1): # N2是新式类 pass
广度优先的继承顺序
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会按照列表里面类的排列顺序进行查找
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是经过一个C3线性化算法来实现的。咱们不去深究这个算法的数学原理,它实际上就是合并全部父类的MRO列表并遵循以下三条准则:
1. 子类会先于父类被检查
2. 多个父类会根据它们在列表中的顺序被检查
3. 若是对下一个类存在两个合法的选择,选择第一个父类
子类继承了父类的方法,而后想进行修改,注意了是基于原有的基础上修改,那么就须要在子类中调用父类的方法
方式一: 父类名.父类方法()
#_*_coding:utf-8_*_ __author__ = 'Linhaifeng' class Vehicle: #定义交通工具类 Country='China' def __init__(self,name,speed,load,power): self.name=name self.speed=speed self.load=load self.power=power def run(self): print('开动啦...') class Subway(Vehicle): #地铁 def __init__(self,name,speed,load,power,line): Vehicle.__init__(self,name,speed,load,power) # 调用:父类名.父类方法() self.line=line def run(self): print('地铁%s号线欢迎您' %self.line) Vehicle.run(self) line13=Subway('中国地铁','180m/s','1000人/箱','电',13) line13.run()
方式二: super()
class Vehicle: #定义交通工具类 Country='China' def __init__(self,name,speed,load,power): self.name=name self.speed=speed self.load=load self.power=power def run(self): print('开动啦...') class Subway(Vehicle): #地铁 def __init__(self,name,speed,load,power,line): #super(Subway,self) 就至关于实例自己 在python3中super()等同于super(Subway,self) super().__init__(name,speed,load,power) self.line=line def run(self): print('地铁%s号线欢迎您' %self.line) super(Subway,self).run() class Mobike(Vehicle):#摩拜单车 pass line13=Subway('中国地铁','180m/s','1000人/箱','电',13) line13.run()
不用super()函数引起的惨案
#!/usr/bin/env python #-*- coding:utf-8 -*- #每一个类中都继承了且重写了父类的方法 class A: def __init__(self): print('A的构造方法') class B(A): def __init__(self): print('B的构造方法') A.__init__(self) class C(A): def __init__(self): print('C的构造方法') A.__init__(self) class D(B,C): def __init__(self): print('D的构造方法') B.__init__(self) C.__init__(self) pass f1=D() print(D.__mro__) #python2中没有这个属性 ''' 执行结果: D的构造方法 B的构造方法 A的构造方法 C的构造方法 A的构造方法 (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>) '''
当使用super()函数时,Python会在MRO列表上继续搜索下一个类。只要每一个从新定义的方法统一使用super()并只调用它一次,那么控制流最终会遍历完整个MRO列表,每一个方法也只会被调用一次
注: 使用super调用的全部属性,都是从MRO列表当前的位置日后找,千万不要经过看代码去找继承关系,必定要看MRO列表.
#!/usr/bin/env python #-*- coding:utf-8 -*- #每一个类中都继承了且重写了父类的方法 class A: def __init__(self): print('A的构造方法') class B(A): def __init__(self): print('B的构造方法') super(B,self).__init__() class C(A): def __init__(self): print('C的构造方法') super(C,self).__init__() class D(B,C): def __init__(self): print('D的构造方法') super(D,self).__init__() f1=D() print(D.__mro__) # python2中没有这个属性 ''' 执行结果: D的构造方法 B的构造方法 C的构造方法 A的构造方法 (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>) '''
多态指的是一类事物有多种形态,(一个抽象类有多个子类,于是多态的概念依赖于继承)
1. 序列类型有多种形态:字符串,列表,元组。
2. 动物有多种形态:人,狗,猪
import abc class Animal(metaclass=abc.ABCMeta): #同一类事物:动物 @abc.abstractmethod def talk(self): pass class People(Animal): #动物的形态之一:人 def talk(self): print('say hello...') class Dog(Animal): #动物的形态之二:狗 def talk(self): print('say wangwang...') class Pig(Animal): #动物的形态之三:猪 def talk(self): print('say hengheng...')
3. 文件有多种形态:文本文件,可执行文件
import abc class File(metaclass=abc.ABCMeta): #同一类事物:文件 @abc.abstractmethod def click(self): pass class Text(File): #文件的形态之一:文本文件 def click(self): print('open file') class ExeFile(File): #文件的形态之二:可执行文件 def click(self): print('execute file')
多态与多态性是两种不一样的概念。
多态性是指具备不一样功能的函数可使用相同的函数名,这样就能够用一个函数名调用不一样功能的函数。
在面向对象方法中通常是这样表述多态性: 向不一样的对象发送同一条消息(obj.func():是调用了obj的func方法,又称为向obj发送了一条消息func),不一样的对象在接收时会产生不一样的行为(即方法)。也就是说,每一个对象能够用本身的方式去响应共同的消息。所谓消息,就是调用函数,不一样的行为就是指不一样的实现,即执行不一样的函数。
好比:老师.下课铃响了(),学生.下课铃响了(),老师执行的是下班操做,学生执行的是放学操做,虽然两者消息同样,可是执行的效果不一样。
多态性分为静态多态性和动态多态性
静态多态性:如任何类型均可以用运算符+进行运算
动态多态性: 以下所示
>>> def func(obj): ... print(obj.__len__()) ... >>> func('Hello') 5 >>> func([0,1,2,3]) 4 >>> func((7,8,9)) 3
#!/usr/bin/python # -*- coding:utf-8 -*- class Animal: def talk(self): print('Is talking') class People(Animal): def talk(self): print('say hello...') class Pig(Animal): def talk(self): print('say hengheng...') class Dog(Animal): def talk(self): print('say wangwang...') class Cat(Animal): def talk(self): print('say miaomiao...') def func(obj): # 参数obj就是对态性的体现 obj.talk() # 执行同一个方法 peo1=People() # 产生一我的的对象 pig1=Pig() # 产生一个猪的对象 dog1=Dog() # 产生一个狗的对象 cat1=Cat() # 产生一个猫的对象 func(peo1) func(pig1) func(dog1) func(cat1) ''' 执行结果: say hello... say hengheng... say wangwang... say miaomiao... '''
#!/usr/bin/env python #-*- coding:utf-8 -*- import abc class File(metaclass=abc.ABCMeta): @abc.abstractmethod def click(self): pass class Text(File): def click(self): print('open file') class ExeFile(File): def click(self): print('execute file') def func(file): # 参数file就是对态性的体现 file.click() t1=Text() # 实例化一个文本文件的对象 e1=ExeFile() # 实例化一个可执行文件的对象 func(t1) func(e1) ''' 执行结果: open file execute file '''
综上所述: 多态性是一个接口(函数func),多种实现如obj.talk()
优势:
1.增长了程序的灵活性
以不变应万变,不论对象变幻无穷,使用者都是同一种形式去调用,如func(animal)
2.增长了程序额可扩展性
经过继承animal类建立了一个新的类,使用者无需更改本身的代码,仍是用func(animal)去调用
>>> class Cat(Animal): #属于动物的另一种形态:猫 ... def talk(self): ... print('say miao') ... >>> def func(animal): #对于使用者来讲,本身的代码根本无需改动 ... animal.talk() ... >>> cat1=Cat() #实例出一只猫 >>> func(cat1) #甚至连调用方式也无需改变,就能调用猫的talk功能 say miao ''' 这样咱们新增了一个形态Cat,由Cat类产生的实例cat1,使用者能够在彻底不须要修改本身代码的状况下。使用和人、狗、猪同样的方式调用cat1的talk方法,即func(cat1) '''
在程序设计中,封装(Encapsulation)是对具体对象的一种抽象,即将某些部分隐藏起来,在程序外部看不到,其含义是其余程序没法调用。
要了解封装,离不开“私有化”,就是将类或者是函数中的某些属性限制在某个区域以内,外部没法调用。
"封装" 就是将抽象获得的数据和行为(或功能)相结合,造成一个有机的总体(即类);封装的目的是加强安全性和简化编程,使用者没必要了解具体的实现细节,而只是要经过外部接口,一特定的访问权限来使用类的成员。
6.2 为何要封装?
封装数据的主要缘由是:保护隐私(把不想别人知道的东西封装起来)
封装方法的主要缘由是:隔离复杂度(好比:电视机,咱们看见的就是一个黑匣子,其实里面有不少电器元件,对于用户来讲,咱们不须要清楚里面都有些元件,电视机把那些电器元件封装在黑匣子里,提供给用户的只是几个按钮接口,经过按钮就能实现对电视机的操做。)
提示:在编程语言里,对外提供的接口(接口可理解为了一个入口),就是函数,称为接口函数,这与接口的概念还不同,接口表明一组接口函数的集合体。
6.3 封装什么?
你钱包的有多少钱 (数据的封装)
箱子里面有什么东西 (数据的封装)
你撒尿的具体功能是怎么实现的 (方法的封装)
6.4 封装分为两个层面
封装其实分为两个层面,但不管哪一种层面的封装,都要对外界提供好访问你内部隐藏内容的接口(接口能够理解为入口,有了这个入口,使用者无需且不可以直接访问到内部隐藏的细节,只能走接口,而且咱们能够在接口的实现上附加更多的处理逻辑,从而严格控制使用者的访问)
第一个层面的封装(什么都不用作):建立类和对象会分别建立两者的名称空间,咱们只能用类名.或者obj.的方式去访问里面的名字,这自己就是一种封装。
>>> r1.nickname '草丛伦' >>> Riven.camp 'Noxus
注: 对于这一层面的封装(隐藏),类名.和实例名.就是访问隐藏属性的接口
第二个层面的封装: 类中把某些属性和方法隐藏起来(或者说定义成私有的),只在类的内部使用、外部没法访问,或者留下少许接口(函数)供外部访问。
在python中用双下划线的方式实现隐藏属性(设置成私有的)
class A: __N=0 #类的数据属性就应该是共享的,可是语法上是能够把类的数据属性设置成私有的如__N,会变形为_A__N def __init__(self): self.__X=10 #变形为self._A__X def __foo(self): #变形为_A__foo print('from A') def bar(self): self.__foo() #只有在类内部才能够经过__foo的形式访问到.
这种自动变形的特色:
1. 类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。
2. 这种变形其实正是针对外部的变形,在外部是没法经过__x这个名字访问到的。
3. 在子类定义的__x不会覆盖在父类定义的__x,由于子类中变造成了:_子类名__x,而父类中变造成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是没法覆盖的。
注: 对于这一层面的封装(隐藏),咱们须要在类中定义一个函数(接口函数)在它内部访问被隐藏的属性,而后外部就可使用了。
这种变形须要注意的问题是:
1.这种机制也并无真正意义上限制咱们从外部直接访问属性,知道了类名和属性名就能够拼出名字:_类名__属性,而后就能够访问了,如a._A__N
>>> a=Animal() >>> a._Animal__name # 在类外部一种特殊的访问私有属性的方式 >>> a._Animal__age >>>Animal._Animal__sex
2.变形的过程只在类的定义是发生一次,在定义后的赋值操做,不会变形
class A: __N = 10 def __init__(self): self.__X = 10 a= A() print(a.__dict__) a.__Y=5 print(a.__dict__) ''' 执行结果: {'_A__X': 10} {'__Y': 5, '_A__X': 10} # 定义后的赋值操做,__Y,没有变形
3.在继承中,父类若是不想让子类覆盖本身的方法,能够将方法定义为私有的
>>> class A: ... def fa(self): ... print('from A') ... def test(self): ... self.fa() ... >>> class B(A): ... def fa(self): ... print('from B') ... >>> b=B() >>> b.test() from B
#把fa定义成私有的,即__fa >>> class A: ... def __fa(self): #在定义时就变形为_A__fa ... print('from A') ... def test(self): ... self.__fa() #只会与本身所在的类为准,即调用_A__fa ... >>> class B(A): ... def __fa(self): ... print('from B') ... >>> b=B() >>> b.test() from A
python并不会真的阻止你访问私有的属性,模块也遵循这种约定,若是模块名以单下划线开头,那么from module import *时不能被导入,可是你from module import _private_module依然是能够导入的
其实不少时候你去调用一个模块的功能时会遇到单下划线开头的(socket._socket,sys._home,sys._clear_type_cache),这些都是私有的,原则上是供内部调用的,做为外部的你,独断独行也是能够用的,只不过显得稍微傻逼一点点。
python要想与其余编程语言同样,严格控制属性的访问权限,只能借助内置方法如__getattr__,详见面向对象进阶。
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) #同上 ''' 输出结果: 314.1592653589793 62.83185307179586 '''
注: 此时的特性arear和perimeter不能被赋值
c.area=3 #为特性area赋值 ''' 抛出异常: AttributeError: can't set attribute '''
将一个类的函数定义成特性之后,对象再去使用的时候obj.name,根本没法察觉本身的name是执行了一个方法而后计算出来的返回值,这种特性的使用方式遵循了统一访问的原则。
ps:面向对象的封装有三种方式:
【public】
这种其实就是不封装,是对外公开的
【protected】
这种封装方式对外不公开,但对朋友(friend)或者子类(形象的说法是“儿子”,但我不知道为何你们 不说“女儿”,就像“parent”原本是“父母”的意思,但中文都是叫“父类”)公开
【private】
这种封装对谁都不公开
python并无在语法上把它们三个内建到本身的class机制中,在C++里通常会将全部的全部的数据都设置为私有的,而后提供set和get方法(接口)去设置和获取,在python中经过property方法能够实现,以下所示:
class Foo: def __init__(self,val): self.__NAME=val #将全部的数据属性都隐藏起来 @property def name(self): return self.__NAME #obj.name访问的是self.__NAME(这也是真实值的存放位置) @name.setter def name(self,value): if not isinstance(value,str): #在设定值以前进行类型检查 raise TypeError('%s must be str' %value) self.__NAME=value #经过类型检查后,将值value存放到真实的位置self.__NAME @name.deleter def name(self): raise TypeError('Can not delete') f=Foo('shuke') print(f.name) # f.name=10 #抛出异常'TypeError: 10 must be str' del f.name #抛出异常'TypeError: Can not delete'
class Foo: def __init__(self,val): self.__NAME=val #将全部的数据属性都隐藏起来 def getname(self): return self.__NAME #obj.name访问的是self.__NAME(这也是真实值的存放位置) def setname(self,value): if not isinstance(value,str): #在设定值以前进行类型检查 raise TypeError('%s must be str' %value) self.__NAME=value #经过类型检查后,将值value存放到真实的位置self.__NAME def delname(self): raise TypeError('Can not delete') name=property(getname,setname,delname) #不如装饰器的方式清晰
封装在于明确区份内外,使得类实现者能够修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。这就提供一个良好的合做基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。
#类的设计者 class Room: def __init__(self,name,owner,width,length,high): self.name=name self.owner=owner self.__width=width self.__length=length self.__high=high def tell_area(self): #对外提供的接口,隐藏了内部的实现细节,此时咱们想求的是面积 return self.__width * self.__length
>>> r1=Room('卧室','shuke',20,20,20) >>> r1.tell_area() #使用者调用接口tell_area 400
#类的设计者,轻松的扩展了功能,而类的使用者彻底不须要改变本身的代码 class Room: def __init__(self,name,owner,width,length,high): self.name=name self.owner=owner self.__width=width self.__length=length self.__high=high def tell_area(self): #对外提供的接口,隐藏内部实现,此时咱们想求的是体积,内部逻辑变了,只需求修该下列一行就能够很简答的实现,并且外部调用感知不到,仍然使用该方法,可是功能已经变了 return self.__width * self.__length * self.__high
对于仍然在使用tell_area接口的人来讲,根本无需改动本身的代码,就能够用上新功能
>>> r1.tell_area()
8000
一般状况下,在类中定义的全部函数(注意了,这里说的就是全部,跟self不要紧,self也只是一个再普通不过的参数而已)都是对象的绑定方法,对象在调用绑定方法时会自动将本身做为参数传递给方法的第一个参数。除此以外还有两种常见的方法:静态方法和类方法,两者是为类量身定制的,可是实例非要使用,也不会报错,后续将介绍。
一种普通函数,位于类定义的命名空间中,不会对任何实例类型进行操做,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'> 2 3 3 3 '''
应用场景: 编写类时须要采用不少不一样的方式来建立实例,而咱们只有一个__init__函数,此时静态方法就派上用场了
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)
类方法是给类用的,类在使用时会将类自己当作参数传给类方法的第一个参数,python为咱们内置了函数classmethod来把类中的函数定义成类方法。
class A: x=1 @classmethod def test(cls): print(cls,cls.x) class B(A): x=2 B.test() ''' 输出结果: <class '__main__.B'> 2 '''
应用场景:
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 '''
1. 绑定方法(绑定给谁,谁来调用就自动将它自己看成第一个参数传入):
1. 绑定到类的方法:用classmethod装饰器装饰的方法(为类量身定制)。
注: 类.boud_method(),自动将类看成第一个参数传入(其实对象也可调用,但仍将类看成第一个参数传入)
2. 绑定到对象的方法:没有被任何装饰器装饰的方法(为对象量身定制)。
注: 对象.boud_method(),自动将对象看成第一个参数传入(属于类的函数,类能够调用,可是必须按照函数的规则来,并不会进行自动传值)
2. 非绑定方法:用staticmethod装饰器装饰的方法
1. 不与类或对象绑定,类和对象均可以调用,可是并不会进行自动传值,就是一个普通工具而已。
注: 与绑定到对象方法区分开,在类中直接定义的函数,没有被任何装饰器装饰的,都是绑定到对象的方法,可不是普通函数,对象调用该方法会自动传值,而staticmethod装饰的方法,无论谁来调用,都不会进行自动传值。
示例说明:
class Foo: def test1(self): pass @classmethod def test2(cls): print(cls) @staticmethod def test3(): pass f=Foo() print(f.test1) # 对象的绑定方法 print(Foo.test2) # 类的绑定方法 print(Foo.test3) # 静态方法,只是一个函数而已,没有进行自动传值功能 print(f.test3) ''' 执行结果: <bound method Foo.test1 of <__main__.Foo object at 0x0000000000B72D30>> <bound method Foo.test2 of <class '__main__.Foo'>> <function Foo.test3 at 0x0000000000B6C510> <function Foo.test3 at 0x0000000000B6C510> '''
# statcimethod 与classmethod的区别 import hashlib import time import settings class MySQL: def __init__(self,host,port): self.id=self.create_id() self.host=host self.port=port print('conneting...') @staticmethod def create_id(): # 非绑定方法,就是类中的普通工具包 m=hashlib.md5(str(time.clock()).encode('utf-8')) return m.hexdigest() @staticmethod def from_conf(): return MySQL(settings.HOST, settings.PORT) # MySQL('127.0.0.1',3306) @classmethod def from_conf(cls): return cls(settings.HOST,settings.PORT) # 在Mariadb类中调用,就将Mariadb类自身传入,灵活性更好,至关于Mariadb('127.0.0.1',3306) class Mariab(MySQL): # def __str__(self): # return 'host:%s port:%s' %(self.host,self.port) pass conn=MySQL.from_conf() print(conn.host) conn1=Mariab.from_conf() print(conn1) ''' 执行结果: conneting... 127.0.0.1 conneting... <__main__.Mariab object at 0x0000000000A5D3C8> '''
不少人在学完了python的class机制以后,遇到一个生产中的问题,仍是会懵逼,这其实太正常了,由于任何程序的开发都是先设计后编程,python的class机制只不过是一种编程方式,若是你硬要拿着class去和你的问题死磕,变得更加懵逼都是分分钟的事,在之前,软件的开发相对简单,从任务的分析到编写程序,再到程序的调试,能够由一我的或一个小组去完成。可是随着软件规模的迅速增大,软件任意面临的问题十分复杂,须要考虑的因素太多,在一个软件中所产生的错误和隐藏的错误、未知的错误可能达到惊人的程度,这也不是在设计阶段就彻底解决的。
因此软件的开发实际上是一整套规范,咱们所学的只是其中的一小部分,一个完整的开发过程,须要明确每一个阶段的任务,在保证一个阶段正确的前提下再进行下一个阶段的工做,称之为软件工程。
面向对象的软件工程包括下面几个部:
8.1 面向对象分析(object oriented analysis ,OOA)
软件工程中的系统分析阶段,要求分析员和用户结合在一块儿,对用户的需求作出精确的分析和明确的表述,从大的方面解析软件系统应该作什么,而不是怎么去作。面向对象的分析要按照面向对象的概念和方法,在对任务的分析中,从客观存在的事物和事物之间的关系,贵南出有关的对象(对象的‘特征’和‘技能’)以及对象之间的联系,并将具备相同属性和行为的对象用一个类class来标识。
创建一个能反映这是工做状况的需求模型,此时的模型是粗略的。
8.2 面向对象设计(object oriented design,OOD)
根据面向对象分析阶段造成的需求模型,对每一部分分别进行具体的设计。
首先是类的设计,类的设计可能包含多个层次(利用继承与派生机制)。而后以这些类为基础提出程序设计的思路和方法,包括对算法的设计。
在设计阶段并不牵涉任何一门具体的计算机语言,而是用一种更通用的描述工具(如伪代码或流程图)来描述。
8.3 面向对象编程(object oriented programming,OOP)
根据面向对象设计的结果,选择一种计算机语言把它写成程序,能够是python,也能够是别的编程语言。
8.4 面向对象测试(object oriented test,OOT)
在写好程序后交给用户使用前,必须对程序进行严格的测试,测试的目的是发现程序中的错误并修正它。
面向对象的测试是用面向对象的方法进行测试,以类做为测试的基本单元。
8.5 面向对象维护(object oriendted soft maintenance,OOSM)
正如对任何产品都须要进行售后服务和维护同样,软件在使用时也会出现一些问题,或者软件商想改进软件的性能,这就须要修改程序。
因为使用了面向对象的方法开发程序,使用程序的维护比较容易。
由于对象的封装性,修改一个对象对其余的对象影响很小,利用面向对象的方法维护程序,大大提升了软件维护的效率,可扩展性高。
在面向对象方法中,最先发展的确定是面向对象编程(OOP),那时OOA和OOD都尚未发展起来,所以程序设计者为了写出面向对象的程序,还必须深刻到分析和设计领域,尤为是设计领域,那时的OOP实际上包含了如今的OOD和OOP两个阶段,这对程序设计者要求比较高,许多人感到很难掌握。
如今设计一个大的软件,是严格按照面向对象软件工程的5个阶段进行的,这个5个阶段的工做不是由一我的从头至尾完成的,而是由不一样的人分别完成,这样OOP阶段的任务就比较简单了。程序编写者只须要根据OOd提出的思路,用面向对象语言编写出程序既可。
在一个大型软件开发过程当中,OOP只是很小的一个部分。
对于全栈开发的你来讲,这五个阶段都有了,对于简单的问题,没必要严格按照这个5个阶段进行,每每由程序设计者按照面向对象的方法进行程序设计,包括类的设计和程序的设计。
1. 面向对象的程序设计看起来高大上,因此我在编程时就应该保证通篇class,这样写出的程序必定是好的程序(面向对象只适合那些可扩展性要求比较高的场景)
2. 不少人喜欢说面向对象三大特性(这是从哪传出来的,封装,多态,继承?漏洞太多太多,好吧暂且称为三大特性),那么我在基于面向对象编程时,我必定要让我定义的类中完整的包含这三种特性,这样写确定是好的程序,好家伙,我说降龙十八掌有十八掌,那么你每次跟人干仗都要从第一掌打到第18掌这才显得你会了是么,我来一万我的你须要打10000*18掌对么,傻叉。
3. 类有类属性,实例有实例属性,因此咱们在定义class时必定要定义出那么几个类属性,想不到怎么办,那就使劲的想,定义的越多越牛逼。
这就犯了一个严重的错误,程序越早面向对象,死的越早,为啥面向对象,由于咱们要将数据与功能结合到一块儿,程序总体的结构都没有出来,或者说须要考虑的问题你都没有搞清楚个八九不离十,你就开始面向对象了,这就致使了,你在那里干想,自觉得想通了,定义了一堆属性,结果后来又都用不到,或者想不通到底应该定义啥,那就一直想吧,想着想着就疯了。
你见过哪家公司要开发一个软件,上来就开始写,确定是频繁的开会讨论计划。
4. 既然这么麻烦,那么我完全解脱了,咱们不要用面向对象编程了,你啊,你有大才,你能成事啊,傻叉。
抽象/实现
抽象指对现实世界问题和实体的本质表现,行为和特征建模,创建一个相关的子集,能够用于 绘程序结构,从而实现这种模型。抽象不只包括这种模型的数据属性,还定义了这些数据的接口。
对某种抽象的实现就是对此数据及与之相关接口的现实化(realization)。现实化这个过程对于客户 程序应当是透明并且无关的。
封装/接口
封装描述了对数据/信息进行隐藏的观念,它对数据属性提供接口和访问函数。经过任何客户端直接对数据的访问,无视接口,与封装性都是背道而驰的,除非程序员容许这些操做。做为实现的 一部分,客户端根本就不须要知道在封装以后,数据属性是如何组织的。在Python中,全部的类属性都是公开的,但名字可能被“混淆”了,以阻止未经受权的访问,但仅此而已,再没有其余预防措施了。这就须要在设计时,对数据提供相应的接口,以避免客户程序经过不规范的操做来存取封装的数据属性。
注: 封装毫不是等于“把不想让别人看到、之后可能修改的东西用private隐藏起来”
真正的封装是,通过深刻的思考,作出良好的抽象,给出“完整且最小”的接口,并使得内部细节能够对外透明。
(注: 对外透明的意思是,外部调用者能够顺利的获得本身想要的任何功能,彻底意识不到内部细节的存在)
合成
合成扩充了对类的 述,使得多个不一样的类合成为一个大的类,来解决现实问题。合成 述了 一个异常复杂的系统,好比一个类由其它类组成,更小的组件也多是其它的类,数据属性及行为, 全部这些合在一块儿,彼此是“有一个”的关系。
派生/继承/继承结构
派生描述了子类衍生出新的特性,新类保留已存类类型中全部须要的数据和行为,但容许修改或者其它的自定义操做,都不会修改原类的定义。
继承描述了子类属性从祖先类继承这样一种方式
继承结构表示多“代”派生,能够述成一个“族谱”,连续的子类,与祖先类都有关系。
泛化/特化
基于继承
泛化表示全部子类与其父类及祖先类有同样的特色。
特化描述全部子类的自定义,也就是,什么属性让它与其祖先类不一样。
多态与多态性
多态指的是同一种事物的多种状态:水这种事物有多种不一样的状态:冰,水蒸气。
多态性的概念指出了对象如何经过他们共同的属性和动做来操做及访问,而不需考虑他们具体的类。
冰,水蒸气,都继承于水,它们都有一个同名的方法就是变成云,可是冰.变云(),与水蒸气.变云()是大相径庭的过程,虽然调用的方法都同样。
自省/反射
自省也称做反射,这个性质展现了某对象是如何在运行期取得自身信息的。若是传一个对象给你,你能够查出它有什么能力,这是一项强大的特性。若是Python不支持某种形式的自省功能,dir和type内建函数,将很难正常工做。还有那些特殊属性,像__dict__,__name__及__doc__。
参考资料(林海峰老师博客): http://www.cnblogs.com/linhaifeng/articles/6182264.html#_label17