Python面向对象
三种编程方法论
- 面向过程:把复杂的任务一步一步分解成简单的任务。
- 函数式编程:思想上接近于数学运算,根据某种方式,根据方式得出的结果。
- 面向对象编程:一种编程方式,需要使用“类”和“对象”来实现,其实就是对“类”和“对象的”使用
软件质量属性
软件质量属性包含:成本,性能,可靠性,安全性,可维护性,可移植性,可伸缩性,可扩展性等
面向过程
- 概念:核心是“过程”二字,“过程”指的是解决问题的步骤,即先干什么再干什么......,把这个大的问题分解成很多个小问题或子过程,这些子过程在执行的过程中继续分解,直到小问题足够简单到可以在一个小步骤范围内解决。
- 优点:复杂的问题流程化,进而简单化
- 缺点:扩展性极差
- 应用场景:一般用于那些功能一旦实现之后就很少需要改变的场景, 如果你只是写一些简单的脚本,去做一些一次性任务,用面向过程的方式是极好的,著名的例子有Linux內核,git,以及Apache HTTP Server等。但如果你要处理的任务是复杂的,且需要不断迭代和维护 的, 那还是用面向对象最方便了。
面向对象编程
- 概念:核心是“对象”二字,对象就是特征与技能的结合体。
- 优点:可扩展性高
- 缺点:编程的复杂度高,易出现过度设计的问题
- 应用场景:应用于需求经常变化的软件中,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方。
一、名词解释
- 类:一个类即是对一类拥有相同属性的对象的抽象、蓝图、原型、模板。在类中定义了这些对象的都具备的属性(variables(data))、共同的方法
- 属性:人类包含很多特征,把这些特征用程序来描述的话,叫做属性,比如年龄、身高、性别、姓名等都叫做属性,一个类中,可以有多个属性
- 方法:人类不止有身高、年龄、性别这些属性,还能做好多事情,比如说话、走路、吃饭等,相比较于属性是名词,说话、走路是动词,这些动词用程序来描述就叫做方法。
- 实例(对象):一个对象即是一个类的实例化后实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同
- 实例化:把一个类转变为一个对象的过程就叫实例化 就是类名+括号,就是在执行类的__init__()方法
- 构造方法:__init__(...)被称为 构造方法或初始化方法,在例实例化过程中自动执行,目的是初始化实例的一些属性。每个实例通过__init__初始化的属性都是独有的
- 析构方法(解构方法):__del__(self)被称为 析构方法或解构方法,实例在内存中被删除时,会自动执行这个方法。
二、类和对象
- 类中可以有任意python代码,这些代码在类定义阶段便会执行
- 因而会产生新的名称空间,用来存放类的变量名与函数名,可以通过 类名.__dict__查看
- 对于经典类来说我们可以通过该字典操作类名称空间的名字(新式类有限制),但python为我们提供专门的.语法
- 点是访问属性的语法,类中定义的名字,都是类的属性
- isinstance(obj,cls)检查obj是否是类 cls 的对象
- 类的两种用法:实例化和属性引用(类的属性就两种:变量和函数)
- 对象也叫实例只有一种用法:属性引用,就对象本身而言它只有数据属性
- 构造函数中的self表示创建的当前对象。方法中的self表示谁调用的方法,self就是谁。
- 示例对象访问属性或调用方法时,永远都是先找自己的,找不到就往父类找。
country='American' def study(): print('study english') class Student(): country = 'China' def __init__(self, ID, NAME, SEX, PROVINCE,country): self.id = ID self.name = NAME self.sex = SEX self.province = PROVINCE self.country = country def search_score(self): print('全局的',country) study() #全局的 print('类里的', Student.country) Student.study(self) #类里的 print('对象的', self.country) #对象有就从自身找,没有就从类中找 self.study() # def study(self): print('study',self.country) s1 = Student('3718818181', 'alex', 'female', 'shanxi','Singapore') s1.search_score() """ 全局的 American study english 类里的 China study Singapore 对象的 Singapore study Singapore """
三、__init__方法
- 为对象初始化自己独有的特征
- 该方法内可以有任意的python代码
- 一定不能有返回值*****
四、属性查找
在obj.name会先从obj自己的名称空间里找name,找不到则去类中找,类也找不到就找父类...最后都找不到就抛出异常
- 类有两种属性:数据属性和函数属性
- 类的数据属性是所有对象共享的
- 类的函数属性是绑定给对象用的
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 """ 从自己找b.test(),没有找类B,也没有找类A,找到后执行self.fa(), self指的是b对象,然后又从自己找fa(),没有就去类B中找fa(),找到了 所以打印 from B """ class C: def __fa(self): #_C__fa print('from C') def test(self): self.__fa() #slef._C__fa __在定义阶段就会变形 class D(C): def __fa(self): #_D__fa print('from D') d=D() d.test() #from C """ __不能被子类继承和覆盖 """
五、特性1:封装
封装在于明确区分内外, 类的实现者不需要让类的调用者知道具体的实现过程,隔离了复杂度,同时也提升了安全性,类的实现者可以修改封装内的东西而不影响外部调用者的代码。
在面向对象思想中是把一些看似无关紧要的内容组合到一起,统一进行存储和使用,这就是封装。
A:封装数据的主要原因是:保护隐私
B:封装方法的主要原因是:隔离复杂度
1、封装分为两个层面
封装其实分为两个层面,但无论哪种层面的封装,都要对外界提供好访问你内部隐藏内容的接口(接口可以理解为入口,有了这个入口,使用者无需且不能够直接访问到内部隐藏的细节,只能走接口,并且我们可以在接口的实现上附加更多的处理逻辑,从而严格控制使用者的访问。
第一个层面的封装(构造方法和类本身的包裹):创建类和对象会分别创建二者的名称空间,我们只能用类名.或者obj.的方式去访问里面的名字,这本身就是一种封装。
注意:对于这一层面的封装(隐藏),类名.和实例名.就是访问隐藏属性的接口
第二个层面的封装(__双下划线属性隐藏):类中把某些属性和方法隐藏起来(或者说定义成私有的),只在类的内部使用、外部无法访问,或者留下少量接口(函数)供外部访问。
- 在python中用双下划线开头的方式将属性隐藏起来,设置成私有的
- 变形的过程只在类的定义时发生一次,在定义后的赋值操作,不会变形
- 在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
- 类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。
- 这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。
- 在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。
- 注意:对于这一层面的封装(隐藏),我们需要在类中定义一个函数(接口函数)在它内部访问被隐藏的属性,然后外部就可以使用了。
- 类中所有双下划线开头的名称如__x都会在类定义时自动变形成:_类名__x的形式
- 这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N
- python并不会真的阻止你访问私有的属性,模块也遵循这种约定,如果模块名以单下划线开头,那么from module import *时不能被导入,但是你from module import _private_module依然是可以导入的
- 其实很多时候你去调用一个模块的功能时会遇到单下划线开头的(socket._socket,sys._home,sys._clear_type_cache),这些都是私有的,原则上是供内部调用的,作为外部的你,一意孤行也是可以用的,只不过显得不合适。
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的形式访问到. class Teacher: def __init__(self,name,age): self.__name=name self.__age=age def tell_info(self): print('姓名:%s,年龄:%s' %(self.__name,self.__age)) def set_info(self,name,age): if not isinstance(name,str): raise TypeError('姓名必须是字符串类型') if not isinstance(age,int): raise TypeError('年龄必须是整型') self.__name=name self.__age=age t=Teacher('egon',18) t.tell_info() t.set_info('egon',19) t.tell_info()
2、特性property
property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则。
- @property 类的静态属性:
- 将一个类的函数定义成特性以后,对象再去使用的时候obj.mbi,根本无法察觉自己的mbi是执行了一个函数然后计算出来的,
- @property这种特性的使用方式遵循了统一访问的原则
- @classmethod
- 把一个方法绑定给类,会把类本身当做第一个参数自动传给绑定到类的方法
- 类的绑定方法
- @staticmethod
- 被staticmethod装饰器修饰过的方法,都是解除绑定的方法,实际上就函数:就没有自动传值功能了
- 解除绑定的方法
- 凡是定义在类的内部,并且没有被任何装饰器修饰过的方法,都是绑定方法:有自动传值功能
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 ''' """ ps:面向对象的封装有三种方式: 【public】 这种其实就是不封装,是对外公开的 【protected】 这种封装方式对外不公开,但对朋友(friend)或者子类公开 【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('egon') print(f.name) # f.name=10 #抛出异常'TypeError: 10 must be str' del f.name #抛出异常'TypeError: Can not delete'
import time class Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day @classmethod #把一个方法绑定给类:类.绑定到类的方法(),会把类本身当做第一个参数自动传给绑定到类的方法 def now(cls): #用Date.now()的形式去产生实例,该实例用的是当前时间 t=time.localtime() #获取结构化的时间格式 obj=cls(t.tm_year,t.tm_mon,t.tm_mday) #新建实例并且返回 return obj @classmethod def tomorrow(cls):#用Date.tomorrow()的形式去产生实例,该实例用的是明天的时间 t=time.localtime(time.time()+86400) return cls(t.tm_year,t.tm_mon,t.tm_mday) class EuroDate(Date): def __str__(self): return '年:%s,月:%s,日:%s' %(self.year,self.month,self.day) e1=EuroDate.now() print(e1) e1=EuroDate(1,1,1) print(e1) """ 年:2019,月:3,日:17 年:1,月:1,日:1 """
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() #获取结构化的时间格式 obj=Date(t.tm_year,t.tm_mon,t.tm_mday) #新建实例并且返回 return obj @staticmethod def tomorrow():#用Date.tomorrow()的形式去产生实例,该实例用的是明天的时间 t=time.localtime(time.time()+86400) return Date(t.tm_year,t.tm_mon,t.tm_mday) d1=Date.now() print(d1.year,d1.month,d1.day) d2=Date.tomorrow() print(d2.day)
##示例对象访问属性或调用方法时,永远都是先找自己的,找不到就往父类找。 class A: def __init__(self, num): self.num = num def func1(self): self.func2() def func2(self): print("A.func2", self.num) class B(A): def func2(self): print("B.func2", self.num) list1 = [A(1), A(2), B(3)] for i in list1: i.func2() """ A.func2 1 A.func2 2 B.func2 3 """ for i in list1: i.func1() """ A.func2 1 A.func2 2 B.func2 3 """ ################################################################################## class Person: # 类变量(静态变量),所有实例对象都共享这个变量 示例对象只能查看不能修改。想要修改类变量,只能通过类名来修改。 country = '中国' # 方法 def __init__(self, name, age): # 属性 self.name = name self.age = age # 方法 def say(self): print('我是{self.name},我今年{self.age}岁!'.format(self=self)) p1 = Person('张三', 18) p2 = Person('李四', 20) print(p1.country, p2.country) # 中国 中国 print(id(p1.country), id(p2.country)) # 4567249032 4567249032 p3 = Person('王五', 3000) p3.country = '大秦' print(p3.name) # 王五 print(p3.country) # 大秦 p4 = Person('赵六', 30) print(p4.name) # 赵六 print(p4.country) # 中国 """ 在上面的代码中,__init__和say都是属于类的成员方法,又称为实例方法。 self.name和self.age都是实例对象的属性,这些称为成员变量或实例变量。 注意,p3.country = '大秦'的时候,并没有去改变类中的country, 而是给自己添加了一个实例属性,这个属性只有在p3这个实例对象中才是存在的,在p4中是不存在的。 """ ################################################################################## class Foo: count = 0 def __init__(self): Foo.count += 1 print(Foo.count) # 0 Foo() Foo() Foo() print(Foo.count) # 3 ################################################################################## class Computer: # 实例方法 def play(self): print('玩游戏') @staticmethod # 静态方法和静态变量一样,都可以使用类名直接访问。 def fire(): # 方法不需要传入实例对象变量 print('煎鸡蛋') @classmethod def calc(cls, a, b): print(cls) return a + b #实例方法 c1 = Computer() c1.play() #玩游戏 实例对象直接调用实例方法 # Computer.play() #报错 Computer.play(c1) #玩游戏 类调用的时候,需要把实例作为第一个参数传入 #静态方法 c1.fire() #煎鸡蛋 Computer.fire() #煎鸡蛋 #类方法 ret = Computer.calc(10, 20) #<class '__main__.Computer'> print(ret) # 30 ret2 = c1.calc(10, 20) #<class '__main__.Computer'> print(ret2) # 30 #属性方法: 其实就是把类中的方法改造成属性的一种写法,该写法需要在方法上加上@property ################################################################################## class Goods: def __init__(self): # 原价 self.original_price = 100 # 折扣 self.discount = 0.8 @property def price(self): # 实际价格 = 原价 * 折扣 new_price = self.original_price * self.discount return new_price @price.setter def price(self, value): self.original_price = value @price.deleter def price(self): del self.original_price obj = Goods() print(obj.price) # 80.0 获取商品价格 obj.price = 200 # 修改商品原价 print(obj.price) #160.0 del obj.price # 删除商品原价 # print(obj.price) # AttributeError: 'Goods' object has no attribute 'original_price' ################################################################################## class A: __secret = '密码' def __init__(self, salary): self.__salary = salary def __play(self): print('嘿嘿嘿') class B(A): def dazuiba(self): self.__play() b1 = B(200) # print(b1.__salary) # 'B' object has no attribute '__salary' # b1.__play() # 'B' object has no attribute '__play' # b1.dazuiba() # AttributeError: 'B' object has no attribute '_B__play' # print(b1.__secret) # AttributeError: 'B' object has no attribute '__secret' # print(B.__secret) # AttributeError: type object 'B' has no attribute '__secret' # print(A.__secret) # AttributeError: type object 'A' has no attribute '__secret' """ 私有成员: 1.只能够在类的内部调用,类的外部无法调用 2.不能被继承。 """
六、特性2:继承
继承是一种创建新类的方式,新建的类可以继承一个或多个父类(python支持多继承),父类又可称为基类或超类,新建的类称为派生类或子类。解决了代码重复的问题
1、继承顺序
- python会计算出一个方法解析顺序(mro)列表,这个mro列表就是一个简单的所有基类的线性顺序列表 F.mro()等同于F.__mro__ 注意只有新式类才有这个方法
- 子类会先于父类被检查
- 多个父类会根据它们在列表中的顺序被检查
- 如果对下一个类存在两个合法的选择,选择第一个父类
- 经典类的深度优先查找和新式类的广度优先查找
Python的类可以继承多个类。继承多个类的时候,其属性的寻找的方法有两种,分别是深度优先和广度优先。
如下的结构,新式类和经典类的属性查找顺序都一致。顺序为D--->A--->E--->B--->C。
class E: def test(self): print('from E') class A(E): def test(self): print('from A') class B: def test(self): print('from B') class C: def test(self): print('from C') class D(A,B,C): def test(self): print('from D') d=D() d.test() print(D.mro()) #新式类才可以查看.mro()方法查看查找顺序 ''' from D [<class '__main__.D'>, <class '__main__.A'>, <class '__main__.E'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>] '''
如下的结构,新式类和经典类的属性查找顺序就不一样了。
经典类遵循深度优先,其顺序为:F--->E--->B--->A--->F--->C--->G--->D
新式类遵循广度优先,其顺序为:F--->E--->B--->F--->C--->G--->D--->A
2、继承原理
python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如:
print(D.mro()) ''' [<class '__main__.D'>, <class '__main__.A'>, <class '__main__.E'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>] '''
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查。
2.多个父类会根据它们在列表中的顺序被检查。
3.如果对下一个类存在两个合法的选择,选择第一个父类。
3、子类中调用父类的方法
- 方法一:父类名.父类方法() 父类名.__init__(self,name,speed,load,power)
- 方法二:【super只能用于新式类】super(自己的类,self).父类的函数名字
python3中写法:super().__init__(name,speed,load,power)
python2中写法:super(Subway,self).__init__(name,speed,load,power) - 二者使用哪一种都可以,但最好不要混合使用,注意区分参数self
- 这两种方式的区别是:方式一是跟继承没有关系的,而方式二的super()是依赖于继承的,并且即使没有直接继承关系,super仍然会按照mro继续往后查找
- super(Subway,self) 就相当于实例本身 在python3中super()等同于super(Subway,self)
- issubclass(sub, super)检查sub类是否是 super 类的派生类
子类继承了父类的方法,然后想进行修改,注意了是基于原有的基础上修改,那么就需要在子类中调用父类的方法。
方法一:父类名.父类方法()
#_*_coding:utf-8_*_ 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引发的惨案
#每个类中都继承了且重写了父类的方法 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) f1=D() print(D.__mro__) ''' D的构造方法 B的构造方法 A的构造方法 C的构造方法 A的构造方法 (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>) '''
使用super()的结果
class A: def __init__(self): print('A的构造方法') class B(A): def __init__(self): print('B的构造方法') super().__init__() #super(B,self).__init__() class C(A): def __init__(self): print('C的构造方法') super().__init__() #super(C,self).__init__() class D(B,C): def __init__(self): print('D的构造方法') super().__init__() #super(D,self).__init__() # C.__init__(self) f1=D() print(D.__mro__) ''' D的构造方法 B的构造方法 C的构造方法 A的构造方法 (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>) '''
当你使用super()函数时,Python会在MRO列表上继续搜索下一个类。只要每个重定义的方法统一使用super()并只调用它一次,那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次(注意注意注意:使用super调用的所有属性,都是从MRO列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看MRO列表)
4、经典类与新式类
- 只有在python2中才分新式类和经典类,python3中统一都是新式类
- 在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类
- 在python2中,显式地声明继承object的类,以及该类的子类,都是新式类
- 在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类
- A.__bases__ 或者 A.__mro__查看类A的继承。mro()方法只在新式类中有
5、继承和抽象(先抽象再继承)
- 抽象即提取类似的部分。
- 基类就是抽象多个类共同的属性得到的一个类。
6、派生
- 派生就是子类在继承父类的基础上衍生出新的属性。
- 子类中独有的,父类中没有的或子类定义与父类重名的属性,那么在调用子类的这个属性时就以子类自己的为准。
- 子类也叫派生类。
七、特性3:多态
- 多态:指的是一类事物有多种形态,比如动物有多种形态:人,狗,猪
- 多态性:指在不考虑实例类型的情况下使用实例,多态性分为静态多态性和动态多态性,想想len 函数的使用
- 多态性是指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同功能的函数。
- 静态多态性:如任何类型都可以用运算符+进行运算
- 动态多态性:len函数
- 实现:不同的类先继承基类,然后不同类的实例调用相同的方法(这个相同的方法是从基类那继承来的)
- 作用:增加了程序的灵活性和可扩展性,(使用者都是同一种形式去调用)
#多态:同一种事物的多种形态,动物分为人类,猪类(在定义角度) class Animal: def run(self): raise AttributeError('子类必须实现这个方法') class People(Animal): def run(self): print('人正在走') class Pig(Animal): def run(self): print('pig is walking') class Dog(Animal): def run(self): print('dog is running') peo1=People() pig1=Pig() d1=Dog() peo1.run() pig1.run() d1.run() #多态性:一种调用方式,不同的执行效果(多态性) def func(obj): obj.run() func(peo1) func(pig1) func(d1) # peo1.run() # pig1.run() # 多态性依赖于: # 1.继承 # 2. ##多态性:定义统一的接口, def func(obj): #obj这个参数没有类型限制,可以传入不同类型的值 obj.run() #调用的逻辑都一样,执行的结果却不一样 func(peo1) func(pig1)
其他补充
一、类的组合
软件重用的重要方式除了继承之外还有另外一种方式,即:组合 组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合
当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好
class Date: #定义日期类 def __init__(self,name,year,mon,day): self.name=name self.year=year self.mon=mon self.day=day def tell_birth(self): print('%s %s-%s-%s'%(self.name,self.year,self.mon,self.day)) class Course: #定义科目类 def __init__(self, name, price, period): self.name = name self.price = price self.period = period def tell_course(self): print('''----------%s info---------- course name:%s course price:%s course period:%s''' % (self.name, self.name, self.price, self.period)) class Person: #定义人类 def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex self.courses=[] def walk(self): print('%s is walking' %self.name) def tell_course(self): for obj in self.courses: obj.tell_course() class Teacher(Person): #定义老师类,继承Person def __init__(self,name,age,sex,salary,level): Person.__init__(self,name,age,sex) self.salary=salary self.level=level def teach(self): print('%s is teaching' %self.name) def tell_info(self): print('''----------%s info --------- NAME:%s AGE: %s SEX: %s SAL: %s LEVEL:%s'''%(self.name,self.name,self.age,self.sex,self.salary,self.level)) class Student(Person): #定义学生类,继承Person def __init__(self,name,age,sex,group): Person.__init__(self,name,age,sex) self.group=group def study(self): print('%s is teaching' %self.name) def tell_info(self): print('''----------%s info --------- NAME:%s AGE: %s SEX: %s GROUP: %s'''%(self.name,self.name,self.age,self.sex,self.group)) egon=Teacher('egon',18,'male',3000,10) python=Course('Python',15800,'6mons') linux=Course('Linux',1800,'3mons') egon.courses.append(python) egon.courses.append(linux) egon.tell_course() egon.birth=Date('egon',1991,11,11) egon.birth.tell_birth() xh=Student('xh',18,'male','group1') xh.courses.append(python) xh.tell_course() xh.tell_info()
二、抽象类(接口与归一化设计)
抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化。
1、为什么要有抽象类
如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。
比如我们有香蕉的类,有苹果的类,有桃子的类,从这些类抽取相同的内容就是水果这个抽象的类,你吃水果时,要么是吃一个具体的香蕉,要么是吃一个具体的桃子。。。。。。你永远无法吃到一个叫做水果的东西。
从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。
从实现角度来看,抽象类与普通类的不同之处在于:抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的。
2、接口
- 接口:接口提取了一群类共同的函数,可以把接口当做一个函数的集合,然后让子类去实现接口中的函数。
- 作用:归一化设计,限制实现接口的类必须按照接口给定的调用方式实现这些方法;对高层模块隐藏了类的内部实现。
- hi boy,给我开个查询接口。。。此时的接口指的是:自己提供给使用者来调用自己功能的方式\方法\入口
- raise NotImplementedError('子类必须实现这个方法')
3、归一化
- 归一化:就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。
- 好处:
- 归一化让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
- 归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合
class Animal: def run(self): raise AttributeError('子类必须实现这个方法') def speak(self): raise AttributeError('子类必须实现这个方法') class People(Animal): def run(self): print('人正在走') def speak(self): print('说话') class Pig(Animal): def run(self): print('pig is walking') def speak(self): print('哼哼哼') # peo1=People() # pig1=Pig() # # peo1.run() # pig1.run()
4、抽象类
- 抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化
- 类是从一堆对象中抽取相同的内容而来的,抽象类是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。
- 抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法。
#_*_coding:utf-8_*_ #抽象类:本质还是类,与普通类额外的特点的是:加了装饰器的函数,子类必须实现他们 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)
5、抽象类与接口
- 抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性。
- 抽象类是一个介于类和接口之间的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计
三、类中定义的函数分成两大类
1、绑定方法(绑定给谁,谁来调用就自动将它本身当作第一个参数传入)
- 绑定到类的方法:用classmethod装饰器装饰的方法。
- 为类量身定制
- 类.bound_method(),自动将类当作第一个参数传入
- (其实对象也可调用)
- 绑定到对象的方法:没有被任何装饰器装饰的方法。
- 为对象量身定制
- 对象.bound_method(),自动将对象当作第一个参数传入
- (属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值那么一说)
2、非绑定方法:用staticmethod装饰器装饰的方法
- 不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说。就是一个普通工具而已
- 注意:与绑定到对象方法区分开,在类中直接定义的函数,没有被任何装饰器装饰的,都是绑定到对象的方法,可不是普通函数,对象调用该方法会自动传值,而staticmethod装饰的方法,不管谁来调用,都没有自动传值一说
#!/usr/bin/env python # -*- coding: utf-8 -*- class A: def __init__(self): pass def func1(self): print("func1",self) @classmethod def func2(self): print("func2",self) @staticmethod def func3(self): print("func3", self) a=A() A.func2() #func2 <class '__main__.A'> a.func2() #func2 <class '__main__.A'> #对象调用绑定到类的方法不需要传第一个参数 a.func1() #func1 <__main__.A object at 0x00000000041A52E8> A.func1(a) #func1 <__main__.A object at 0x00000000041A52E8> #类调用绑定到对象的方法需要传第一个参数 a.func3(a) #func3 <__main__.A object at 0x00000000041A52E8> A.func3(a) #func3 <__main__.A object at 0x00000000041A52E8> a.func3(A) #func3 <class '__main__.A'> A.func3(A) #func3 <class '__main__.A'>
四、反射
通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
- hasattr(object,name) 判断object中有没有一个name字符串对应的方法或属性
- getattr(object, name, default=None)
- setattr(x, y, v)
- delattr(x, y)
class Teacher: school = 'oldboy' def __init__(self,name,age): self.name = name self.age = age def teach(self): print('%s i teaching'%self.name) ''' 定义一个老师的类 有3个数据属性,1个函数属性 ''' # hasattr(object,name) 判断object中有没有一个name字符串对应的方法或属性。 print(hasattr(Teacher,'school')) #判断Teacher中是否有school print(hasattr(Teacher,'teach')) #判断Teacher中是否有teach ''' True True ''' # getattr(object, name, default=None) 从object中取一个name字符串对应的方法或属性,没有返回default print(getattr(Teacher,'school',-1)) print(getattr(Teacher,'teach',-1)) print(getattr(Teacher,'student',-1)) ''' oldboy <function Teacher.teach at 0x000000000280BB70> -1 ''' #setattr(x, y, v) 将对象x中的字符串为y的属性设置为v setattr(x, 'y', v) is equivalent to “x.y=v”' print(getattr(Teacher,'school',-1)) setattr(Teacher,'school',123) print(Teacher.school) ''' oldboy 123 ''' #delattr(x, y) 将对象x中的字符串为y的属性删除 delattr(x, 'y') is equivalent to “del x.y” print(getattr(Teacher,'school',-1)) delattr(Teacher,'school') print(hasattr(Teacher,'school')) ''' oldboy False '''
import sys def s1(): print ('s1') def s2(): print ('s2') this_module = sys.modules[__name__] print(hasattr(this_module, 's1')) print(getattr(this_module, 's2')) ''' True <function s2 at 0x00000000027FBAE8> '''
import sys def add(): print('add') def change(): print('change') def search(): print('search') def delete(): print('delete') this_module=sys.modules[__name__] #获取当前模块 while True: cmd=input('>>:').strip() if not cmd:continue if hasattr(this_module,cmd): func=getattr(this_module,cmd) func() #多分支判断方式1: # func_dic={ # 'add':add, # 'change':change, # 'search':search, # 'delete':delete # } # # while True: # cmd=input('>>:').strip() # if not cmd:continue # if cmd in func_dic: # func=func_dic.get(cmd) # func() #多分支判断方式2: # class Service: # def run(self): # while True: # inp = input('>>: ').strip() # cmd='get a.txt' # cmds = inp.split() # cmds=['get','a.txt'] # if hasattr(self, cmds[0]): # func = getattr(self, cmds[0]) # func(cmds) # # def get(self, cmds): # print('get.......', cmds) # # def put(self, cmds): # print('put.......', cmds) # # obj = Service() # obj.run()
#ftpclient.py class FtpClient: 'ftp客户端,但是还么有实现具体的功能' def __init__(self,addr): print('正在连接服务器[%s]' %addr) self.addr=addr def test(self): print('test') def get(self): print('get------->') #ftpserver.py #服务端同学可以不用非等着客户端同学写完代码才去实现 import ftpclient f1=ftpclient.FtpClient('192.168.1.1') if hasattr(f1,'get'): #有就执行没有就执行其他逻辑 func=getattr(f1,'get') func() else: print('其他逻辑')
#!/usr/bin/env python # -*- coding:utf-8 -*- # 先定义类 后产生对象 class Animal: def eat(self): print('is eating') class Student(Animal): # 继承,可继承多个,用mro()查看继承关系 school = 'Peking University' # 类的数据属性 count = 0 def __init__(self, name, age): # 类的函数属性,也就是对象的绑定方法() self.name = name self.age = age Student.count += 1 def learn(self): print('%s is learning' % self.name) def eat(self): # 重写父类方法 print('Wash your hands before eating') # 加入自己新功能 # Peo2.eat(self) #重用父类的方法 # super(Student,self).eat() #重用父类的方法 super().eat() # 重用父类的方法 super是按照mro的顺序查找 """ 类: 1、类的数据属性是所有对象共享的,id都一样 2、类的函数属性是绑定给对象用的,obj.method称为绑定方法,绑定到不同的对象是不同的绑定方法,对象调用绑定方式时,会把对象本身当作第一个传入,传给self (绑定到对象的方法的这种自动传值的特征,决定了在类中定义的函数都要默认写一个参数self,self可以是任意名字,但是约定俗成地写出self。 ) 3、__init__方法 (1)、该方法内可以有任意的python代码 (2)、一定不能有返回值 继承: """ print(issubclass(Student, Animal))# 检查Student类是否是Animal类的派生类 # 增 Student.country = 'China' # 删 del Student.country # 改 Student.school = 'Tsinghua University' # 查 print(Student.__dict__) # 查看类的名称空间 print(Student.school) print(Student.learn) print(Student.__mro__) # 查看类的继承关系 print(Student.__bases__) # 查看类的父类 """ 对象: 属性查找顺序:在obj.name会先从obj自己的名称空间里找name,找不到则去类中找,类也找不到就找父类...最后都找不到就抛出异常 """ stu = Student('tom', 18) # 实例化一个对象 print(isinstance(stu,Student)) #obj是否是类 cls 的对象 # 增 stu.gender = 'male' # 删 del stu.gender # 改 stu.age = 28 # 查 print(stu.__dict__) print(stu.name) stu.eat() """ super 按照mro的顺序查找 """ class A: def f1(self): print('from A') super().f1() class B: def f1(self): print('from B') class C(A, B): pass print(C.mro()) # [<class '__main__.C'>, # <class '__main__.A'>, # <class '__main__.B'>, # <class 'object'>] c = C() c.f1() # 打印 from A from B """ 封装:类的实现者不需要让类的调用者知道具体的实现过程 """ """ __私有属性,私有方法 (类的定义极端就已经变形了) 这种变形的特点: 1、在类外部无法直接obj.__AttrName 2、在类内部是可以直接使用:obj.__AttrName 3、子类无法覆盖父类__开头的属性 4、在定义阶段就会变形,也就是说__开头的属性(变量和函数)不能被子类继承 因为在定义阶段__属性名就变成了_父类名__属性名。 """ class A: def __foo(self): # _A__foo print('A.foo') def bar(self): print('A.bar') self.__foo() # self._A__foo() class B(A): def __foo(self): # _B__foo print('B.foo') b = B() b.bar() """ @property 类的静态属性,类方法,类的静态方法 在类内部定义的函数,分为两大类: 一:绑定方法:绑定给谁,就应该由谁来调用,谁来调用就回把调用者当作第一个参数自动传入 绑定到对象的方法:在类内定义的没有被任何装饰器修饰的 绑定到类的方法:在类内定义的被装饰器classmethod修饰的方法 二:非绑定方法:没有自动传值这么一说了,就类中定义的一个普通工具,对象和类都可以使用 非绑定方法:不与类或者对象绑定 """ import hashlib, time class Teacher: def __init__(self, name, weight, height): self.name = name self.weight = weight self.height = height self.id = self.create_id() @property # 类的静态属性@property 封装的部分体现 def bmi(self): return self.weight / (self.height ** 2) @classmethod # 绑定到类的方法 def talk(cls): print('is talking') @staticmethod # 非绑定方法 类的静态方法,就是一个普通工具 def create_id(): m = hashlib.md5(str(time.time()).encode('utf-8')) return m.hexdigest() def play(self): # 绑定到对象的方法 print('is playing') t = Teacher('jack', 80, 1.8) print(t.bmi) print(t.id) """ 多态: 不考虑对象的类型,直接使用对象,也就是不同类的实例调用相同的方法,实现的过程不一样。 多态怎么来的? 1,不同的类先继承基类, 2,然后不同类的实例调用相同的方法(这个相同的方法是从基类那继承来的) 抽象类: 本质还是类,于普通类不一样的是,加了装饰器的函数,子类必须实现他们,也就是抽象类的方法必须要重写 """ import abc class People(metaclass=abc.ABCMeta): # 抽象类,只能被继承,不能被实例化 @abc.abstractmethod def answer(self): pass class Chinese(People): def answer(self): # 抽象类的方法必须要重写 print('用中文答辩') class English(People): def answer(self): # 抽象类的方法必须要重写 print('use english') chi = Chinese() eng = English() def ans(obj): # 相同的调用方式 obj.answer() ans(chi) # 相同的调用方式 多态 ans(eng) # 相同的调用方式 """ 类的组合: 软件重用的重要方式除了继承之外还有另外一种方式,即:组合 组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合 """ class Date: def __init__(self, year, mon, day): self.year = year self.mon = mon self.day = day def tell_info(self): print('My birthday is %s-%s-%s' % (self.year, self.mon, self.day)) d = Date(1986, 6, 26) stu2 = Student('rose', 88) stu2.birth = d stu2.birth.tell_info() """ 反射:通过字符串映射到对象的属性 setattr(obj,'sex','male') getattr(obj,'namexxx',None) hasattr(obj,'talk') delattr(obj,'age') """ class Service: def run(self): while True: inp = input('>>: ').strip() # cmd='get a.txt' cmds = inp.split() # cmds=['get','a.txt'] if hasattr(self, cmds[0]): func = getattr(self, cmds[0]) func(cmds) def get(self, cmds): print('get.......', cmds) def put(self, cmds): print('put.......', cmds) obj = Service() # obj.run()
五、类名称空间与对象/实例名称空间
- 创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性。
- 类有两种属性:数据属性和函数属性,其中类的数据属性是共享给所有对象的。
- 创建一个对象/实例就会创建一个对象/实例的名称空间,存放对象/实例的名字,称为对象/实例的属性。
- 在obj.name会先从obj自己的名称空间里找name,找不到则去类中找,类也找不到就找父类...最后都找不到就抛出异常。
class Garen: camp='Demacia' def __init__(self,nickname,aggressivity=58,life_value=455): self.nickname=nickname self.aggressivity=aggressivity self.life_value=life_value def attack(self,enemy): enemy.life_value-=self.aggressivity r1=Garen('锐雯雯') #类的数据属性是共享给所有对象的。 print(id(r1.camp)) #本质就是在引用类的camp属性,二者id一样 print(id(Garen.camp)) #类的函数属性是绑定到所有对象的: print(id(r1.attack)) print(id(Garen.attack)) ''' r1.attack就是在执行Riven.attack的功能,python的class机制会将Riven的函数属性attack绑定给r1,r1相当于拿到了一个指针,指向Riven类的attack功能 除此之外r1.attack()会将r1传给attack的第一个参数 ''' """ 一:我们定义的类的属性到底存到哪里了?有两种方式查看 dir(类名):查出的是一个名字列表 类名.__dict__:查出的是一个字典,key为属性名,value为属性值 二:特殊的类属性 类名.__name__# 类的名字(字符串) 类名.__doc__# 类的文档字符串 类名.__base__# 类的第一个父类(在讲继承时会讲) 类名.__bases__# 类所有父类构成的元组(在讲继承时会讲) 类名.__dict__# 类的字典属性 类名.__module__# 类定义所在的模块 类名.__class__# 实例对应的类(仅新式类中) 对象/实例只有一种作用:属性引用 对象/实例本身只有数据属性,但是python的class机制会将类的函数绑定到对象上,称为对象的方法,或者叫绑定方法, 绑定方法唯一绑定一个对象,同一个类的方法绑定到不同的对象上,属于不同的方法,内存地址都不会一样。 查看实例属性 同样是dir和内置__dict__两种方式 特殊实例属性 __class__ __dict__ """
class Student: def __init__(self, name, age, sex, group): self.name=name self.age=age self.sex=sex self.group=group def study(self): print('%s is study' % self.name) def tell_info(self): print(''' ----------%s info--------- NAME:%s AGE:%s SEX:%s group:%s ''' %(self.name,self.name,self.age,self.sex,self.group)) import pickle xh=Student('xh',18,'male','group1') with open('studentdb.pkl','wb')as f: pickle.dump(xh,f) with open('studentdb.pkl','rb')as f: obj=pickle.load(f) obj.tell_info()
六、__del__
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
""" 为什么要用property 将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的, 这种特性的使用方式遵循了统一访问的原则在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('egon') print(f.name) # f.name=10 #抛出异常'TypeError: 10 must be str' del f.name #抛出异常'TypeError: Can not delete' ################################################################# # 一个静态属性property本质就是实现了get,set,delete三种方法 class Foo: @property def AAA(self): print('get的时候运行我啊') @AAA.setter def AAA(self,value): print('set的时候运行我啊') @AAA.deleter def AAA(self): print('delete的时候运行我啊') #只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter f1=Foo() f1.AAA f1.AAA='aaa' del f1.AAA ################################################# class Foo: def get_AAA(self): print('get的时候运行我啊') def set_AAA(self,value): print('set的时候运行我啊') def delete_AAA(self): print('delete的时候运行我啊') AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应 f1=Foo() f1.AAA f1.AAA='aaa' del f1.AAA ################################################### class Goods: def __init__(self): # 原价 self.__original_price = 100 # 折扣 self.__discount = 0.8 @property def price(self): # 实际价格 = 原价 * 折扣 new_price = self.__original_price * self.__discount return new_price @price.setter def price(self, value): self.__original_price = value @price.deleter def price(self): del self.__original_price obj = Goods() print(obj.price) # 获取商品价格 obj.price = 200 # 修改商品原价 print(obj.price) del obj.price # 删除商品原价
""" l=[123,456,789] info={"name":"alex","age":1000} s="hello" print(len(l)) print(len(info)) print(len(s)) """ ##################### 归一化设计 ##################### # 支付宝 微信 银行卡 nfc支付 class AliPay(object): def __init__(self,name,money): self.money=money self.name=name def pay(self): # 支付宝提供了一个网络上的联系渠道 print('%s通过支付宝消费了%s元'%(self.name,self.money)) class WeChatPay(object): def __init__(self,name,money): self.money=money self.name=name def pay(self): # 微信提供了一个网络上的联系渠道 print('%s通过微信消费了%s元'%(self.name,self.money)) def pay_func(pay_obj): pay_obj.pay() alipay=AliPay("alex",100) wechatpay=WeChatPay("yuan",200) pay_func(alipay) pay_func(wechatpay) ##################### 规范化方法 ##################### ''' 规范化方法 支付宝 微信 银行卡 nfc支付 同事协作之间的代码规范问题 规定:Payment 就是一个规范类,这个类存在的意义不在于实现实际的功能,而是为了约束所有的子类必须实现pay的方法 Payment : 抽象类 pay = Payment() # 抽象类: 不能实例化 抽象类主要就是作为基类/父类,来约束子类中必须实现的某些方法 抽象类的特点: 必须在类定义的时候指定metaclass = ABCMeta 必须在要约束的方法上方加上@abstractmethod方法 ''' from abc import ABCMeta,abstractmethod #(抽象方法) class Payment(metaclass=ABCMeta): # metaclass 元类 metaclass = ABCMeta表示Payment类是一个规范类 def __init__(self,name,money): self.money=money self.name=name @abstractmethod # @abstractmethod表示下面一行中的pay方法是一个必须在子类中实现的方法 def pay(self,*args,**kwargs): pass @abstractmethod def back(self): pass class AliPay(Payment): def pay(self): # 支付宝提供了一个网络上的联系渠道 print('%s通过支付宝消费了%s元'%(self.name,self.money)) class WeChatPay(Payment): def pay(self): # 微信提供了一个网络上的联系渠道 print('%s通过微信消费了%s元'%(self.name,self.money)) def pay_func(pay_obj): pay_obj.pay() alipay=AliPay("alex",100) wechatpay=WeChatPay("yuan",200) pay_func(alipay) pay_func(wechatpay) #当子类和父类都存在相同的pay()方法时,我们说,子类的pay()覆盖了父类的pay(), # 在代码运行的时候,总是会调用子类的pay()。这样,我们就获得了继承的另一个好处:多态。 ''' 你会发现,新增一个Payment的子类,不必对pay()做任何修改,实际上,任何依赖Payment作为参数的函数或者方法都可以不加修改地正常运行,原因就在于多态。 多态的好处就是,当我们需要传入AliPay、WeChatPay、ApplePay……时,我们只需要接收Payment类型就可以了,因为AliPay、WeChatPay、ApplePay……都 是Payment类型,然后,按照Payment类型进行操作即可。由于Payment类型有pay()方法,因此,传入的任意类型,只要是Payment类或者子类,就会自动调用实 际类型的pay()方法,这就是多态的意思: 对于一个变量,我们只需要知道它是Payment类型,无需确切地知道它的子类型,就可以放心地调用pay()方法,而具体调用的pay()方法是作用在AliPay、WeChatPay、 ApplePay哪个类对象上,由运行时该对象的确切类型决定,这就是多态真正的威力:调用方只管调用,不管细节,而当我们新增一种Payment的子类时,只要确保pay()方 法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则: 对扩展开放:允许新增Payment子类; 对修改封闭:不需要修改依赖Payment类型的pay()等函数。 ''' class ApplePay(Payment): def pay(self): print('%s通过苹果支付消费了%s元'%(self.name,self.money)) applepay=ApplePay("egon",800) ##################### 鸭子类型 ##################### """ 对于静态语言(例如Java)来说,如果需要传入Payment类型,则传入的对象必须是Payment类型或者它的子类,否则,将无法调用pay()方法。 对于Python这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个pay()方法就可以了: 这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。 """ class CardPay(object): def __init__(self,name,money): self.money=money self.name=name def pay(self): print('%s通过银联卡支付消费了%s元'%(self.name,self.money)) def pay_func(pay_obj): pay_obj.pay() cp=CardPay("alvin",1000) pay_func(cp)
# 1 初始化方法:__init__ class A(object): def __init__(self): print("初始化执行方法") A() # 2 构造方法:__new__ class B(object): def __new__(cls,*args,**kwargs): print("我是用来开辟一块空间的") obj=super().__new__(cls) return obj def __init__(self): print("self就是__new__开辟的空间地址") B() # 应用:比如单例模式 # 3 __str__ class C(object): def __init__(self,name,age): self.name=name self.age=age def __str__(self): # 必须返回字符串类型 return self.name c1=C("c1",20) c2=C("c2",23) print(c1) print(c2) # 4 __call__ class D(object): def __call__(self, *args, **kwargs): print("call 被调用...") print(callable(D)) d1=D() print(callable(d1)) d1() # 5 析构方法 __del__ class F(object): def __del__(self): print("删除对象时被调用!") f=F() # del f # 思考:注释掉为什么也会调用__del__ # import time # time.sleep(100) # 应用 class Filehandler(object): file="a.text" def __init__(self): self.f=open(self.file) def __del__(self): self.f.close() # 6 __getitem__ class G(object): def __init__(self): pass def __getitem__(self,item): print("__getitem__被调用") def __setitem__(self, key, value): print("__setitem__被调用") def __delitem__(self, key): print("__delitem__被调用") g=G() g["name"]="alex" print(g["name"]) del g["name"] # 7 __getattr__ class H(object): def __init__(self): pass def __getattr__(self, item): print("__getattr__被调用") def __setattr__(self, key, value): print("__setattr__被调用") def __delattr__(self, item): print("__delattr__被调用") h=H() h.name="alex" print(h.name) del h.name # 8 __eq__ class I(object): def __init__(self,name,age): self.name=name self.age=age def __eq__(self, other): if self.name==other.name and self.age==other.age: return True else: return False i1=I("alex",30) i2=I("alex",30) print(i1==i2) # __len__ class G(object): def __len__(self): return 100 g=G() print(len(g))
class Foo: def __del__(self): print('执行我啦') f1=Foo() del f1 print('------->') #输出结果 执行我啦 -------> ###################### class Foo: def __del__(self): print('执行我啦') f1=Foo() # del f1 print('------->') #输出结果 -------> 执行我啦
#_*_coding:utf-8_*_ format_dict={ 'nat':'{obj.name}-{obj.addr}-{obj.type}',#学校名-学校地址-学校类型 'tna':'{obj.type}:{obj.name}:{obj.addr}',#学校类型:学校名:学校地址 'tan':'{obj.type}/{obj.addr}/{obj.name}',#学校类型/学校地址/学校名 } class School: def __init__(self,name,addr,type): self.name=name self.addr=addr self.type=type def __repr__(self): return 'School(%s,%s)' %(self.name,self.addr) def __str__(self): return '(%s,%s)' %(self.name,self.addr) def __format__(self, format_spec): # if format_spec if not format_spec or format_spec not in format_dict: format_spec='nat' fmt=format_dict[format_spec] return fmt.format(obj=self) s1=School('oldboy1','北京','私立') print('from repr: ',repr(s1)) print('from str: ',str(s1)) print(s1) ''' str函数或者print函数--->obj.__str__() repr或者交互式解释器--->obj.__repr__() 如果__str__没有被定义,那么就会使用__repr__来代替输出 注意:这俩方法的返回值必须是字符串,否则抛出异常 ''' print(format(s1,'nat')) print(format(s1,'tna')) print(format(s1,'tan')) print(format(s1,'asfdasdffd')) ''' from repr: School(oldboy1,北京) from str: (oldboy1,北京) (oldboy1,北京) oldboy1-北京-私立 私立:oldboy1:北京 私立/北京/oldboy1 oldboy1-北京-私立 '''
""" __module__ 表示当前操作的对象在那个模块 __class__ 表示当前操作的对象的类是什么 """ # lib/aa.py class C: def __init__(self): self.name = ‘SB' ################ from lib.aa import C obj = C() print obj.__module__ # 输出 lib.aa,即:输出模块 print obj.__class__ # 输出 lib.aa.C,即:输出类
class Foo: def __init__(self,name): self.name=name def __getitem__(self, item): print(self.__dict__[item]) def __setitem__(self, key, value): self.__dict__[key]=value def __delitem__(self, key): print('del obj[key]时,我执行') self.__dict__.pop(key) def __delattr__(self, item): print('del obj.key时,我执行') self.__dict__.pop(item) f1=Foo('sb') f1['age']=18 f1['age1']=19 del f1.age1 del f1['age'] f1['name']='alex' print(f1.__dict__)
class Foo: x=1 def __init__(self,y): self.y=y def __getattr__(self, item): print('----> from getattr:你找的属性不存在') def __setattr__(self, key, value): print('----> from setattr') # self.key=value #这就无限递归了,你好好想想 # self.__dict__[key]=value #应该使用它 def __delattr__(self, item): print('----> from delattr') # del self.item #无限递归了 self.__dict__.pop(item) #__setattr__添加/修改属性会触发它的执行 f1=Foo(10) print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值 f1.z=3 print(f1.__dict__) #__delattr__删除属性的时候会触发 f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作 del f1.a print(f1.__dict__) #__getattr__只有在使用点调用属性且属性不存在的时候才会触发 f1.xxxxxx