封装是面向对象的核心特性
###将封装的属性进行隐藏
在属性名前加__前缀,就会实现实现一个对外隐藏属性
####特点
- 在类的外部无法直接访问隐藏属性; 知道了类名和属性名可以拼出名字_类名__属性名,然后就可以访问了;
因此这种隐藏 *** 作并没有严格意义上的限制外部访问类的属性,只是一种语法意义上的变形 *** 作 - 这种隐藏对外部对内, __开头的属性在类的定义阶段统一发生变形;这种变形只在检查类体语法的时候发生
class Foo:
__x = 1 # _Foo__x
def __f1(self):print(“在类的外部是无法访问到我的”) # _Foo__f1
print(Foo.dict)
####隐藏数据属性
#####为何隐藏
为了不让外部使用者直接访问该属性
#####如何使用
在类内部定义接口,让使用者通过接口来访问隐藏属性,开发者可以在接口中定义任何逻辑
class Person:
def init(self, name, age, gender):
self.__name = name
self.__age = age
self.__gender = gender定义接口访问隐藏属性 def get(self):
print(“姓名: %s 年龄: %s 性别: %s” % (self.__name, self.__age, self.__gender))
person1 = Person(“斯达舒”, 12, “female”)
person1.get()
####隐藏函数属性
#####为何隐藏
目的是为了隔离复杂度,只在类内使用
###property
将绑定给对象的方法伪造成一个数据属性
####案例1
class Person:
def init(self, name, height, weight):
self.name = name
self.height = height
self.weight = weight
@property
def bmi(self):
return self.weight / (self.height**2)
person1 = Person(“漆仲勇”, 1.79, 75)
print(person1.bmi)
####案例2
class People:
def init(self, name):
self.__name = name
@property
def name(self):
return self.__name
@name.setter
def name(self, val):
if type(val) is not str:
print(“必须传入字符串类型”)
return
self.__name = val
@name.deleter
def name(self):
print(“不让删除”)
people1 = People(“DJS”)
print(people1.name)
people1.name = “djs”
print(people1.name)
del people1.name
- 继承是一种创建新类的方式,新建的类可称为子类或派生类,父类可称为基类或超类,解决类与类之间代码冗余的问题
- python支持多继承,新建的类可以支持一个或多个父类
优点:子类可以同时遗传多个父类的属性,极大限度大的重用代码
缺点:降低代码的可读性和扩展性 - 新式类:继承了object类的子类,以及该子类的子类…
- 经典类:没有继承object类的子类,以及该子类的子类…
- 在python3中没有继承任何类,那么会默认继承object类,所以python3中所有的类都是新式类
- 父类当中的属性会遗传给子类
class Person1(object): # object类可有可无,当需要与python2兼容的时候可以有object类
pass
class Person2(object):
pass
单继承:
class Male(Person1):
pass
多继承:
class Female(Person1, Person2):
pass
class Student:
school = “tins_hua”
def init(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
def choose_course(self):
print(“学生 %s 正在选课” % self.name)
class Teacher:
school = “tins_hua”
def init(self, name, age, sex, salary, level):
self.name = name
self.age = age
self.sex = sex
self.salary = salary
self.level = level
def score(self):
print(“老师 %s 正在给学生打分” % self.name)
解决方案
class TinsHuaPeople:
school = “tins_hua”
def init(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
class Student(TinsHuaPeople):
def choose_course(self):
print(“学生 %s 正在选课” % self.name)
student1 = Student(“李华”, 12, “男”)
student1.choose_course()
print(student1.school)
class Teacher(TinsHuaPeople):
def init(self, name, age, sex, salary, level):
TinsHuaPeople.init(self, name, age, sex)
self.salary = salary
self.level = level
def score(self):
print(“老师 %s 正在给学生打分” % self.name)
teacher1 = Teacher(“李华”, 22, “female”, 200000, 99)
print(teacher1.dict)
print(teacher1.school)
class Foo:
def f1(self):
print(“Foo.f1()”)
# 或者
def __f1(self):
print(“Foo.f1()”)
def f2(self):
print(“Foo.f2()”)
# self.f1()
# 调用当前类中的f1
Foo.f1()
# 调用当前类中的f1
self.__f1()
class Bar(Foo):
def f1(self):
print(“Bar.f1()”)
object1 = Bar()
object1.f2()
# 结果是: Foo.f2()
# Bar.f1()
- python多继承中的菱形问题(钻石问题)
在python中,一个子类可以继承多个父类,但这可能会引发著名的菱形问题
B C
D
D继承B和C, B和C继承于A; 如果A中有一个方法,B和C都重写了该方法,那么D继承的是那个版本的方法
# 菱形继承 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, C): pass obj = D() obj.test() # 结果是 from B
对于开发者定义的每个类,python都会计算出一个方法解析顺序(MOR)列表,该MOR列表就是一个简单的所有基类的线性顺序表
print(D.mro())
[
类以及该类的对象访问属性都是参照该类的mro列表
python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性算法来实现的,它实际上就是合并所有父类的MRO列表。遵循以下三条准则:
- 子类会先于父类被检查
- 多个父类会根据他们在列表中的顺序倍被检查
- 如果对下一个类存在两个合法的选择,选择第一个父类
如果多继承是非菱形继承,在新式类中python2与python3的属性查找顺序一样,都是一个分支一个分支的依次查找下去,最后查找object
class E:
def test(self):
print(“from E”)
class F:
def test(self):
print(“from F”)
class B(E):
def test(self):
print(“from B”)
class C(F):
def test(self):
print(“from C”)
class D:
def test(self):
print(“from D”)
class A(B, C, D):
def test(self):
print(“from A”)
pass
# 新式类
print(A.mro()) # [
#
在菱形继承下,那么经典类与新式类会有不同的MRO列表,分别对应属性的两种查找方式:广度优先和深度优先
当类是 经典类时,多继承情况下,在被查找的属性不存在时,会按照深度优先的方式查找下去(在检索第一条分支的时候就会检索公共父类)
class G:
def test(self):
print(“from G”)
class E(G):
def test(self):
print(“from E”)
class F(G):
def test(self):
print(“from F”)
class B(E):
def test(self):
print(“from B”)
class C(F):
def test(self):
print(“from C”)
class D(G):
def test(self):
print(“from D”)
class A(B, C, D):
def test(self):
print(“from A”)
pass
查找顺序是:A -> B -> E -> G -> C -> F -> D
当类是 新式类时,多继承情况下,在被查找的属性不存在时,会按照广度优先的方式查找下去(在检索最后一条分支的时候查找公共父类)
class G(object):
def test(self):
print(“from G”)
class E(G):
def test(self):
print(“from E”)
class F(G):
def test(self):
print(“from F”)
class B(E):
def test(self):
print(“from B”)
class C(F):
def test(self):
print(“from C”)
class D(G):
def test(self):
print(“from D”)
class A(B, C, D):
def test(self):
print(“from A”)
pass
print(A.mro())
查找顺序是[
- 核心:在多继承背景下,尽可能地提升多继承的可读性
- 注意:使用Mixin类实现多重继承要非常小心
(1)首先它必须表示某一种功能,而不是某个物品;python对于mixin类的命名一般以Mixin,able,ible为后缀
(2)其次它必须责任单一,如果有多个功能,则写多个Mixin类
(3)不依赖于子类的实现
(4)如果子类没有继承这个Mixin类,也可以工作,只是缺少某个功能
class Vehicle:
pass
class Flyable: # 子类混用某些功能的时候,在类名后加Mixin后缀,提示程序员这是一个混合的功能,不是子类的父类
def fly(self):
pass
class CivilAircraft(Flyable, Vehicle):
pass
class Helicopter(Flyable, Vehicle):
pass
class Car(Vehicle):
pass
- 指名道姓调用某一个类下的函数,不依赖于继承
class TinsHuaPeople:
school = “tins_hua”
def init(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
class Teacher(TinsHuaPeople):
def init(self, name, age, sex, salary, level):
TinsHuaPeople.init(self, name, age, sex)
self.salary = salary
self.level = level
def score(self):
print(“老师 %s 正在给学生打分” % self.name)
teacher1 = Teacher(“李华”, 22, “female”, 200000, 99)
print(teacher1.dict)
print(teacher1.school) - supper调用父类提供给自己的方法,严格依赖于继承
调用super()函数会得到一个特殊的对象,该对象会参照发起属性查找的那个类的mro列表,去查找要找的属性
class TinsHuaPeople:
school = “tins_hua”
def init(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
class Teacher(TinsHuaPeople):
def init(self, name, age, sex, salary, level):
super().init(name, age, sex) # 调用的是方法,自动传参,不需要传入self参数
self.salary = salary
self.level = level
def score(self):
print(“老师 %s 正在给学生打分” % self.name)
teacher1 = Teacher(“李华”, 22, “female”, 200000, 99)
print(teacher1.dict)
print(teacher1.school)
super
案例
class A:
def test(self):
print(“from A”)
super().test()
class B:
def test(self):
print(“from B”)
class C(A, B):
pass
obj = C()
obj.test()
# print(C.mro()) # [
, , , ]
在一个类中以另外一个类的对象作为数据属性,称为类的组合。组合与继承都是用来解决代码的复用问题,不同的是,继承是一种是
的关系
比如教师是人,学生是人,当类之间有很多相同的属性时,应该用继承;
而组合是一种有
的关系,比如学生有姓名,班级,当类之间有显著不同,并且较小的类是较大类所需的组件时,应该使用组合
- 多态指的是同一种事物有多种形态
# 动物的多种形态
class Animal:
pass
class Monkey(Animal):
pass
class Pig(Animal):
pass
class People(Animal):
pass - 多态会带来什么样的特性
多态性指的是可以在不考虑对象具体类型的情况下而直接使用对象
class Animal:
def say(self):
print("动物基本的发声频率 “, end=” ")
class Monkey(Animal):
def say(self):
super().say()
print(“猴子的发声频率”)
class Pig(Animal):
def say(self):
super().say()
print(“猪的发声频率”)
class People(Animal):
def say(self):
super().say()
print(“人的发声频率”)
obj1 = Monkey()
obj2 = Pig()
obj3 = People()
# obj1.say() 结果:动物基本的发声频率 猴子的发声频率
# obj2.say() 结果:动物基本的发声频率 猪的发声频率
# obj3.say() 结果:动物基本的发声频率 人的发声频率
- 父类同一了所有子类的方法
定义统一的接口,接收传入的动物对象
def animal_say(animal):
animal.say()
animal_say(obj1)
animal_say(obj2)
animal_say(obj3)
# 动物基本的发声频率 猴子的发声频率
# 动物基本的发声频率 猪的发声频率
# 动物基本的发声频率 人的发声频率
- python推崇的鸭子类型:不采用父类继承的方式实现多态,而是使多个类中有相似的方法,从而不考虑对象的类型而直接使用对象
class Cpu:
def read(self):
print(“cpu read”)
def write(self):
print(“cpu write”)
class Mem:
def read(self):
print(“mem read”)
def write(self):
print(“mem write”)
class Txt:
def read(self):
print(“txt read”)
def write(self):
print(“txt write”)
obj1 = Cpu()
obj2 = Mem()
obj3 = Txt()
obj1.read()
obj1.write()
obj2.read()
obj2.write()
obj3.read()
obj3.write()
结果:
cpu read
cpu write
mem read
mem write
txt read
txt write
5. 总结
综上可知,多态性的本质在于不同的类中定义有相同的方法名,这样就可以不考虑类而统一用一种方式去使用对象
print("mem read")
def write(self):
print("mem write")
class Txt:
def read(self):
print(“txt read”)
def write(self):
print(“txt write”)
obj1 = Cpu()
obj2 = Mem()
obj3 = Txt()
obj1.read()
obj1.write()
obj2.read()
obj2.write()
obj3.read()
obj3.write()
结果:
cpu read
cpu write
mem read
mem write
txt read
txt write
5. 总结
综上可知,多态性的本质在于不同的类中定义有相同的方法名,这样就可以不考虑类而统一用一种方式去使用对象
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)