面向对象编程二
类命名空间与对象、实例的命名空间
创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性
而类有两种属性:静态属性和动态属性
- 静态属性就是直接在类中定义的变量 #只要使用静态属性,就用类名去调用,全局生效
- 动态属性就是定义在类中的方法
其中类的数据属性是共享给所有对象的
创建一个对象/实例就会创建一个对象/实例的名称空间,存放对象/实例的名字,称为对象/实例的属性
在obj.name会先从obj自己的名称空间里找name,找不到则去类中找,类也找不到就找父类...最后都找不到就抛出异常
例1:写一个类,能统计这个类被多少个对象实例化了.
所有的对象共享这个结果
定义静态变量
class SS: count=0 def __init__(self): SS.count+= 1 shili1=SS() shili2=SS() shili3=SS() print(SS.count)
例2:
圆环是由两个圆组成的,圆环的面积是外面圆的面积减去内部圆的面积。圆环的周长是内部圆的周长加上外部圆的周长。
这个时候,我们就首先实现一个圆形类,计算一个圆的周长和面积。然后在"环形类"中组合圆形的实例作为自己的属性来用
from math import pi class Circle: def __init__(self,r): self.r=r def area(self): return pi*(self.r**2) def perimeter(self): return 2*pi*self.r r10=Circle(10) #实例化圆环 area1=r10.area() #计算圆面积 perimeter1=r10.perimeter() #计算圆周长 print(area1,perimeter1) class Ring: def __init__(self,r_out,r_in): self.r_out=Circle(r_out) self.r_in=Circle(r_in) def area(self): ring_area=self.r_out.area()-self.r_in.area() return ring_area def perimeter(self): ring_perimeter = self.r_out.perimeter() + self.r_in.perimeter() return ring_perimeter ring=Ring(10,5) #实例化一个环 r_a=ring.area() #计算环的面积 r_p=ring.perimeter() #计算环的周长 print(r_a) print(r_p)
例3:
用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python课程
参考:
class Birthday: def __init__(self,year,month,day): self.year = year self.month = month self.day = day class Teacher: def __init__(self,name,sex,course,birth): self.name = name self.sex = sex self.course = course self.birth = birth # birth是一个对象 birth = Birthday(1960,3,7) # birth 是Birthday类的一个对象 alex = Teacher('alex','male','python',birth) # alex.birth = birth # time '1960-3-7' # 老师的年龄 2018 - 1960 print(birth) import time if birth.month == time.localtime().tm_mon and \ birth.day == time.localtime().tm_mday: print('生日快乐') print(time.localtime().tm_year - birth.year)
实践:
class Birthday: def __init__(self,year,month,day): self.year=year self.month=month self.day=day class Teacher: def __init__(self, name, sex, course,birth): self.name=name self.sex=sex self.course=course self.birth=birth birth=Birthday(1987,6,17) zhang=Teacher('zhang','男','python',birth) print(birth) print(zhang.birth.day)
例4:
人狗大战-武器版
class Person(): #定义玩家
def __init__(self,name,aggress,hp,money): #定义对象的各种属性 self.name=name self.aggress=aggress self.hp=hp self.money=money def attack(self,dog): dog.hp-=self.aggress #狗被攻击后的血量 class Dog(): def __init__(self,name,kind,aggress,hp): #定义狗的各种属性 self.name=name self.kind=kind self.aggress = aggress self.hp = hp def bite(self,persons): #人被狗咬后的血量 persons.hp-=self.aggress class Weapen: def __init__(self,name,price,aggress,hp): #定义武器的各种属性 self.name = name self.aggress = aggress self.hp = hp self.price = price def update(self,man): #装载武器后添加属性 man.money-=self.price man.aggress+=self.aggress man.hp+=self.hp def skill(self,obj): #武器技能,被攻击者血量-500 obj.hp-=500 player=Person('王大锤',200,3000,8800) #实例化一个玩家 hei_2=Dog('哮天犬','狼狗',300,1500) #实例化一条狗 brick=Weapen('板砖',5000,300,1000) #实例化一件武器 if player.money>=brick.price: brick.update(player) player.weapen=brick print('玩家的剩余金钱为%d,玩家的血量为%d,玩家的攻击力为%d'%(player.money,player.hp,player.aggress)) print('哮天犬的血量为%d'%hei_2.hp) player.attack(hei_2) brick.skill(hei_2) print('当玩家用武器攻击哮天犬后,哮天犬的血量为%d'%hei_2.hp)
接口类和抽象类
接口类
继承有两种用途:
一:继承基类的方法,并且做出自己的改变或者扩展(代码重用)
二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能
借用abc模块来实现接口:
from abc import ABCMeta,abstractclassmethod class Payment(metaclass=ABCMeta): @abstractclassmethod def pay(self,money): raise NotADirectoryError class Wechatpay(Payment): def pay(self,money): print('微信支付了%s元'%money) p=Wechatpay() p.pay(100)
实践中,继承的第一种含义意义并不很大,甚至常常是有害的。因为它使得子类与基类出现强耦合。
继承的第二种含义非常重要。它又叫“接口继承”。
接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象”——这在程序设计上,叫做归一化。
归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合——就好象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
接口提取了一群类共同的函数,可以把接口当做一个函数的集合。 然后让子类去实现接口中的函数。 这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。 归一化,让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
# 接口类是一个规范 # 多种支付方式,每一种支付方式都是一个类 # 每一个类中定义一个支付方法完成支付功能 # 由于每一种支付方法完成支付功能 # 由于每一种支付方式在程序中表现出来的都是支付类的对象 # 为了方便支付方法的调用 # 需要统一一个入口,pay函数 # interface # 在python里没有接口类这种数据类型,没有接口类专门的语法
抽象类
什么是抽象类
与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化
为什么要有抽象类
如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。
比如我们有香蕉的类,有苹果的类,有桃子的类,从这些类抽取相同的内容就是水果这个抽象的类,你吃水果时,要么是吃一个具体的香蕉,要么是吃一个具体的桃子。。。。。。你永远无法吃到一个叫做水果的东西。
从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。
从实现角度来看,抽象类与普通类的不同之处在于:抽象类中有抽象方法,该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的,即将揭晓答案
在python中实现抽象类
#一切皆文件 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)
抽象类与接口类
抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性。
抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计
在python中,并没有接口类这种东西,即便不通过专门的模块定义接口,我们也应该有一些基本的概念。
1.多继承问题
在继承抽象类的过程中,我们应该尽量避免多继承;
而在继承接口的时候,我们反而鼓励你来多继承接口
接口隔离原则: 使用多个专门的接口,而不使用单一的总接口。即客户端不应该依赖那些不需要的接口。
2.方法的实现
在抽象类中,我们可以对一些抽象方法做出基础实现;
而在接口类中,任何方法都只是一种规范,具体的功能需要子类实现
在python里抽象类和接口类没有区别
接口和抽象的概念来自Java等其他编程语言
接口类和抽象类的区别:
首先接口和抽象类的设计目的就是不一样的。接口是对动作的抽象,而抽象类是对根源的抽象。对于抽象类,比如男人,女人这两个类,那我们可以为这两个类设计一个更高级别的抽象类--人。对于接口,我们可以坐着吃饭,可以站着吃饭,可以用筷子吃饭,可以用叉子吃饭,甚至可以学三哥一样用手抓着吃饭,那么可以把这些吃饭的动作抽象成一个接口--吃饭。所以在高级语言中(如Java,C#),一个类只能继承一个抽象类(因为你不可能同时是生物又是非生物)。但是一个类可以同时实现多个接口,比如开车接口,滑冰接口,踢足球接口,游泳接口。
总结几句话来说:
1、抽象类和接口都不能被直接实例化,如果二者要实例化,就涉及到多态。如果抽象类要实例化,那么抽象类定义的变量必须指向一个子类对象,这个子类继承了这个抽象类并实现了这个抽象类的所有抽象方法。如果接口要实例化,那么这个接口定义的变量要指向一个子类对象,这个子类必须实现了这个接口所有的方法。
2、抽象类要被子类继承,接口要被子类实现。
3、接口里面只能对方法进行声明,抽象类既可以对方法进行声明也可以对方法进行实现。
4、抽象类里面的抽象方法必须全部被子类实现,如果子类不能全部实现,那么子类必须也是抽象类。接口里面的方法也必须全部被子类实现,如果子类不能实现那么子类必须是抽象类。
5、接口里面的方法只能声明,不能有具体的实现。这说明接口是设计的结果,抽象类是重构的结果。
6、抽象类里面可以没有抽象方法。
7、如果一个类里面有抽象方法,那么这个类一定是抽象类。
8、抽象类中的方法都要被实现,所以抽象方法不能是静态的static,也不能是私有的private。
9、接口(类)可以继承接口,甚至可以继承多个接口。但是类只能继承一个类。
10、抽象级别(从高到低):接口>抽象类>实现类。
11、抽象类主要是用来抽象类别,接口主要是用来抽象方法功能。当你关注事物的本质的时候,请用抽象类;当你关注一种操作的时候,用接口。
12、抽象类的功能应该要远多于接口,但是定义抽象类的代价较高。因为高级语言一个类只能继承一个父类,即你在设计这个类的时候必须要抽象出所有这个类的子类所具有的共同属性和方法;但是类(接口)却可以继承多个接口,因此每个接口你只需要将特定的动作方法抽象到这个接口即可。也就是说,接口的设计具有更大的可扩展性,而抽象类的设计必须十分谨慎。
-----------------------------------------------------------------------分割线--------------------------------------------------------------------------
多态性
一 什么是多态动态绑定(在继承的背景下使用时,有时也称为多态性)
多态性是指在不考虑实例类型的情况下使用实例
在面向对象方法中一般是这样表述多态性:
向不同的对象发送同一条消息(!!!obj.func():是调用了obj的方法func,又称为向obj发送了一条消息func),不同的对象在接收时会产生不同的行为(即方法)。
也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。
比如:老师.下课铃响了(),学生.下课铃响了(),老师执行的是下班操作,学生执行的是放学操作,虽然二者消息一样,但是执行的效果不同
多态性
peo=People() dog=Dog() pig=Pig() #peo、dog、pig都是动物,只要是动物肯定有talk方法 #于是我们可以不用考虑它们三者的具体是什么类型,而直接使用 peo.talk() dog.talk() pig.talk() #更进一步,我们可以定义一个统一的接口来使用 def func(obj): obj.talk()
鸭子类型
逗比时刻:
Python崇尚鸭子类型,即‘如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子’
python程序员通常根据这种行为来编写程序。例如,如果想编写现有对象的自定义版本,可以继承该对象
也可以创建一个外观和行为像,但与它无任何关系的全新对象,后者通常用于保存程序组件的松耦合度。
例1:利用标准库中定义的各种‘与文件类似’的对象,尽管这些对象的工作方式像文件,但他们没有继承内置文件对象的方法
例2:序列类型有多种形态:字符串,列表,元组,但他们直接没有直接的继承关系
#二者都像鸭子,二者看起来都像文件,因而就可以当文件一样去用 class TxtFile: def read(self): pass def write(self): pass class DiskFile: def read(self): pass def write(self): pass