封装: 如何组织类或模块,让封装的类或组件,尽量只负责一个领域的工作继承: 复用方式之一,概念形成统一。通过继承可以管理多个概念多态: 类、方法等的行为不同的做法。目标一致,实现的方式不同
继承
初识继承
继承指的是类与类之间的关系,是一种什么“是”什么的关系,继承的功能之一就是用来解决代码重用问题
继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可以成为基类或超类,新建的类称为派生类或子类
python中类的继承分为:单继承和多继承
class ParentClass1: #定义父类passclass ParentClass2: #定义父类passclass SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClasspassclass SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类pass
查看继承
>>> SubClass1.__bases__ #__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类(<class '__main__.ParentClass1'>,)>>> SubClass2.__bases__(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
经典类与新式类(关于新式类与经典类的区别,我们稍后讨论)
1.只有在python2中才分新式类和经典类,python3中统一都是新式类2.在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类3.在python2中,显式地声明继承object的类,以及该类的子类,都是新式类4.在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类
提示:如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如str)的实现。
>>> ParentClass1.__bases__(<class 'object'>,)>>> ParentClass2.__bases__(<class 'object'>,)
继承与抽象
先抽象再继承,抽象即抽取共性
抽象分成两个层次:
1.将奥巴马和梅西这俩对象比较像的部分抽取成类;
2.将人,猪,狗这三个类比较像的部分抽取成父类。
抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)
继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。
抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类

属性查找
对象属性查找顺序,按照类的继承顺序自下而上(先从本类中找,找不到再从父类中找)
# 属性查找小练习class Foo:def f1(self):print('from Foo.f1')def f2(self):print('from Foo.f2')self.f1() # b.f1()class Bar(Foo):def f1(self):print('from Bar.f1')b = Bar()# print(b.__dict__)b.f2()# from Foo.f2# from Bar.f1
派生
子类可以覆盖父类的属性和方法:一旦重新定义了自己的属性和方法且与父类重名,那么调用新增的属性时,就以自己为准了。
class Hero:def __init__(self, nickname, life_value, aggresivity):self.nickname = nicknameself.life_value = life_valueself.aggresivity = aggresivitydef attack(self, enemy):enemy.life_value -= self.aggresivityclass Garen(Hero):camp = 'Demacia'def attack(self, enemy):print('from Garen Class')class Riven(Hero):camp = 'Noxus'g = Garen('草丛伦', 100, 30)r = Riven('锐雯雯', 80, 50)# print(g.camp)# g.attack(r)# print(r.life_value)g.attack(r)
继承的实现原理
python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如
新式类

# Python3class A:def f2(self):print("f2 from A")class B(A):def f1(self):print("from B")class C(A):def f2(self):print("f2 from C")class D(B, C):passprint(D.mro()) # D->B->C->Ad = D()d.f2() # f2 from C
经典类

# Python2class A:def f2(self):print("f2 from A")class B(A):def f1(self):print("from B")class C(A):def f2(self):print("f2 from C")class D(B, C):pass# D->B->A->Cd = D()d.f2() # f2 from A

