python面向对象的三大特性继承 多态 封装
1 继承
python的继承分为单继承和多继承,新建的类被成为派生类或者子类,而继承的类被成为父类或者超类
查看继承关系
使用__bases__方法查看所有继承对象------》 对象.__bases__
如果没有指定继承父类,则python3中则默认所有的类都继承object类,也叫新式类。
继承与抽象的关系
一定是将对象中的类似的内容抽象出来,在进行继承
软件的重用
如果我们在开发过程中,需要写一个新类,而这个类的功能和已存在的类功能相似,那么就可以通过继承,获得部分的功能,减少代码量
同样的标准库或者第三方库同样可以被继承
派生
新类中也可以有自己的属性和方法,其中属性成为派生属性,方法成为派生方法。但是在定义的时候,要注意尽量别和父类的属性名和方法名重复,因为在调用的时候,如果新类中有,就回优先使用自己的而不去找父类的。
如果新类中的方法需要用到父类中同名的方法,则可以使用类名.方法名()方法名直接调用,也可以使用supper()方法直接调用
# class animal: # def __init__(self,name,aggr,hp): # self.name=name # self.aggr=aggr # self.hp=hp # # def eat(self): # self.hp+=100 # class dog(animal): # def __init__(self,name,aggr,hp,kind): # animal.__init__(self,name,aggr,hp)# 这里的self 是dog的self # self.kind=kind #派生属性 在父类的基础之上又派生了新的属性 # def bite(self,person): #派生方法 在父类的基础上有派生了新的方法 # person.hp-=self.aggr # # def eat(self): # """ # 如果迹象实现新的功能也想使用父类原本的功能,还需要在子类中在掉用父类 # :return: # """ # animal.eat(self)# 这里的self是dog 作为参数传给了animal.eat # self.teeth = 2 # class person(animal): # def __init__(self,name,aggr,hp,sex): # animal.__init__(self,name,aggr,hp) # self.money=0 # self.sex=sex # # def attack(self,dog): # dog.hp-=self.aggr # # def get_weson(self,weson): # pass # # jin=dog('二哈',100,500,'哈士奇') # jin.eat() # print(jin.hp) # dan=person('dandan',20,100,'baobao') # dan.eat() # print(dan.hp) # jin.bite(dan) # print(dan.hp)
super()用法
class animal: def __init__(self,name,aggr,hp): self.name=name self.aggr=aggr self.hp=hp def eat(self): self.hp+=100 print('吃药回血') class dog(animal): def __init__(self,name,aggr,hp,kind): #super(dog,self).__init__(name,aggr,hp) supper内参数可以不传 super().__init__(name, aggr, hp) self.kind=kind def eat(self): print('dog eating') jin=dog('二哈',200,100,'哈士奇') print(jin.name) jin.eat() super(dog,jin).eat() #如果是在类外 使用supper(类名,实例化名).方法() 就会执行类中的方法
python中的抽象类和接口类:
python原生不支持接口类,接口类是java的概念,在python中可以将接口类看成抽象类。抽象类无法被实例化
,且内部方法不能被实现。抽象类和接口类更像是一种类的规范。
python中的继承包括两种用途:
1 继承父类的方法,并且有自己改变或者扩展(派生)
2 声明某个子类兼容与某父类,就是定义一个接口类interface,接口类中定义了一些接口名但 是未能实现,子类继承接口类,必须要实现继承的接口类中的方法。
在实际工作中,继承的第一种用法含义并不大,因为它是的子类与父类穿线强耦合
第二种比较重要,叫着接口的继承。接口继承实际上要求做出一个良好的抽象,这个抽象规定了一个兼容接口,是的外部调用者无序关心具体袭击,可以一视同仁的处理实现了特定接口的所有对象--这种程序上的设计叫做归一化。类似与linux的泛文件概念。
# from abc import abstractclassmethod,ABCMeta # class Payment(metaclass=ABCMeta):# 元类 默认的元类 type 这里指定元类为ABCmeta 规范一个类 # @abstractclassmethod # def pay(self,money): # pass # # class Payment1: # # def pay1(self): # # raise NotImplemented #没有实现这个方法,也就是子类中没有实现父类的方法就会主动抛出异常 # class Wechat(Payment): # def pay(self,money): # print('已经使用微信支出%s',money) # class alichat(Payment): # def pay(self,money): # print('已经使用ali支出%s',money) # class appchat(Payment): # def pay1(self,money): # print('已经使用app支出%s',money) # def pay(pay_obj,money): #统一支付接口 # pay_obj.pay(money) # we=Wechat() # app=appchat() # pay(we,199) # pay(app,300)
接口类的继承:
# from abc import abstractclassmethod,ABCMeta # class swim_animal(metaclass=ABCMeta): # @abstractclassmethod # def swim(self): # pass # # class walk_animal(metaclass=ABCMeta): # @abstractclassmethod # def walk(self): # pass # # class fly_animal(metaclass=ABCMeta): # @abstractclassmethod # def fly(self): # pass # # class tiger(swim_animal,walk_animal,fly_animal): # def swim(self): # print('tiger swim') # def walk(self): # print('tiger swim') # def fly(self): # print('tiger swim')
python中利用abc模块实现抽象类:在py3.4之后,@abc.abstractclassmethod被弃用,所以pycharm会在该装饰器函数上画一条横线
import abc #利用abc模块实现抽象类 class all_file(metaclass=abc.ABCMeta): all_type='file' @abc.abstractclassmethod def read(self):#定义抽象方法,无需实现功能,当然也可以实现部分功能 '子类必须定义读功能' print('all_file read') pass @abc.abstractclassmethod def write(self): '子类必须定义写功能' pass class txt(all_file): def read(self): print('txt read') def write(self): print('txt write') t1=txt() t1.read() t1.write() print(t1.all_type)
继承的顺序:
1 python中的类可以继承多个类,java和c#只能继承一个类
2 python中的类如果继承了多个类,那么其寻找方法的方式有两种,分别是广度优先和深度优先。如果是经典类,多继承下是深度优先,如果是新式类多继承下是广度优先,在python中都是新式类。
原理:
如果你定义一个类,python会计算出一个方法解析顺序mro列表,这个mro列表即使一个简单的所有父类的线性顺序列表
父类的mro列表回遵循如下三条准则
1 子类会先于父类被检查,如果子类和父类中有相同的方法名的时候 要特别注意。
2 多个父类会根据它们在列表中的顺序被检查
3 如果对下一个类存在两个合法的选择,选择第一个父类
多态
python中天生支持多态。一种事物有多种形态,强类型语言需要定义参数类型,python是动态强类型语言,比如2+str=2str 但python中会报类型错误
百度中的概念:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。
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 aoao')
peo=People() dog=Dog() pig=Pig() #peo、dog、pig都是动物,只要是动物肯定有talk方法 #于是我们可以不用考虑它们三者的具体是什么类型,而直接使用 peo.talk() dog.talk() pig.talk() #更进一步,我们可以定义一个统一的接口来使用 def func(obj): obj.talk()
封装:
代码的保护,面向对象的思想本身就是一种封装,只让自己的对象调用自己类中的方法。就是将属性和方法都藏起来。
类的私有:
所有的私有,都是在变量的左边加上双下划线
1 对象的私有属性
2 类中的私有方法
3 类中的静态私有属性
所有的私有,都不能在类的外部使用
#类的私有属性 只是在代码层面不允许用 对象.属性的方式调用 # class userinfo: # __key=123 #私有的静态属性 # def __init__(self,name,pwd): # self.name=name # self.__pwd=pwd #私有属性 # def get_pwd(self): # # 只要在类的内部使用私有属性,就会自动带上 _类名 # # 注意如果此时在类的外部调用pwd 需要用 _userinfo__pwd # # 这一点可以通过在外部定义一个 带下划线的方法来实验例如 yuan._pwd1 # # 这样在外部依然可以用 yuan._pwd1来调用该方法 # print('get_pwd--》',self.__dict__) # return self.__pwd # # def __user_name(self): # #私有的方法 只在内部使用 外部没办法感知 # print(self.__dict__) # print('__user_name') # def use(self): # print('use',self.__dict__) # self.__user_name() # yuan=userinfo('dandan',123456) # print(yuan.name) # # #print(yuan.__pwd) 会报错 # print(yuan.__dict__) 查看self之后看到对象中没有__pwd,所以需要yuan._userinfo__pwd的方式调用 # print(yuan._userinfo__pwd) #_类名__属性 # yuan._weight=100 #设定对象的隐藏方法 # print(yuan._weight) # print(yuan.use()) #假设父类的私有属性,能被子类调用?? class foo: __y='212' class son(foo): #print(foo.__key) pass #弱项调用必须在外部调用需要以下列方式 # print(son._foo__y)
property classmethod staicmethod用法:
''' 遗留的内置函数 property classmethod staticmethod ''' #property 内置装饰器函数,只在面向对象中使用 #被property装饰的方法不能传任何参数 # 只是使类中的方法看起来更像是一个属性, # 在类的外部调用该方法的时候,不用加()调用 from math import pi # class circle: # def __init__(self,r): # self.r=r # @property # def perimeter(self): # return 2*pi*self.r # @property # def area(self): # return pow(self.r,2)*pi # # c1=circle(6) # print(c1.perimeter) # print(c1.area) # class person: # def __init__(self,name,high,weight): # self.name=name # self.high=high # self.weight=weight # @property # def bmi(self): # return self.weight/self.high**2 # @property # def fab_bmi(self): # return 22*pow(self.high,2) # # yuan=person('袁',1.74,91) # print(yuan.bmi) # print(yuan.fab_bmi) #利用property 修改类的私有属性 #property 可以看成将方法置成了一个类的属性 在外部是不能被更改的 #需要配合 @被property装饰的函数同名.setter 在定义一个新的和被property装饰的方法同名的方法才能完成修改 #所以如果想使用 @被property装饰的函数同名.setter 更改则必须先使用@property # class edit_name: # def __init__(self,name): # self.__name=name # @property # def name1(self): # return self.__name+'sb' # # @name1.setter # def name1(self,new_name): # print('name1 edit') # self.__name=new_name # # cat=edit_name('dandan') # print(cat.name1) # cat.name1='wangyongmei' # print(cat.name1) # class goods: # discount=0.8 # def __init__(self,name,price): # self.name=name # self.__price=price # @property # def price(self): # return self.__price*goods.discount # app=goods('玉米',6) # print(app.price) #属性的删除 修改 查看 # class del_name: # def __init__(self,name): # self.__name=name # @property # def name1(self): # return self.__name # @name1.deleter # def name1(self): # print('执行了这个方法') # del self.__name # @name1.setter # def name1(self,newname): # self.__name=newname # # del_oldname=del_name('niu') # print(del_oldname.name1) # print(del_oldname.__dict__) # # #del 只是触发了类中 @被property装饰的函数同名.deleter 装饰的方法 # del del_oldname.name1 # print(del_oldname.__dict__) # #print(del_oldname.name1) ''' method 方法 staticmethod 静态的方法 classmethod 类方法 把一个方法变成一个类中的方法,这个方法就直接可以被类调用,不需要依托任何对象 当这个方法的操作只设计静态属性的时候,就应该使用classmethod来装饰这个方法 ''' # class goods: # __discount=0.8 # def __init__(self,name,price): # self.name=name # self.__price=price # @property # def price(self): # return self.__price*goods.__discount # @classmethod #把一个方法变成一个类中的方法,这个方法就直接可以被类调用,不需要依托任何对象 # def change_discont(cls,newdiscont): # cls.__discount=newdiscont # # app=goods('玉米',6) # print(app.price) # goods.change_discont(0.6) # print(app.price) #面向对象编程 staticmethod 方法 class Login: def __init__(self,name,password): self.name=name self.password=password def login(self): pass @staticmethod def get_user(): user=input('usename') pwd=input('pwd') Login(user,pwd) Login.get_user() #在完全面向对象的程序中 #如果一个函数,既和对象没有关系,也和类没有关系,那么就用staticmethod将这个函数变成一个静态方法 #类方法,有一个默认参数cls,代表这个类cls #静态方法 没有默认的参数,就像函数一样
反射 hasattr getattr deattr:
''' 反射 hasattr getattr deattr ''' class teacher: dic={'查看学生信息':'show_student','查看讲师信息':'show_teacher'} @classmethod def show_student(cls): print('show_student') @classmethod def show_teacher(self): print('show_teacher') @classmethod def func(cls): print('sdf') # ret=getattr(teacher,'dic') #teacher.dic 类也是对象,需要注意的是被调用的函数是实例函数还是类函数,如果是实例函数则需要实例化类--> # print(ret) # ret01=getattr(teacher,'func') #teacher.func() # ret01() # if hasattr(teacher,'show_student'): # ret02=getattr(teacher,'show_student') # print(ret02) # ret02() # # # yuan=teacher() # ret03=getattr(yuan,'show_teacher') # ret03() for k in teacher.dic: print(k) key_info=input("pelase select info") if hasattr(teacher,key_info): ret7=getattr(teacher,teacher.dic[key_info]) ret7() ''' 通过反射 对象名 获取对象属性和普通方法 类名 获取静态属性和类方法和静态方法 1 同样,若不想正常调用模块中的方法,可以采用反射 2 内置模块也能用 import time # time.asctime() ret9=getattr(time,'asctime') print(ret9()) ''' 3 反射自己模块中的变量 # import sys # def cap(): # print('反射自己模块中变量') # return '反射自己模块中变量01' # year=2018 # ret4=getattr(sys.modules['__main__'],'year') # print(ret4) # #反射自己模块中的函数 # ret5=getattr(sys.modules['__main__'],'cap') # print(ret5())
类的内置方法:
''' 类的内置方法 类的内置方法 和 内置函数之间有着千丝万缕的联系 双下方法 __str__ object 里边有个__str__,一旦被调用,就返回调用这个方法的对象的内存地址 __str__返回的一定是个字符串 __repr__ %s str() 直接打印 实际上执行的是 __str__ %r repr() 实际上是__repr__ repr是str的备胎,但是str不能做repr的备胎 str(obj) 实际上是内部调用了obj.__str__方法,如果str方法有,那么它必须返回一个字符串 如果没有__str__方法,回系按照本类 __repr__方法,再没有再找父类中的__str__ repr() 只会找__repr__ 如果没有找父类的 ''' # class a: # def __str__(self): # return 'a' # # a1=a() # print(a1) #打印一个对象的时候,就是调用a.__str__ # #a1.__str__ ---》object # #object 里里边有个__str__ 一旦被调用,就返回调用这个方法的对象的内存地址 # # l=[1,2,3,4] #实例化了一个列表对象 # print(l) #---->实际上 调用了 __str__ # class teacher: # def __init__(self,name,salary): # self.name=name # self.salary=salary # def __str__(self): # return 'teacher object :%s'%self.name # # def __repr__(self): # return str(self.__dict__) # def func(self): # return 'dddddd' # # wukong=teacher('aoteman',252) # print(str(wukong)) # print(repr(wukong)) # print('%r'%wukong) #自己有__repr__ 就用自己的 如果没有 就调用父类的object # class cla: # def __init__(self,name): # self.name=name # self.studen=[] # def __len__(self): # print('len1') # # 这个地方self.studen不是实例化对象所以不会调用自己的双下len方法, # # 但是如果写成len(self)就回默认调用自己的双下len方法,因为self指的是类的本身 # # # __len__方法是cla类的内置方法,根据继承可知,实例在调用方法的时候,会先调自己的 # #所以在外部调用len(实例化对象) 会调自己的__len__方法 # #但是在return的时候又了一次len方法,但是传入的是一个实例化的列表对象 # #所以这个return的len实际上是调用list的__len__方法,list自己没有__len__方法,所以最终调用的还是object的 # return len(self.studen) # ps=cla('py9') # ps.studen.append('dsf') # ps.studen.append('dsf') # print(len(ps)) # # ''' # __del__ 析构函数 # 在删除一个对象之前,进行一些收尾工作 ''' # class af: # def __del__(self): #析构函数 在删除一个对象前进行一些收尾工作 # self.f.close() # a123=af() # a123.f=open() #打开文件1 在操作系统中打开一个文件 拿到文件操作符存在内存中 # del a123 #直接删除a123 等于也删除了a123.f拿到的了文件操作符,但是此时系统中的文件并没有关闭 #引用计数 python的垃圾回收机制 如果检测到下边还有地方引用a123或者类 计数+1如果没有就制程0 # # ''' # __call__ 一个对象加() 等于执行类内部的__call__方法 # # ''' class yu: def __init__(self,name): self.name=name def __call__(self): print('内置call方法被执行') a=yu('ni')()
item __getiem__ __setitem__ __delitem__ 支持字典增删改查,查看字典源码,其实也是这样写的,包含列表,只是多了一种取值方法。
# class foo(object): # def __init__(self,name,age,sex): # self.name=name # self.age=age # self.sex=sex # def __getitem__(self, item): # if hasattr(self,item): # return self.__dict__[item] # def __setitem__(self, key, value): # self.__dict__[key]=value # def __delitem__(self, key): # del self.__dict__[key] # # f=foo('yuan',18,'man') # print(f['name']) # f['like']='son' # print(f.like) #object原生支持 # print(f.like,f['like']) #通过自己实现的 # print(f.__dict__) # del f['like'] # print(f.__dict__)
__init__ 初始化方法 __new__ 创建一个对象。其实类的实例化时,是默认执行了object的__new__方法,用于构建一个self
# class a: # def __init__(self): # self.x=1 # print('init function') # def __new__(cls, *args, **kwargs): # print('__new__') # return object.__new__(a,*args,**kwargs) #这里class a并没有能实现构造的方法 所以还是需要调用object的 # b=a()
单例模式 __new__ 一个类始终只有一个实例,限制一个类从头到位只有一个实例,当你之后再来实例化的时候看就用之前创建的对象
#23种 #__new__ 单例模式 # 一个类始终只有一个实例 限制一个类从头到尾只有一个实例 # 当你之后再来实例化的时候看就用之前创建的对象 # class b: # __insta=False # def __init__(self,name,age): # self.name=name # self.age=age # def __new__(cls, *args, **kwargs): # ''' # 在外部实例化该类的时候,回执行类中的双下new方法 # 通过判断_insta的真假,返回不同的返回值,在第一次 # 实例化该类的时候,其实通过在类的内部改变了私有__insta的方法 # 当下次再实例化调用时,就返回已经变更过的私有__insta方法 # # ''' # if cls.__insta: # return cls.__insta # cls.__insta=object.__new__(b) # return cls.__insta # egon=b('agg',38) # nezha=b('nazha',25) # print(nezha) # print(egon) # print(nezha.name) # print(egon.name)
__eq__ 和 __hash__
# class f: # def __init__(self,name): # self.name=name # # def __eq__(self, other): # if self.name==other.name: # return True # else: # return False # yu1=f('yuan') # yu2=f('yuan') # print(yu1==yu2) class h: def __init__(self,name,sex): self.name=name self.sex=sex def __hash__(self): return hash(self.name+self.sex) h1=h('yu','man') h2=h('yu','man') print(hash(h1)) print(hash(h2))
set 依赖__hash__ __eq__
class hashs: def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex def __eq__(self, other): print(self,other) print('eq2222') if self.name==other.name and self.sex==other.sex: return True else: return False def __hash__(self): print('hash111') return hash(self.name+self.sex) a=hashs('yuan',18,'man') b=hashs('yuan',25,'man') #set的实现 依赖对象hash eq方法 先调用hash 拿到hash值 在调用eq print(set((a,b))) hs=set((a,b)) print(hs)
其他的内置方法
类名.__name__# 类的名字(字符串) 类名.__doc__# 类的文档字符串 类名.__base__# 类的第一个父类(在讲继承时会讲) 类名.__bases__# 类所有父类构成的元组(在讲继承时会讲) 类名.__dict__# 类的字典属性 类名.__module__# 类定义所在的模块 类名.__class__# 实例对应的类(仅新式类中)
类的组合
''' 面向对象的三大特性: 继承 多态 封装 类的组合:一个对象的属性值是另外一个类的对象 w=weapon('粪叉子',100,3,200) yuanbao.get_weapon(w) 通过self 将武器对象绑定成自己的属性值 yuanbao.weapon.hand18(jin) ''' # class dog: # def __init__(self,name,aggr,hp,kind): # self.name=name # self.aggr=aggr # self.hp=hp # self.kind=kind # def bite(self,person): # person.hp-=self.aggr # print('%s被狗咬了,掉了%s滴血'%(person.name,self.aggr)) # # class person: # def __init__(self,name,aggr,hp,sex): # self.name=name # self.aggr=aggr # self.hp=hp # self.sex=sex # self.money=0 # def attack(self,dog): # dog.hp-=self.aggr # print('%s被人打了,掉了%s滴血'%(dog.name,self.aggr)) # def get_weapon(self,weapon): # if self.money>weapon.price: # self.money-=weapon.price # self.aggr+=weapon.statck # self.weapon=weapon # else: # print('钱不够') # # class weapon: # def __init__(self,name,statck,lasting,price): # self.name=name # self.statck=statck # self.lasting=lasting # self.price=price # def hand18(self,person): # if self.lasting>0: # person.hp-=self.statck*2 # self.lasting-=1 # # # # yuanbao=person('蛋蛋',2,100,'未知') # # jin=dog('二哈',100,500,'teddy') # w=weapon('粪叉子',100,3,200) # #充钱 # yuanbao.money=1000 # print(yuanbao.__dict__) # #装备打狗棒 # yuanbao.get_weapon(w) # #打狗 # yuanbao.attack(jin) # print(jin.hp) # # #使用打狗棒大招 # yuanbao.weapon.hand18(jin) # print(jin.hp) ''' 类的组合 : 一个对象的属性是另一类的对象 实际上在A类中将B类的实例化赋值给变量S,然后通过调用的方式使用B中的属性 AA=A.S.func 圆环是由两个圆组成的,圆环的面积是外面圆的面积减去内部圆的面积。圆环的周长是内部圆的周长加上外部圆的周长。 这个时候,我们就首先实现一个圆形类,计算一个圆的周长和面积。然后在"环形类"中组合圆形的实例作为自己的属性来用 ''' # from math import pi # # class roundness: # def __init__(self,r): # self.r=r # def permiter(self): # return pi*self.r*2 # def areas(self): # return pi*pow(self.r,2) # # # class annulus: # def __init__(self,outside_r,inside_r): # self.outs=roundness(outside_r) # self.inside=roundness(inside_r) # # # def annu_per(self): # return self.outs.permiter()+self.inside.permiter() # def annu_areas(self): # return abs(self.outs.areas()-self.inside.permiter()) # # ring=annulus(30,20) # print(ring.annu_areas()) # print(ring.annu_per()) class bir: def __init__(self,year,month,day): self.year=year self.month=month self.day=day class teacher: def __init__(self,name,age,sex,b): self.name=name self.age=age self.sex=sex self.b=b birs=bir(2018,12,8) t1=teacher('yu',18,'man',birs) print(t1.name) print(t1.b.year)