面向对象基础
初始面向对象
类的相关知识
对象的相关知识
对象之间的交互
类名中空间与对象,实例的命名空间
类的组合用法
初始面向对象小结
面向对象的三大特性
继承
多态
封装
面向对象的更多说明
面向对象的软件开发
几个概念的说明
面向对象常用术语
一,初始类和对象
python中一切皆对象,类型的本质就是类.
dict # 类型dict就是类dict print(dict) # <class 'dict'> d = dict(name='chen') # 实例化 a = d.pop('name') # 向d发一条消息 print(a)
从上面的例子来看,字典就是一类数据结构,我一说字典你就知道是个用{}表示,里面有k-v键值对的东西,他还具有一些增删改查的方法,但是我一说字典你能知道字典里具体存在了那些内容?不能,所以我们说对于一类来说,它具有相同的特征属性和方法.
而具体的{'name': 'aval'}这个字典,他是一个字典,可以使用字典的所有方法,并且里面有了具体的值,他就是字典的一个对象.对象激素hi已经实实在在存在某一个具体的个体.
在举一个列子,比如在一个动物园,你想描述这个动物园,那么动物园里的每一个动物就是一个类,老虎,天鹅,鳄鱼,他们都是相同的属性,比如身高,体重,品种,还有各种动作,比如,鳄鱼会游泳,天鹅会飞.
但是这些老虎啥的都不是具体的某一只,而是一类动物,虽然他们都有身高体重,但是你却没有办法确定这个值是多少,如果这个时候给你一只具体的老虎,那么你就能给他量身高体重,这些数值是不是变成了据个体的了,那么具体的之一只老虎就是一个具体的实例,也是一个对象,不止一只,其实每一只具体的老虎都有自己的身高体重,那么每一只老虎都是老虎类的一个对象
在python中,用变量表示特征,用函数表示技能,因而具有相同特征和技能的一类事物就是'类',对象是这一类事物中具体的一个
类的相关知识
初始类
声明
''' class 类名: '类的文档字符串' 类体 ''' # 创建一个类 class Date: pass class Person: role = 'person' def walk(self): print('person is walking...')
类有两种作用:属性引用和实例化
属性引用(类型:属性)
class Person: # 定义一个人类 role = 'person' # 人的角色都是属性 def walk(self): # 人都可以走路,也就是有一个走路的方法 print('person is walking') print(Person.role) # 查看人类的role的属性 print(Person.walk) # 引用人的走路的方法,注意这里不是在调用
实例化: 类名加括号就是实例化,会自动触发__init__函数的运行,可以用它来为每个实例定制自己的特征
class Person: role = 'person' def __init__(self, name): self.name = name def walk(self): # 人都可以走路,也就是一个走路的方法 print('person is walking') print(Person.role) # 查看人的role属性 print(Person.walk) # 引用人的走路方式,注意:这里不是在调用
实例化的过程就是类--->对象的过程
原本我们只有一个Person类,这个过程中,产生了一个egg对象,有自己具体的名字,攻击力和生命值
语法: 对象名 = 类名(参数)
egg = Person('egg') # 类名()就等于执行Person.__init__() # 执行完__init__()就会有一个对象,这个对象类似一个字典,存这属于这个人本身的一些属性和方法
查看属性&调用方法
print(egg.name) # 查看属性直接 对象.属性 print(egg.walk()) # 调用方法 对象名.方法名()
关于self
self: 在实例化是自动将对象/实例本身传给__init__的一个一个参数,你也可以给他起个别的名字,但是正常人都不会这个么做.
因为瞎改别人就不认识了
类属性的补充
类属性的补充
对象的相关知识
人狗大战创建一个人的类
class Person: # 定义一个人类 role = 'person' # 人的角色属性都是人 def __init__(self, name, aggre, life_value): self.name = name # 每一个人都有自己的昵称 self.agger = aggre # 每一个角色都有自己的攻击力 self.life_value = life_value # 每一个角色都有自己的生命值 def attack(self, dog): # 人可以攻击狗,这里的狗也是一个对象. # 人攻击狗,那么狗的生命值会根据人的攻击力而下降 dog.life_value -= self.agger
对象是关于类而实际存在的一个例子,即实例
对象/实例只有一种作用:属性引用
egg = Person('盲僧', 10, 100) print(egg.name) print(egg.agger) print(egg.life_value)
当然了,你也可以引用一个方法,因为方法也是一个属性,只不过是一个类似函数的属性,我们也管他叫动态属性.引用动态属性并不是执行这个方法,要想调用方法和调用函数是一样的,都需要在后面加上括号
print(agg.attack)
我知道再类里说,你可能还有好多地方不能理解,那我们就用函数来解释一下这个类,对象到底是个啥,
class 类名: def __init__(self, 参数1,参数2): self.对象的属性1 = 参数1 self.对象的属性2 = 参数2 def 方法名(self):pass def 方法名(self):pass 对象名 = 类名(1,2) # 对象就是实例,代表一个具体的东西 # 类名(): 类名+括号就是实例化一个类,相当于调用了__init__方法 # 括号里传参,参数不需要self,其他与init中的形参一一对应 # 结束返回一个对象 对象名.对象的属性1 # 查看对象的属性,直接用 对象名.属性名 即可 对象名.方法名() # 调用类中的方法,直接用 对象名.方法名() 即可
类命名空间与对象,实例的命名空间
创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性
而类有两种属性: 静态属性和动态属性
静态属性就是直接在勒种定义的变量
动态属性就是定义在类中的方法
其中类的数据属性是共享给所有对象的
>>>id(egg.role) 4341594072 >>>id(Person.role) 4341594072
而类的动态属性是绑定到所有的对象的
>>>egg.attack <bound method Person.attack of <__main__.Person object at 0x101285860>> >>>Person.attack <function Person.attack at 0x10127abf8>
创建一个对象/实例就会创建一个对象/实例的名称空间,存放对象/实例的名字,成为对象/实例的属性
在obj.name会先从obh自己的名称空间里找name,找不到则去类中找,类也找不到就去找父类...最后找不到就会抛出异常
面向对象的组合用法
软件重用的重要方式除了继承之外还有另外一种方式,即:组合.组合值得是,在一个类中以另外一个类的对象作为数据属性,成为类的组合
class Game_role: def __init__(self, name, ad, hp): self.name = name self.ad = ad self.hp = hp def attack(self, obj): obj.hp -= self.ad print('%s受到%s的%s点伤害,剩下%s血' % (obj.name, self.name, self.ad, obj.hp)) def weapon_attack(self, w): self.weapon = w class Weapon: def __init__(self, name, weapon_ad): self.name = name self.weapon_ad = weapon_ad def fight(self, p1, p2): p2.hp -= p1.ad print('%s拿着%s砍了%s%s滴血,%s还剩下%s血' % (p1.name, self.name, p2.name, self.weapon_ad, p2.name,p2.hp)) gl = Game_role('盖伦', 10, 1000) # 实例化一个盖伦对象 jj = Game_role('剑姬', 20, 900) # 实例化一个剑姬对象 arms = Weapon('多兰剑', 10) # 实例化一个武器 gl.weapon_attack(arms) # 将实例化的武器对象,传递给gl实例对象中的weapon_attack函数,(self.weapon = w)函数会把武器这个实例增加到gl的属性中,下一步gl就可以通过weapon调用fight函数 gl.weapon.fight(gl, jj)
class Weapon: def prick(self, obj): # 这是带装备的主动技能 obj.life_value -= 800 # 假设攻击力是800 print(obj.life_value) class Person: # 定义一个人类 role = 'people' def __init__(self, name, life_value): self.life_value = life_value self.name = name self.weapon = Weapon # 给角色绑定一个武器 egg = Person('egon', 1000) dog = Person('二哈', 800) egg.weapon.prick(1, dog) # egg组合了一个武器的对象,可以直接egg.weapon来使用组合类中的所有方法 print(egg.__dict__)
圆环是由两个圆组成的,圆环的面积是外面圆的面积减去内部圆的面积.圆环的周长是内部圆的周长加上外部圆的周长.
这个时候,我们就实现了一个圆型类,计算一个圆的周长和面积,然后再环类型中组合圆形的实例作为自己的属性来用
from math import pi class Circle: ''' 定义一个圆形类, 提供计算面积(area)和周长(perimeter)的方法 ''' def __init__(self, radius): self.radius = radius def area(self): return pi * self.radius * self.radius def perimeter(self): return 2 * pi * self.radius circle = Circle(10) # 实例化一个圆 area1 = circle.area() # 计算圆面积 per1 = circle.perimeter() # 计算圆周长 print(area1, per1) class Ring: ''' 定义一个圆环类, 提供圆环的面积和周长的方法 ''' def __init__(self, radius_outside, radius_inside): self.outsid_circle = Circle(radius_outside) self.inside_circle = Circle(radius_inside) def area(self): return self.outsid_circle.area() - self.inside_circle.area() def perimeter(self): return self.outsid_circle.perimeter() + self.inside_circle.perimeter() ring = Ring(10, 5) print(ring.perimeter()) print(ring.area())
当类之间由显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好用
初始面向对象小结
人狗大战
class Person: role = 'person' def __init__(self, name, aggressivity, life_value, money): self.name = name self.aggressivity = aggressivity self.life_value = life_value self.money = money def attack(self, enemy): enemy.life_value -= self.aggressivity class Dog: role = 'dog' def __init__(self, name, breed, agg, life_value): self.name = name self.breed = breed self.agg = agg self.life_value = life_value def bite(self, enemy): enemy.life_value -= self.agg class Weapon: def __init__(self, name, price, aggrev, life_value): self.name = name 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 prick(self, obj): obj.life_value -= 500 lance = Weapon('长矛', 200, 6, 100) egg = Person('egon', 10, 1000, 600) ha2 = Dog('二哈', '哈士奇', 10, 1000) lance.update(egg) print(egg.__dict__) egg.weapon = lance # 将武器这个实例化对象添加到egg对象的属性中 print(egg.__dict__) print(egg.weapon) print(egg.money, egg.life_value, egg.aggressivity) print(ha2.life_value) egg.attack(ha2) print(ha2.life_value) egg.weapon.prick(ha2) print(ha2.life_value)
面向对象的三大特性
继承
什么是继承
继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可以成为基类或者超类,新建的类称为派生类或子类
python中类的继承分为:单继承和多继承
class ParentClass1: # 定义父类 pass class ParentClass2: # 定义父类 pass class SubClass1(ParentClass1): # 单继承,基类是ParentClass1,派生类是SubClass pass class SubClass2(ParentClass1, ParentClass2): # python支持多继承,用逗号隔开多个继承的类 pass
查看继承
print(SubClass1.__bases__) # __base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类 print(SubClass2.__bases__) # (<class '__main__.ParentClass1'>,) # (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
提示 :如果没有指定的基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现.
print(ParentClass1.__bases__) print(ParentClass2.__bases__) # (<class 'object'>,) # (<class 'object'>,)
继承与抽象(先抽象再继承)
抽象即抽取类似或者说比较像的部分.
抽象分成两个层次:
1,将奥巴马和梅西这两个对象比较的部分抽取成类
2,将人,猪,狗这三个类较像的部分抽取成类
抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)