# Python2# coding:utf-8class A():def test(self):print('from A')class B(A):passclass C(A):def test(self):print('from C')class D(B):passclass E(C):def test(self):print('from E')class F(D, E):# def test(self):# print('from F')passf1 = F()f1.test() # from A# 新式类继承顺序:F->D->B->E->C->A# 经典类继承顺序:F->D->B->A->E->C# python3中统一都是新式类# pyhon2中才分新式类与经典类
总结
定义的基类A最后访问,(除了A,从左到右,按列)
不知道你是否发现,如果用正经的C3算法规则去分析一个类继承关系有点繁琐,尤其是遇到一个复杂的类也要分析很久。
所以,我自己根据经验总结了一句话赠送给大家:从左到右,深度优先,大小钻石,留住顶端,基于这句话可以更快的找到继承关系。
class M:passclass N:passclass E(M):passclass G:passclass K:passclass H(K):passclass D(G, H):passclass F(M, N):passclass P:passclass C(E, F):passclass B(D, E):passclass A(B, C, P):passprint(A.mro()) # 简写为:A -> B -> D -> G -> H -> K -> C -> E -> F -> M -> N -> P -> object
调用父类代码
# 实例化对象class Hero:def __init__(self, nickname, life_value, aggresivity):self.nickname = nicknameself.life_value = life_valueself.aggresivity = aggresivitydef attack(self, enemy):enemy.life_value -= self.aggresivityclass Garen(Hero):camp = 'Demacia'def __init__(self, nickname, life_value, aggresivity, weapon):# 字段可不重写,调用父类__init__# self.nickname=nickname# self.life_value=life_value# self.aggresivity=aggresivity# 方式1.Hero.__init__(self, nickname, life_value, aggresivity)# 方式2.super(Garen, self).__init__(nickname, life_value, aggresivity)# 方式3.Python3简写(推荐):super().__init__(nickname, life_value, aggresivity)self.weapon = weapondef attack(self, enemy):super().attack(self, enemy) # 或者 指名道姓 Hero.attack(self, enemy)print('from Garen Class')g = Garen('草丛伦', 100, 30, '大宝剑')print(g.__dict__)# {'nickname': '草丛伦', 'life_value': 100, 'aggresivity': 30, 'weapon': '大宝剑'}
# 对象交互class Hero:def __init__(self, nickname, life_value, aggresivity):self.nickname = nicknameself.life_value = life_valueself.aggresivity = aggresivitydef attack(self, enemy):enemy.life_value -= self.aggresivityclass Garen(Hero):camp = 'Demacia'def attack(self, enemy):super().attack(enemy) # 依赖继承print('from Garen Class')class Riven(Hero):camp = 'Noxus'g = Garen('草丛伦', 100, 30)r = Riven('锐雯雯', 80, 50)g.attack(r)print(r.life_value) # 50
组合
软件重用的重要方式除了继承之外还有另外一种方式,即:组合
组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合
- 继承和另一个类的关系:is
- 组合和另一个类的关系:has ```python
class People: school=’mufeng’
def __init__(self,name,age,sex):self.name=nameself.age=ageself.sex=sex
class Teacher(People): def init(self,name,age,sex,level,salary,): super().init(name,age,sex)
self.level=levelself.salary=salarydef teach(self):print('%s is teaching' %self.name)
class Student(People): def init(self, name, age, sex, classtime,): super()._init(name,age,sex)
self.class_time=class_timedef learn(self):print('%s is learning' % self.name)
class Course: def init(self,course_name,course_price,course_period): self.course_name = course_name self.course_price = course_price self.course_period = course_period
def tell_info(self):print('课程名<%s> 课程价钱<%s> 课程周期<%s>' %(self.course_name,self.course_price,self.course_period))
teacher1=Teacher(‘ecithy’,18,’male’,10,3000,) teacher2=Teacher(‘mufeng’,28,’male’,30,3000,) python=Course(‘python’,3000,’3mons’) linux=Course(‘linux’,2000,’4mons’) teacher1.course=python teacher2.course=python
<a name="blogTitle19"></a>## 抽象类**作用**- 如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。- 接口提取了一群类共同的函数,可以把接口当做一个函数的集合,然后让子类去实现接口中的函数。这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。**语法特点**:- 继承抽象类的类,必须实现抽象类所有的函数,当然可以增加其它函数- 抽象类只能被继承不能被实例化```pythonimport abcclass Animal(metaclass=abc.ABCMeta): #只能被继承,不能被实例化all_type='animal'@abc.abstractmethoddef run(self):pass@abc.abstractmethoddef eat(self):pass# animal=Animal()class People(Animal):def run(self):print('people is running')def eat(self):print('people is eating')class Pig(Animal):def run(self):print('people is walking')def eat(self):print('people is eating')class Dog(Animal):def run(self):print('people is walking')def eat(self):print('people is eating')pig1=Pig()# dog1=Dog()# peo1.eat()# pig1.eat()# dog1.eat()## print(peo1.all_type)
多态
多态指的是一类事物有多种形态,比如
多态性:指的是可以在不考虑对象的类型的情况下而直接使用对象
作用:
1.增加了程序的灵活性
以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal)
2.增加了程序额可扩展性
通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用
# 多态:同一类事物的多种形态import abcclass Animal(metaclass=abc.ABCMeta): #同一类事物:动物@abc.abstractmethoddef talk(self):passclass 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')class Cat(Animal):def talk(self):print('say miamiao')# 多态性:指的是可以在不考虑对象的类型的情况下而直接使用对象peo1=People()dog1=Dog()pig1=Pig()cat1=Cat()# -------------------def func(instance, absCls=Animal):'依赖基类: 基类指向子类对象'absCls = instancereturn absClsfunc(peo1).talk()func(dog1).talk()func(pig1).talk()func(cat1).talk()# -------------------def func(animal):'不依赖基类'return animalfunc(peo1).talk()func(pig1).talk()func(dog1).talk()func(cat1).talk()
duck-typing

