面向对象
''' class Person: #类名 country = "China" # 创建这类的属性 #类属性:静态属性 def __init__(self,name,blood,aggr,sex,money): #初始化方法,self是对象,必须传的参数 self.name = name #对象的属性 self.blood = blood self.aggr = aggr self.sex = sex self.money = money def da(self,dog): #方法,一般情况下必须传self参数,且必须写在第一个,后面还可以传其他参数,是自由的 dog.blood -= self.aggr print("%s被%s打了,掉了%s的血"%(dog.name,self.name,self.aggr)) def get_wuqi(self,wuqi): if self.money >= wuqi.price: self.money -= wuqi.price self.aggr += wuqi.aggr self.wuqi = wuqi else: print("余额不足,请充值") class Dog: def __init__(self,name,blood,aggr,kind): self.name = name self.blood = blood self.aggr = aggr self.kind = kind def yao(self,person): person.blood -= self.aggr print("%s被%s打了,掉了%s的血" % (person.name, self.name, self.aggr)) class Wuqi: def __init__(self,name,price,aggr,njd): self.name = name self.price = price self.aggr = aggr self.njd = njd def dazhao(self,dog): if self.njd >0: dog.blood -= self.aggr*2 self.njd -= 1 if dog.blood>0: print("%s被打死了"%(dog.name)) print(Person.country) #类名 可以查看类中的属性,不需要实例化就可以查看 xiaoli = Person("狗蛋",1000,100,"男",0) #实例化 xiali是对象 zhu = Dog("猪猪侠",2000,10,"泰迪") #对象 = 类名() wuqi = Wuqi("打狗棒",1000,200,10) print(xiaoli.__dict__) #查看所有属性 print(zhu.__dict__["name"]) #查看zhu的name属性值 可以看作是一个字典,属性对应的相当于字典的 # "key",而属性值相当于字典的"value",对象的属性操作,可以适用字典的操作 print(wuqi.__dict__) xiaoli.da(zhu) #调用方法 类名. zhu.yao(xiaoli) xiaoli.get_wuqi(wuqi) xiaoli.money += 1000 xiaoli.get_wuqi(wuqi) xiaoli.da(zhu) xiaoli.wuqi.dazhao(zhu) print(zhu.blood) #查看属性值 print(wuqi.njd)
组合 :一个对象的属性值是另外一个类的对象
xiaoli.wuqi 是 Wuqi类的对象
from math import pi class Circle: def __init__(self,r): self.r = r def area(self): return self.r**2 * pi def perimeter(self): return 2*pi*self.r class Ring: def __init__(self,outside_r,inside_r): self.outside_c = Circle(outside_r) self.inside_c = Circle(inside_r) def area(self): return self.outside_c.area() - self.inside_c.area() def perimeter(self): return self.outside_c.perimeter()+self.inside_c.perimeter() # ring = Ring(20,10) # print(ring.area()) # print(ring.perimeter())
# 创建一个老师类 # 老师有生日 # 生日也可以是一个类 # 组合 class Birthday: def __init__(self,year,month,day): self.year = year self.month = month self.day = day class Course: def __init__(self,course_name,period,price): self.name = course_name self.period = period self.price = price class Teacher: def __init__(self,name,age,sex,birthday): self.name = name self.age = age self.sex = sex self.birthday =birthday self.course = Course('python','6 month',2000) b = Birthday(2018,1,16) egg = Teacher('egon',0,'女',b) print(egg.name) print(egg.birthday.year) print(egg.birthday.month) print(egg.course.price)
## 对象 = 类名() 》实例化
# 过程:
# 类名() 首先 会创造出一个对象,创建了一个self变量
# 调用init方法,类名括号里的参数会被这里接收
# 执行init方法
# 返回self
# 对象能做的事:
# 查看属性
# 调用方法
# __dict__ 对于对象的增删改查操作都可以通过字典的语法进行
# 类名能做的事:
# 实例化
# 调用方法 : 只不过要自己传递self参数
# 调用类中的属性,也就是调用静态属性
# __dict__ 对于类中的名字只能看 不能操作
# 定义类
# class
# 函数 : 方法 动态属性 # 类中可以定义方法,方法都有一个必须传的参数self
# 变量 : 类属性 静态属性 # 类中可以定义静态属性
# __init__方法 初始化方法
# python帮我们创建了一个对象self
# 每当我们调用类的时候就会自动触发这个方法。默认传self
# 在init方法里面可以对self进行赋值
# self是什么 self拥有属性都属于对象
#在类的内部,self就是一个对象
# xiaoli = Person() #实例化
# xiaoli.da(zhu) == Person.da(xiaoli,zhu) #对象调用方法:对象名.方法名 == 类名.方法名(对象名)
# 实例化
# 对象 = 类(参数是init方法的)
# 实例、对象 完全没有区别
# 对象查看属性
# 对象.属性名
# 对象调用方法
# 对象.方法名(参数) #类名.方法名(对象名,参数)
一:我们定义的类的属性到底存到哪里了?有两种方式查看 dir(类名):查出的是一个名字列表 类名.__dict__:查出的是一个字典,key为属性名,value为属性值 二:特殊的类属性 类名.__name__# 类的名字(字符串) 类名.__doc__# 类的文档字符串 类名.__base__# 类的第一个父类(在讲继承时会讲) 类名.__bases__# 类所有父类构成的元组(在讲继承时会讲) 类名.__dict__# 类的字典属性 类名.__module__# 类定义所在的模块 类名.__class__# 实例对应的类(仅新式类中)
# class Course: # language = "Chinese" # def __init__(self,teacher,course_name,period,price): # self.teacher = teacher # self.course_name = course_name # self.period = period # self.price = price # print(Course.language) # 查看类属性 Chinese # Course.language = "English" #修改类的属性 #类名.属性 = 新的属性名 # print(Course.language) # English # python = Course("xiaoli","python","6 months",20000) # linux = Course("zhu","linux","3 montths",13000) # print(python.language) # English # python.language = "Chinese" #相当于给对象python 添加了一个language属性 # print(python.language) # Chinese # print(linux.language) #对象调用类的静态属性 # English # class Course: # language = ["Chinese"] # def __init__(self,teacher,course_name,period,price,): # self.teacher = teacher # self.course_name = course_name # self.period = period # self.price = price # print(Course.language) # 查看类属性 ['Chinese'] # python = Course("xiaoli","python","6 months",20000) # linux = Course("zhu","linux","3 montths",13000) # print(python.language) # ['Chinese'] # python.language[0] = "English" # print(python.language) # ['English'] # print(linux.language) # ['English'] # # # 类中的静态变量 可以被对象和类调用 # # 对于不可变数据类型来说,类变量最好用类名操作 # # 对于可变数据类型来说,对象名的修改是共享的,重新赋值是独立的
继承
class Animal: # def __init__(self,name,aggr,hp): # self.name = name # self.aggr = aggr # self.hp = hp # def eat(self): # print("吃药回血") # self.hp += 100 # class Dog(Animal): # def __init__(self,name,aggr,hp,kind): # Animal.__init__(self,name,aggr,hp) # self.kind = kind #派生属性 # def eat(self): #如果既想实现新的功能也想使用父类原本的功能,还需要在子类中再调用父类 # Animal.eat(self) # self.teeth = 2 # def yao(self,person): #派生方法 # person.hp -= self.aggr # class Person(Animal): # def __init__(self,name,aggr,hp,sex): # super().__init__(name,aggr,hp) #等同于Animal.__init__(self,name,aggr,hp),新式类才有,py3 都是新式类 # self.sex = sex # def gongji(self,dog): # dog.hp -= self.aggr # p = Person("小利",100,100,"男") # d = Dog("疯狗",2,500,"泰迪") # print(Person.__bases__) #查看 Person类的继承情况 #(<class '__main__.Animal'>,)
# # python3 -新式类# 没有继承父类默认继承object
# 父类中没有的属性 在子类中出现 叫做派生属性
# 父类中没有的方法 在子类中出现 叫做派生方法
# 只要是子类的对象调用,子类中有的名字 一定用子类的,子类中没有才找父类的,如果父类也没有报错
# 如果父类 子类都有 用子类的
# 如果还想用父类的,单独调用父类的:
# 父类名.方法名 需要自己传self参数
# super().方法名 不需要自己传self
# 正常的代码中 单继承 === 减少了代码的重复
# 继承表达的是一种 子类是父类的关系
多继承
#class F: # def f(self): # print("F") # class C(F): # def f(self): # print("C") # class D(F): # def f(self): # print("D") # class B(D,C): # def f(self): # print("B") # b = B() # b.f() # B 如果B类里的代码用pass,则找类D中的值,D离B进 B>D>C>A 广度优先
# class E: # def f(self): # print("E") # class C(E): # def f(self): # print("C") # class D: # def f(self): # print("D") # class B(D): # def f(self): # print("B") # class A(B,C): # def f(self): # print("A") # a = A() # a.f() # A # A>B>D>C>E
# class E: # def f(self): # print("E") # class C(E): # def f(self): # print("C") # class D: # def f(self): # print("D") # class B(D): # def f(self): # print("B") # class A(B,C): # def f(self): # print("A") # a = A() # a.f() # A # A>B>D>C>E
继承原理
python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如
>>> A.mro() #等同于A.__mro__ [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.D'>, <class '__main__.F'>, <class 'object'>]
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
继承小结
继承的作用
减少代码的重用 提高代码可读性 规范编程模式
几个名词
抽象:抽象即抽取类似或者说比较像的部分。是一个从具题到抽象的过程。 继承:子类继承了父类的方法和属性 派生:子类在父类方法和属性的基础上产生了新的方法和属性
抽象类与接口类
1.多继承问题 在继承抽象类的过程中,我们应该尽量避免多继承; 而在继承接口的时候,我们反而鼓励你来多继承接口 2.方法的实现 在抽象类中,我们可以对一些抽象方法做出基础实现; 而在接口类中,任何方法都只是一种规范,具体的功能需要子类实现
钻石继承
新式类:广度优先 经典类:深度优先
# class A(object): # def func(self): print('A') # # class B(A): # def func(self): # super().func() # print('B') # # class C(A): # def func(self): # super().func() # print('C') # # class D(B,C): # def func(self): # super().func() # print('D') # d = D() #A>C>B>D # d.func() # # super 只在python3中存在 # # super的本质 :不是单纯找父类 而是根据调用者的节点位置的广度优先顺序来的
继承有两种用途:
一:继承基类的方法,并且做出自己的改变或者扩展(代码重用)
二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能
#接口类 单继承 # from abc import abstractmethod,ABCMeta # class Pay(metaclass=ABCMeta): #元类 # @abstractmethod # def pay(self,money): # pass # # 规范 :接口类或者抽象类都可以 # # 接口类 支持多继承,接口类中的所有的方法都必须不能实现 —— java演化来的 # # 抽象类 不支持多继承,抽象类中方法可以有一些代码的实现 —— java # class Wechat(Pay): # def pay(self,money): # print("已经用微信支付了%s元"%money) # class Ali(Pay): # def pay(self,money): # print("已经用支付宝支付了%s元"%money) # class Apple(Pay): # def pay(self,money): # print("已经用苹果支付了%s元"%money) # def s(pay_obj,money): #统一支付入口 # pay_obj.pay(money) # ali = Ali() # ali.pay(200) # we = Wechat() # we.pay(1) # s(ali,10) # s(we,123) #接口类 多继承 # from abc import abstractmethod,ABCMeta # class Swim_Animal(metaclass=ABCMeta): # @abstractmethod # def swim(self): # pass # class Walk_Animal(metaclass=ABCMeta): # @abstractmethod # def walk(self): # pass # class Fly_Animal(metaclass=ABCMeta): # @abstractmethod # def fly(self): # pass # # class Tiger(Swim_Animal,Walk_Animal): # def swim(self): # print("f") # def walk(self): # pass # def h(self): # print("s") # t = Tiger() # t.swim() # 接口类 刚好满足接口隔离原则 面向对象开发的思想 规范
抽象类
什么是抽象类
与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.方法的实现
在抽象类中,我们可以对一些抽象方法做出基础实现;
而在接口类中,任何方法都只是一种规范,具体的功能需要子类实现
# 抽象类 : 规范
# 一般情况下 单继承 能实现的功能都是一样的,所以在父类中可以有一些简单的基础实现
# 多继承的情况 由于功能比较复杂,所以不容易抽象出相同的功能的具体实现写在父类中
# 抽象类还是接口类 : 面向对象的开发规范 所有的接口类和抽象类都不能实例化
# java :
# java里的所有类的继承都是单继承,所以抽象类完美的解决了单继承需求中的规范问题
# 但对于多继承的需求,由于java本身语法的不支持,所以创建了接口Interface这个概念来解决多继承的规范问题
# python
# python中没有接口类 :
# python中自带多继承 所以我们直接用class来实现了接口类
# python中支持抽象类 : 一般情况下 单继承 不能实例化
# 且可以实现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')
文件有多种形态:文本文件,可执行文件
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),不同的对象在接收时会产生不同的行为(即方法)。 也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。 比如:老师.下课铃响了(),学生.下课铃响了(),老师执行的是下班操作,学生执行的是放学操作,虽然二者消息一样,但是执行的效果不同
多态性
peo=People() dog=Dog() pig=Pig() #peo、dog、pig都是动物,只要是动物肯定有talk方法 #于是我们可以不用考虑它们三者的具体是什么类型,而直接使用 peo.talk() dog.talk() pig.talk() #更进一步,我们可以定义一个统一的接口来使用 def func(obj): obj.talk()
鸭子类型
逗比时刻:
Python崇尚鸭子类型,
# list tuple
# 不崇尚根据继承所得来的相似
# 我只是自己实现我自己的代码就可以了。
# 如果两个类刚好相似,并不产生父类的子类的兄弟关系,而是鸭子类型
# list tuple 这种相似,是自己写代码的时候约束的,而不是通过父类约束的
# 优点 : 松耦合 每个相似的类之间都没有影响
# 缺点 : 太随意了,只能靠自觉
封装
# 广义上面向对象的封装 :代码的保护,面向对象的思想本身就是一种
# 只让自己的对象能调用自己类中的方法
# 狭义上的封装 —— 面向对象的三大特性之一
# 属性 和 方法都藏起来 不让你看见
class Person: __key = 123 #私有静态属性 def __init__(self,name,pwd): self.name = name self.__pwd = pwd #对象私有属性 def __get_pwd(self): #私有方法 return self.__pwd def s(self): #正常调用私有方法 return self.__get_pwd() p = Person("xiaoli",123456) print(p.__dict__) #查看对象p的属性值 print(p._Person__pwd) #查看对象p的密码 _类名__属性名 print(Person._Person__key) #查看Person类的私有静态属性 print(p.s()) #正常调用
# 所有的私有 都是在变量的左边加上双下划綫
# 对象的私有属性
# 类中的私有方法
# 类中的静态私有属性
# 所有的私有的 都不能在类的外部使用
class Foo: __key = '123' # _Foo__key class Son(Foo): print(Foo.__key) # _Son__key # 会用到私有的这个概念de场景 #1.隐藏起一个属性 不想让类的外部调用 #2.我想保护这个属性,不想让属性随意被改变 #3.我想保护这个属性,不被子类继承
# property # 内置装饰器函数 只在面向对象中使用 #属性 查看 修改 删除 class Person: def __init__(self,name): self.__name = name @property # 把属性伪装成一个方法 不能传参数 def name(self): return self.__name @name.deleter # 在外部调用同属性一样的方法名来删除属性,不能传参数 与del配合使用 def name(self): del self.__name @name.setter #在外部调用同属性名一样的方法来修改属性值,只可传一个值 def name(self,new_name): self.__name = new_name brother2 = Person('二哥') brother2.name = 'newName' del brother2.name print(brother2.name)
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_discount(cls,new_discount): # 修改折扣 cls.__discount = new_discount apple = Goods('苹果',5) print(apple.price) Goods.change_discount(0.5) # Goods.change_discount(Goods) print(apple.price) # 当这个方法的操作只涉及静态属性的时候 就应该使用classmethod来装饰这个方法
class Login: def __init__(self,name,password): self.name = name self.pwd = password def login(self):pass @staticmethod def get_usr_pwd(): # 静态方法 usr = input('用户名 :') pwd = input('密码 :') Login(usr,pwd) Login.get_usr_pwd() # 在完全面向对象的程序中, # 如果一个函数 既和对象没有关系 也和类没有关系 那么就用staticmethod将这个函数变成一个静态方法
# 类方法和静态方法 都是类调用的
# 对象可以调用类方法和静态方法么? 可以 一般情况下 推荐用类名调用
# 类方法 有一个默认参数 cls 代表这个类 cls 同对象的self 一样
# 静态方法 没有默认的参数 就象函数一样
反射
class Person: key = 123 def __init__(self,name,sex): self.name = name self.sex = sex def da(self): print("打人") @classmethod def f(cls): print("人生") @staticmethod def login(): print("hh") s = getattr(Person,"key") #通过反射,类名获取静态属性 print(s) # 123 s1 = getattr(Person,"da") #通过反射,类名获取公共方法 print(s1) # <function Person.da at 0x000001AB42A73840> 是一个地址 s1(s) # 打人 #地址加()才能执行这个方法 而必须要传一个参数(因为上面self) s2 =getattr(Person,"f") # 通过反射,类名获取 类方法 print(s2) # <bound method Person.f of <class '__main__.Person'>> s2() # 人生 s3 = getattr(Person,"login") #通过反射,类名调用静态方法 s3() # hh p = Person("h","nan") s4 = getattr(p,"key") #通过反射 对象名获取静态属性 print(s4) # 123 s5 = getattr(p,"da") s5() # 打人 #通过反射,对象名获取方法 #s6 = getattr(p,"ky") # 不论类名 还是 对象名 调用没有值的时候 都是报错 #print(s6) # AttributeError: 'Person' object has no attribute 'ky' if hasattr(p,"ky"): # hasattr 检验是否有这个值,如果有 getattr可以取到,没有则什么都不显示 s7 = getattr(p,"ky") print(s7) #没有任何显示 if hasattr(Person,"key"): # hasattr 和 getattr 是一个组合用 s8 = getattr(Person,"key") print(s8) #123