property装饰器
一:装饰器
装饰器是在不修改被装饰对象源代码以及调用方式的前提下为被装饰对象添加
#新功能的可调用对象#print(property)
property是一个装饰器,是用来绑定给对象的方法伪造成一个数据属性
二:案例
"""成人的BMI数值:
过轻:低于18.5
正常:18.5-23.9
过重:24-27
肥胖:28-32
非常肥胖, 高于32
体质指数(BMI)=体重(kg)÷身高^2(m)
EX:70kg÷(1.75×1.75)=22.86"""
#案例1:
classPeople:def __init__(self, name, weight, height):
self.name=name
self.weight=weight
self.height=height#定义函数的原因1:
#1、从bmi的公式上看,bmi应该是触发功能计算得到的
#2、bmi是随着身高、体重的变化而动态变化的,不是一个固定的值
#说白了,每次都是需要临时计算得到的
#但是bmi听起来更像是一个数据属性,而非功能
@propertydefbmi(self):return self.weight / (self.height ** 2)
obj1= People('egon', 90, 1.50)#print(obj1.bmi())
obj1.height= 1.60
#print(obj1.bmi())
print(obj1.bmi)#输出:
35.15624999999999egon#案例2:
classPeople:def __init__(self, name):
self.__name =name#@property
defget_name(self):return self.__name
defset_name(self, val):if type(val) is notstr:print('必须传入str类型')returnself.__name =valdefdel_name(self):print('不让删除')#def self.__name
name11=property(get_name, set_name, del_name)
obj1= People('egon')#print(obj1.get_name())
print(obj1.get_name())
obj1.set_name('xxq')print(obj1.get_name())
obj1.del_name()#输出:
egon
xxq
不让删除
egon#案例三:
classPeople:def __init__(self, name):
self.__name =name
@propertydef name(self): #obj1.name
return self.__name@name.setterdef name(self, val): #obj1.name='EGON'
if type(val) is notstr:print('必须传入str类型')returnself.__name =val
@name.deleterdef name(self): #del obj1.name
print('不让删除')#del self.__name
obj1= People('egon')#人正常的思维逻辑
print(obj1.name) #
#obj1.name=18#del obj1.name
#输出:
egon
继承
一:什么是继承
I:继承是一种创建新类的方式,新建的类可称为子类或派生类,父类又可称为基类或超类,子类会遗传父类的属性
II:需要注意的是:python支持多继承
在Python中,新建的类可以继承一个或多个父类
classParent1(object):
x= 1111
classParent2(object):pass
class Sub1(Parent1): #单继承
pass
class Sub2(Parent1,Parent2): #多继承
pass
print(Sub1.__bases__) #(,)
print(Sub2.__bases__) #(, )
print(Sub1.x) #1111
ps1: 在Python2中有经典类与新式类之分
新式类:继承了object类的子类,以及该子类的子类子子类。。。
经典:没有继承object类的子类,以及该子类的子类子子类。。。
ps2:在Python3中没有继承任何类,那么会默认继承object类,所以Python3中所有的类都是新式类
print(Parent1.__bases__) #(,)
print(Parent2.__bases__) #(,)
III:Python的多继承
#优点:子类可以同时遗传多个父类的属性,最大限度地重用代码
#缺点:
#1、违背人的思维习惯:继承表达的是一种什么"是"什么的关系
#2、代码可读性会变差
#3、不建议使用多继承,有可能会引发可恶的菱形问题,扩展性变差,
#如果真的涉及到一个子类不可避免地要重用多个父类的属性,应该使用Mixins
二:为何要用继承
用来解决类与类之间代码冗余问题
三:如何实现继承
示范1:类与类之间存在冗余问题
classStudent:
school='OLDBOY'
def __init__(self, name, age, sex):
self.name=name
self.age=age
self.sex=sexdefchoose_course(self):print('学生%s 正在选课' %self.name)classTeacher:
school='OLDBOY'
def __init__(self, name, age, sex, salary, level):
self.name=name
self.age=age
self.sex=sex
self.salary=salary
self.level=leveldefscore(self):print('老师 %s 正在给学生打分' % self.name)
示范2:基于继承解决类与类之间的冗余问题
classOldboyPeople:
school= 'OldBoy'
def __init__(self, name, age, sex):
self.name=name
self.age=age
self.sex=sexclassStudent(OldboyPeople):defchoose_course(self):print('学生%s 正在选课' %self.name)
stu_obj= Student('lili', 18, 'female')#print(stu_obj.__dict__) # {'name': 'lili', 'age': 18, 'sex': 'female'}#print(stu_obj.school) # OldBoy#stu_obj.choose_course() # 学生lili 正在选课
classTeacher(OldboyPeople):#老师的空对象,'egon',18,'male',3000,10
def __init__(self, name, age, sex, salary, level):#指名道姓地跟父类OldboyPeople去要__init__
OldboyPeople.__init__(self, name, age, sex)
self.salary=salary
self.level=leveldefscore(self):print('老师 %s 正在给学生打分' %self.name)
tea_obj= Teacher('egon', 18, 'male', 3000, 10)#print(tea_obj.__dict__) # {'name': 'egon', 'age': 18, 'sex': 'male', 'salary': 3000, 'level': 10}#print(tea_obj.school) # OldBoy
tea_obj.score()#老师 egon 正在给学生打分
单继承背景下的属性查找
示范1:
classFoo:deff1(self):print('Foo.f1')deff2(self):print('Foo.f2')
self.f1()#obj.f1()
classBar(Foo):deff1(self):print('Bar.f1')
obj=Bar()
obj.f2()#预料的结果#Foo.f2#Foo.f1
#实际的结果#Foo.f2#Bar.f1
示范2:
classFoo:deff1(self):print('Foo.f1')deff2(self):print('Foo.f2')
Foo.f1(self)#调用当前类中的f1
classBar(Foo):deff1(self):print('Bar.f1')
obj=Bar()
obj.f2()#输出:#Foo.f2#Foo.f1
示范3:
classFoo:def __f1(self): #_Foo__f1
print('Foo.f1')deff2(self):print('Foo.f2')
self.__f1() #self._Foo__f1,# 调用当前类中的f1
classBar(Foo):def __f1(self): #_Bar__f1
print('Bar.f1')
obj=Bar()
obj.f2()#Foo.f2#Foo.f1
多继承带来的菱形问题
一:菱形问题
大多数面向对象语言都不支持多继承,而在Python中,一个子类是可以同时继承多个父类的,这固然可以带来一个子类可以对多个不同父类加以重用的好处,但也有可能引发著名的 Diamond problem菱形问题(或称钻石问题,有时候也被称为“死亡钻石”),菱形其实就是对下面这种继承结构的形象比喻。
classA(object):deftest(self):print('from A')pass
classB(A):deftest(self):print('from B')pass
classC(A):deftest(self):print('from C')pass
classD(C, B):#def test(self):
#print('from D')
pass
#print(D.mro()) # 类D以及类D的对象访问属性都是参照该类的mro列表#输出:[, , , , ]
obj=D()
obj.test()#from C
print(D.test) #
print(C.mro()) #类C以及类C的对象访问属性都是参照该类的mro列表#输出:[, , ]
c=C()
c.test()#from C
总结:类相关的属性查找(类名.属性,该类的对象.属性),都是参照该类的mro
python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。 而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
二:如果多继承是非菱形继承,经典类与新式的属性查找顺序一样:
都是一个分支一个分支地找下去,然后最后找object
classE:#def test(self):
#print('from E')
pass
classF:deftest(self):print('from F')classB(E):#def test(self):
#print('from B')
pass
classC(F):#def test(self):
#print('from C')
pass
classD:deftest(self):print('from D')classA(B, C, D):#def test(self):
#print('from A')
pass
#新式类#print(A.mro()) # A->B->E->C->F->D->object
obj=A()
obj.test()#结果为:from F
三:如果多继承是菱形继承,经典类与新式类的属性查找顺序不一样:
经典类:深度优先,会在检索第一条分支的时候就直接一条道走到黑,即会检索大脑袋(共同的父类)
新式类:广度优先,会在检索最后一条分支的时候检索大脑袋
class G: #在python2中,未继承object的类及其子类,都是经典类
#def test(self):
#print('from G')
pass
classE(G):#def test(self):
#print('from E')
pass
classF(G):deftest(self):print('from F')classB(E):#def test(self):
#print('from B')
pass
classC(F):deftest(self):print('from C')classD(G):deftest(self):print('from D')classA(B, C, D):#def test(self):
#print('from A')
pass
#新式类#print(A.mro()) # A->B->E->C->F->D->G->object
#经典类:A->B->E->G->C->F->D
obj =A()
obj.test()#
四:总结:
多继承到底要不用???
要用,但是规避几点问题
1.继承结构尽量不要过于复杂
2.推荐使用mixins机制:在多继承的背景下满足继承的什么"是"什么的关系