作用
除了多态的好处之外:不依赖基类,可以操作有不同基类的类,比多态更灵活
class Disk:def read(self):print('disk read')def write(self):print('disk write')class Text:def read(self):print('text read')def write(self):print('text write')def myOpen(cls):'不依赖基类'return cls()f = myOpen(Disk)f.read()f.write()f = myOpen(Text)f.read()f.write()
Python内置类型用到了很多鸭子类型,如序列类型
# 序列类型:列表list,元祖tuple,字符串strl=list([1,2,3])t=tuple(('a','b'))s=str('hello')# print(l.__len__())# print(t.__len__())# print(s.__len__())# def len(obj):# return obj.__len__()print(len(l))print(len(t))print(len(s))
封装
私有属性
语法:__X
作用:定义的私有属性和方法只能在本类和本类对象中使用,继承类和外部无法使用
#其实这仅仅这是一种变形操作#类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式:# 作用class A:__N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__Ndef __init__(self):self.__X=10 #变形为self._A__Xdef __foo(self): #变形为_A__fooprint('from A')def bar(self):self.__foo() #只有在类内部才可以通过__foo的形式访问到.#A._A__N是可以访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形
这种变形的特点:
1、在类外部无法直接obj.AttrName
2、在类内部是可以直接使用:obj.AttrName
3、子类无法覆盖父类__开头的属性
这种变形需要注意的问题是:
1、这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:类名_属性,然后就可以访问了,如a._A__N
2、变形的过程只在类的定义是发生一次,在定义后的赋值操作,不会变形
3、在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
class A:def __foo(self): #_A__fooprint('A.foo')def bar(self):print('A.bar')self.__foo() #self._A__foo()class B(A):def __foo(self): #_B__fooprint('B.foo')b=B()b.bar()
封装的意义
一:封装数据
明确的区分内外,控制外部对隐藏的属性的操作行为
class People:def __init__(self,name,age):self.__name=nameself.__age=agedef tell_info(self):print('Name:<%s> Age:<%s>' %(self.__name,self.__age))def set_info(self,name,age):if not isinstance(name,str):print('名字必须是字符串类型')returnif not isinstance(age,int):print('年龄必须是数字类型')returnself.__name=nameself.__age=agep=People('ecithy',18)p.tell_info()p.set_info('ecithy',38)p.tell_info()p.set_info(123,38)p.set_info('ecithy','38')p.tell_info()
二:封装方法
目的是隔离复杂度
取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱#对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做#隔离了复杂度,同时也提升了安全性class ATM:def __card(self):print('插卡')def __auth(self):print('用户认证')def __input(self):print('输入取款金额')def __print_bill(self):print('打印账单')def __take_money(self):print('取款')def withdraw(self):self.__card()self.__auth()self.__input()self.__print_bill()self.__take_money()a=ATM()a.withdraw()
property
将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则
class People:def __init__(self,name):self.__name=name@propertydef name(self):# print('getter')return self.__name@name.setterdef name(self,val):# print('setter',val)if not isinstance(val,str):print('名字必须是字符串类型')returnself.__name=val@name.deleterdef name(self):print('deleter')print('不允许删除')p=People('mufeng')# print(p.get_name())# print(p.name)# p.namep.name='mufeng'p.name=123# print(p.name)# del p.name
类中两大方法
在类内部定义的函数,分为两大类
一:绑定方法:绑定给谁,就应该由谁来调用,谁来调用就回把调用者当作第一个参数自动传入
绑定到对象的方法:在类内定义的没有被任何装饰器修饰的
绑定到类的方法:在类内定义的被装饰器classmethod修饰的方法
二:非绑定方法:没有自动传值这么一说了,就类中定义的一个普通工具,对象和类都可以使用
非绑定方法:不与类或者对象绑定
# settings.pyname='mufeng'age=18sex='female'
import settingsimport hashlibimport timeclass People:def __init__(self,name,age,sex):self.id=self.create_id()self.name=nameself.age=ageself.sex=sexdef tell_info(self): #绑定到对象的方法print('Name:%s Age:%s Sex:%s' %(self.name,self.age,self.sex))@classmethoddef from_conf(cls):obj=cls(settings.name,settings.age,settings.sex)return obj@staticmethoddef create_id():m=hashlib.md5(str(time.time()).encode('utf-8'))return m.hexdigest()# p=People('mufeng',18,'male')# 绑定给对象,就应该由对象来调用,自动将对象本身当作第一个参数传入# p.tell_info() #tell_info(p)# 绑定给类,就应该由类来调用,自动将类本身当作第一个参数传入# p=People.from_conf() #from_conf(People)# p.tell_info()#非绑定方法,不与类或者对象绑定,谁都可以调用,没有自动传值一说p1=People('mufeng1',18,'male')p2=People('mufeng2',28,'male')p3=People('mufeng3',38,'male')print(p1.id)print(p2.id)print(p3.id)
反射
通过字符串的形式操作对象相关的属性
四个可以实现自省的函数 下列方法适用于类和对象(一切皆对象,类本身也是一个对象)
hasattr(object,name)
判断object中有没有一个name字符串对应的方法或属性
getattr(object, name, default=None)
def getattr(object, name, default=None): # known special case of getattr"""getattr(object, name[, default]) -> valueGet 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'texist; without it, an exception is raised in that case."""pass
setattr(x, y, v)
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
delattr(x, y)
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
# 反射:通过字符串映射到对象的属性class People:country='China'def __init__(self,name,age):self.name=nameself.age=agedef talk(self):print('%s is talking' %self.name)obj=People('mufeng',18)# 检测是否含有某属性print(hasattr(obj,'name')) # obj.name #obj.__dict__['name']print(hasattr(obj,'talk')) # obj.talk# print(getattr(obj,'namexxx',None))# print(getattr(obj,'talk',None))# 设置属性# setattr(obj,'sex','male') #obj.sex='male'# print(obj.sex)# 删除属性# delattr(obj,'age') #del obj.age# print(obj.__dict__)
# 反射的应用:class Service:def run(self):while True:inp=input('>>: ').strip() #cmd='get a.txt'cmds=inp.split() #cmds=['get','a.txt']# print(cmds)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()

