封装: 如何组织类或模块,让封装的类或组件,尽量只负责一个领域的工作
继承: 复用方式之一,概念形成统一。通过继承可以管理多个概念
多态: 类、方法等的行为不同的做法。目标一致,实现的方式不同
继承
初识继承
继承指的是类与类之间的关系,是一种什么“是”什么的关系,继承的功能之一就是用来解决代码重用问题
继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可以成为基类或超类,新建的类称为派生类或子类
python中类的继承分为:单继承和多继承
class ParentClass1: #定义父类
pass
class ParentClass2: #定义父类
pass
class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass
pass
class 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 = nickname
self.life_value = life_value
self.aggresivity = aggresivity
def attack(self, enemy):
enemy.life_value -= self.aggresivity
class 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列表就是一个简单的所有基类的线性顺序列表,例如
新式类
# Python3
class 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
print(D.mro()) # D->B->C->A
d = D()
d.f2() # f2 from C
经典类
# Python2
class 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->C
d = D()
d.f2() # f2 from A
# Python2
# coding:utf-8
class A():
def test(self):
print('from A')
class B(A):
pass
class C(A):
def test(self):
print('from C')
class D(B):
pass
class E(C):
def test(self):
print('from E')
class F(D, E):
# def test(self):
# print('from F')
pass
f1 = F()
f1.test() # from A
# 新式类继承顺序:F->D->B->E->C->A
# 经典类继承顺序:F->D->B->A->E->C
# python3中统一都是新式类
# pyhon2中才分新式类与经典类
总结
定义的基类A最后访问,(除了A,从左到右,按列)
不知道你是否发现,如果用正经的C3算法规则去分析一个类继承关系有点繁琐,尤其是遇到一个复杂的类也要分析很久。
所以,我自己根据经验总结了一句话赠送给大家:从左到右,深度优先,大小钻石,留住顶端,基于这句话可以更快的找到继承关系。
class M:
pass
class N:
pass
class E(M):
pass
class G:
pass
class K:
pass
class H(K):
pass
class D(G, H):
pass
class F(M, N):
pass
class P:
pass
class C(E, F):
pass
class B(D, E):
pass
class A(B, C, P):
pass
print(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 = nickname
self.life_value = life_value
self.aggresivity = aggresivity
def attack(self, enemy):
enemy.life_value -= self.aggresivity
class 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 = weapon
def 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 = nickname
self.life_value = life_value
self.aggresivity = aggresivity
def attack(self, enemy):
enemy.life_value -= self.aggresivity
class 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=name
self.age=age
self.sex=sex
class Teacher(People): def init(self,name,age,sex,level,salary,): super().init(name,age,sex)
self.level=level
self.salary=salary
def 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_time
def 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>
## 抽象类
![image.png](https://cdn.nlark.com/yuque/0/2021/png/12405790/1612497591189-7f2c62ef-0c9a-4a78-9628-74f98393d199.png#height=118&id=Q2S5h&margin=%5Bobject%20Object%5D&name=image.png&originHeight=236&originWidth=1262&originalType=binary&ratio=1&size=93843&status=done&style=none&width=631)
**作用**
- 如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。
- 接口提取了一群类共同的函数,可以把接口当做一个函数的集合,然后让子类去实现接口中的函数。这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。
**语法特点**:
- 继承抽象类的类,必须实现抽象类所有的函数,当然可以增加其它函数
- 抽象类只能被继承不能被实例化
```python
import abc
class Animal(metaclass=abc.ABCMeta): #只能被继承,不能被实例化
all_type='animal'
@abc.abstractmethod
def run(self):
pass
@abc.abstractmethod
def 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 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')
class Cat(Animal):
def talk(self):
print('say miamiao')
# 多态性:指的是可以在不考虑对象的类型的情况下而直接使用对象
peo1=People()
dog1=Dog()
pig1=Pig()
cat1=Cat()
# -------------------
def func(instance, absCls=Animal):
'依赖基类: 基类指向子类对象'
absCls = instance
return absCls
func(peo1).talk()
func(dog1).talk()
func(pig1).talk()
func(cat1).talk()
# -------------------
def func(animal):
'不依赖基类'
return animal
func(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,字符串str
l=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__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、在类外部无法直接obj.AttrName
2、在类内部是可以直接使用:obj.AttrName
3、子类无法覆盖父类__开头的属性
这种变形需要注意的问题是:
1、这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:类名_属性,然后就可以访问了,如a._A__N
2、变形的过程只在类的定义是发生一次,在定义后的赋值操作,不会变形
3、在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
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()
封装的意义
一:封装数据
明确的区分内外,控制外部对隐藏的属性的操作行为
class People:
def __init__(self,name,age):
self.__name=name
self.__age=age
def tell_info(self):
print('Name:<%s> Age:<%s>' %(self.__name,self.__age))
def set_info(self,name,age):
if not isinstance(name,str):
print('名字必须是字符串类型')
return
if not isinstance(age,int):
print('年龄必须是数字类型')
return
self.__name=name
self.__age=age
p=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
@property
def name(self):
# print('getter')
return self.__name
@name.setter
def name(self,val):
# print('setter',val)
if not isinstance(val,str):
print('名字必须是字符串类型')
return
self.__name=val
@name.deleter
def name(self):
print('deleter')
print('不允许删除')
p=People('mufeng')
# print(p.get_name())
# print(p.name)
# p.name
p.name='mufeng'
p.name=123
# print(p.name)
# del p.name
类中两大方法
在类内部定义的函数,分为两大类
一:绑定方法:绑定给谁,就应该由谁来调用,谁来调用就回把调用者当作第一个参数自动传入
绑定到对象的方法:在类内定义的没有被任何装饰器修饰的
绑定到类的方法:在类内定义的被装饰器classmethod修饰的方法
二:非绑定方法:没有自动传值这么一说了,就类中定义的一个普通工具,对象和类都可以使用
非绑定方法:不与类或者对象绑定
# settings.py
name='mufeng'
age=18
sex='female'
import settings
import hashlib
import time
class People:
def __init__(self,name,age,sex):
self.id=self.create_id()
self.name=name
self.age=age
self.sex=sex
def tell_info(self): #绑定到对象的方法
print('Name:%s Age:%s Sex:%s' %(self.name,self.age,self.sex))
@classmethod
def from_conf(cls):
obj=cls(
settings.name,
settings.age,
settings.sex
)
return obj
@staticmethod
def 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]) -> 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
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=name
self.age=age
def 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()