继承与重用
在开发程序的过程中,如果我们定义了一个类A,然后又想新建另一个类B,但是类B的大部分内容与类A的相同时
我们不可能从头开始写一个类B,这就用到了类的继承的概念
通过继承的方式新建类B,让B继承A,B会遗传A所有属性(数据属性和函数属性),实现代码重用
class Animal: ''' 人和狗都是动物,所以创造哟个Animal基类 ''' def __init__(self, name, aggre, life_value): self.name = name self.aggre = aggre self.life_value = life_value def eat(self): print('%s is eating' %self.name) class Dog(Animal): pass class Person(Animal): pass egg = Person('egon', 10, 1000) ha2 = Dog('二哈', 50, 1000) egg.eat() ha2.eat()
提示:用已经有的类建立一个新的类,这样就重用了已经有的软件中的一部分设置大部分,大大省了变成的工作量,这就是常说的软件重用,不仅可以重用自己的类,也可以继承别人的,不如标准库,来定制新的数据类型,这样就是大大缩短了软件的开发周期,对大型软件开发来说, 意义重大
派生
当然子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的时,一但重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为标准了
class Animal: ''' 人和狗都是动物,所以创造一个Animal基类 ''' def __init__(self, name, aggre, life_value): self.name = name self.aggre = aggre self.life_value = life_value def eat(self): print('%s is eating' % self.name) class Dog(Animal): ''' 狗类,继承Animal类 ''' def bite(self, people): ''' 派生:狗的技能是咬人 :param people: :return: ''' people.life_value -= self.aggre class Person(Animal): ''' 人类,继承Animal ''' def attack(self, dog): ''' 派生:人有攻击的技能 :param dog: :return: ''' dog.life_value -= self.aggre egg = Person('egon', 10, 1000) ha2 = Dog('二哈', 20, 1000) print(ha2.life_value) print(egg.attack(ha2)) print(ha2.life_value)
注意: 像ha2.life_value之类的属性引用,会先从实例中找life_value然后去类中找,然后再去父类中找...知道最顶级的父类
在子类中,新建的重名的函数属性,在编辑函数内功能的时候,有可能需要重用父类中重名的那个函数功能,应该是用调用普通函数的方式,即:类名.func(),此时就与调用普通函数无异了,因此即便是self单数也要为其传值
python3中,子类执行父类的方法也可以直接用super方法
class A: def hahaha(self): print('A') class B(A): def hahaha(self): super().hahaha() #super(B,self).hahaha() #A.hahaha(self) print('B') a = A() b = B() b.hahaha() super(B,b).hahaha() 帮你了解super
class Animal: ''' 人和狗都是动物,所以创造一个Animal基类 ''' def __init__(self, name, aggressivity, life_value): self.name = name # 人和狗都有自己的昵称; self.aggressivity = aggressivity # 人和狗都有自己的攻击力; self.life_value = life_value # 人和狗都有自己的生命值; def eat(self): print('%s is eating'%self.name) class Dog(Animal): ''' 狗类,继承Animal类 ''' def __init__(self,name,breed,aggressivity,life_value): super().__init__(name, aggressivity, life_value) #执行父类Animal的init方法 self.breed = breed #派生出了新的属性 def bite(self, people): ''' 派生出了新的技能:狗有咬人的技能 :param people: ''' people.life_value -= self.aggressivity def eat(self): # Animal.eat(self) #super().eat() print('from Dog') class Person(Animal): ''' 人类,继承Animal ''' def __init__(self,name,aggressivity, life_value,money): #Animal.__init__(self, name, aggressivity, life_value) #super(Person, self).__init__(name, aggressivity, life_value) super().__init__(name,aggressivity, life_value) #执行父类的init方法 self.money = money #派生出了新的属性 def attack(self, dog): ''' 派生出了新的技能:人有攻击的技能 :param dog: ''' dog.life_value -= self.aggressivity def eat(self): #super().eat() Animal.eat(self) print('from Person') egg = Person('egon',10,1000,600) ha2 = Dog('二愣子','哈士奇',10,1000) print(egg.name) print(ha2.name) egg.eat()
通过继承建立了派生类与基类之间的关系,他是一种'是'的关系,比如白马是马,人是动物
当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好
抽象类与接口类
接口类
继承有两种用途:
一:继承基类的方法,并且做出自己的改变或者扩展(代码重用)
二:声明某个子类兼容于某基类,定义一个接口类interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能
class Alipay: ''' 支付宝支付 ''' def pay(self, money): print('支付宝支付%s元' %money) class Applepae: ''' apple pay 支付 ''' def pay(self, money): print('apple pay 支付了%s元' %money) def pay(payment, money): ''' 支付函数,总体负责支付 对应支付的对象和要支付的金额 :param payment: :param money: :return: ''' payment.pay(money) p = Alipay() pay(p, 200)
开发中常遇到的问题
class Alipay: ''' 支付宝支付 ''' def pay(self,money): print('支付宝支付了%s元'%money) class Applepay: ''' apple pay支付 ''' def pay(self,money): print('apple pay支付了%s元'%money) class Wechatpay: def fuqian(self,money): ''' 实现了pay的功能,但是名字不一样 ''' print('微信支付了%s元'%money) def pay(payment,money): ''' 支付函数,总体负责支付 对应支付的对象和要支付的金额 ''' payment.pay(money) p = Wechatpay() pay(p,200) #执行会报错
接口初成:手动报异常:NotlmplementedError来解决开发中遇到的问题
class Payment: def pay(self): raise NotImplementedError class Wechatpay(Payment): def fuqian(self,money): print('微信支付了%s元'%money) p = Wechatpay() #这里不报错 pay(p,200) #这里报错了
借用abc模块来实现接口
from abc import ABCMeta,abstractmethod class Payment(metaclass=ABCMeta): @abstractmethod def pay(self,money): pass class Wechatpay(Payment): def fuqian(self,money): print('微信支付了%s元'%money) p = Wechatpay() #不调就报错了
实践中,继承的第一种含义意义并不很大,甚至常常是有害的。因为它使得子类与基类出现强耦合。
继承的第二种含义非常重要。它又叫“接口继承”。
接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象”——这在程序设计上,叫做归一化。
归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合——就好象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也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化
为什么要有抽象类
如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。
比如我们有香蕉的类,有苹果的类,有桃子的类,从这些类抽取相同的内容就是水果这个抽象的类,你吃水果时,要么是吃一个具体的香蕉,要么是吃一个具体的桃子。。。。。。你永远无法吃到一个叫做水果的东西。
从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。
从实现角度来看,抽象类与普通类的不同之处在于:抽象类中有抽象方法,该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的,即将揭晓答案
#一切皆文件 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中,并没有接口类这种东西,即便不通过专门的模块定义接口,我们也应该有一些基本的概念。
多继承问题
在继承抽象类的过程中,我们应该尽量避免多继承
而在继承接口的时候,我们反而福利你来多继承接口
接口隔离原则:
使用多个专门的接口,而不是使用单一的总接口,即客户端不应该依赖那些不需要的接口.
方法的实现
在抽象类中,我们可以对一些抽象方法做出基础实现,
而在接口类中,任何方法都是一种规范,具体的功能需要子类实现
钻石继承
继承顺序
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会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止
而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
继承小结
继承的作用
1,减少代码的重用
2,提高代码的可读性
3,规范编程模式
几个名词
抽象: 抽象即抽取类似或者说比较像的部分.是一个从具体到抽象的过程
继承,子类继承了父类的方法和书信给
派生:子类的父类方法和属性的基础上产生了新的方法和属性
抽象类与接口类
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 wang wang') class Pig(Animal): # 动物的形态之一:猪 def talk(self): print('say aoao')
多态性
什么是多态动态绑定(在继承的背景下使用时,有时也称为多态性)
多态性是指在不考虑实例类型的情况下使用实例
在面向对象方法中一般是这样表述多态性:
像不同的对象发送同一条消息(!!!obj.func(): 是调用了obj的方法func,又称为向obj发送了一条消息func),不同的对象在接受时会产生不同的行为(即方法)
也就是说,每个对象可以用自己的方式相应共同的消息.所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数
比如:老师.下课铃响了(),学生.下课铃响了(),老师执行的时下班操作,学生执行的时放学操作,虽然二者的消息一样,但是执行的效果不同
鸭子类型:
python崇尚鸭子类型,即'如果看起来像,叫声像而且走起路来像鸭子,那么他就是鸭子'
python程序员通常根据这种行为来编写程序,例如:如果想编写现有对象的自定义版本,可以继承对象
也可以创建一个外观和相位像,但与它无任何关系的全新对象,后者通常用于保存 程序组件的松耦合度.
例子:python中的str,list,tuple中的index方法
封装
封装:
隐藏对象的属性和实现细节,仅对外提供公共访问方式
好处
1,将变化隔离
2,便于使用
3,提高复用性
4,提高安全性
封装原则:
1,将不需要对外提供的内容都隐藏起来
2,把属性都隐藏起来,提供公共方法对其访问
私有变量和私有方法
在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)
私有变量
#其实这仅仅这是一种变形操作 #类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式: 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的形式访问到. #A._A__N是可以访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形
这种自动变形的特点:
1,类中定义的__X只能在内部使用,如self.__X,引用的就是变形的结果.
2,这种变形其实正是针对外部的变形,在外部无法通过__X这个名字访问到
3,在子类定义的__X不会覆盖在父类定义的__X,因为子类中变形成了:_子类名__X,而父类中变形成了:_父类名__X,即双下划线开头的属性在继承给了子类时,子类时无法覆盖的.
这种变形需要注意的问题是:
1,这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后可以访问了
2,变形的过程只在类的内部生效,在定义后的赋值操作吗,不会变形
私有方法
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
封装在于明确区分内外没事的类实现者可以调用修改封装内的东西卜星相外部调用者的代码,而外部使用者只知道一个接口函数,只要接口名,参数不变,使用者的代码永远无需改变.这就提供了一个良好的合作基础--或者说,只要接口这个基础约定不变,则代码改变不足为虑
#类的设计者 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('卧室','egon',20,20,20) >>> r1.tell_area() #使用者调用接口tell_area #类的设计者,轻松的扩展了功能,而类的使用者完全不需要改变自己的代码 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()
property属性
什么事特性property
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 ''' 例二:圆的周长和面积
注意:此时的特性area和perimeter不能被赋值
c.area=3 #为特性area赋值 ''' 抛出异常: AttributeError: can't set attribute '''
为什么要用property
讲一个类的而函数定义成特性以后,对象再去使用的时候,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('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() obj.price # 获取商品价格 obj.price = 200 # 修改商品原价 print(obj.price) del obj.price # 删除商品原价
classmethod
class Classmethod_Demo(): role = 'dog' @classmethod def func(cls): print(cls.role) Classmethod_Demo.func()
staticmethod
class Staticmethod_Demo(): role = 'dog' @staticmethod def func(): print("当普通方法用") Staticmethod_Demo.func()
反射---->>
python面向对象中的反射:通过字符串形式操作对象相关属性.python中的一切事物都是对象(都可以使用反射)
四个可以实现自省的函数
下列方法适用于类和对象(一切皆对象,类本身也是一个对象)
hasattr
def hasattr(*args, **kwargs): # real signature unknown """ Return whether the object has an attribute with the given name. This is done by calling getattr(obj, name) and catching AttributeError. """ pass hasattr
getattr
def getattr(object, name, default=None): # known special case of getattr """ getattr(object, name[, default]) -> value Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y. When a default argument is given, it is returned when the attribute doesn't exist; without it, an exception is raised in that case. """ pass getattr
setattr
def setattr(x, y, v): # real signature unknown; restored from __doc__ """ Sets the named attribute on the given object to the specified value. setattr(x, 'y', v) is equivalent to ``x.y = v'' """ pass setattr
delattr
def delattr(x, y): # real signature unknown; restored from __doc__ """ Deletes the named attribute from the given object. delattr(x, 'y') is equivalent to ``del x.y'' """ pass delattr
class Foo: f = '类的静态变量' def __init__(self,name,age): self.name=name self.age=age def say_hi(self): print('hi,%s'%self.name) obj=Foo('egon',73) #检测是否含有某属性 print(hasattr(obj,'name')) print(hasattr(obj,'say_hi')) #获取属性 n=getattr(obj,'name') print(n) func=getattr(obj,'say_hi') func() print(getattr(obj,'aaaaaaaa','不存在啊')) #报错 #设置属性 setattr(obj,'sb',True) setattr(obj,'show_name',lambda self:self.name+'sb') print(obj.__dict__) print(obj.show_name(obj)) #删除属性 delattr(obj,'age') delattr(obj,'show_name') delattr(obj,'show_name111')#不存在,则报错 print(obj.__dict__) 四个方法的使用演示
class Foo(object): staticField = "old boy" def __init__(self): self.name = 'wupeiqi' def func(self): return 'func' @staticmethod def bar(): return 'bar' print getattr(Foo, 'staticField') print getattr(Foo, 'func') print getattr(Foo, 'bar') 类也是对象
#!/usr/bin/env python # -*- coding:utf-8 -*- import sys def s1(): print 's1' def s2(): print 's2' this_module = sys.modules[__name__] hasattr(this_module, 's1') getattr(this_module, 's2') 反射当前模块成员
def test(): print('from the test')
""" 程序目录: module_test.py index.py 当前文件: index.py """ import module_test as obj #obj.test() print(hasattr(obj,'test')) getattr(obj,'test')()

浙公网安备 33010602011771号