文章目录
一、面向对象的三大特征
- 封装
- 封装 <–> 整合
- 继承
- 多态
一、封装
1. 什么是封装
- 封装指的就是把数据与功能都整合到一起,听起来是不是很熟悉,没错,我们之前所说的”整合“二字其实就是封装的通俗说法。除此之外,针对封装到对象或者类中的属性,我们还可以严格控制对它们的访问,分两步实现:隐藏与开放接口
2. 隐藏属性
- 将封装的属性隐藏
1. 如何隐藏: 在属性名前加__前缀, 就会实现一个对外隐藏属性的效果
# 如何隐藏
class Foo():
x = 1
def f1(self):
print('from f1')
f = Foo()
print(f.x)
f.f1()
# 隐藏 加__
class Foo():
__x = 1
def __f1(self):
print('from f1')
f = Foo()
print(f.x) # 报错找不到x
print(f.__x) # 报错找不到x
2 为何要隐藏
- 隐藏数据属性
- 将数据隐藏起来就限制了类外部对数据的直接操作,然后类内应该提供相应的接口来允许类外部间接地操作数据,接口之上可以附加额外的逻辑来对数据的操作进行严格地控制
# 开发者
class People():
def __init__(self, name):
self.__name = name
def get_name(self):
# 通过该接口就可以间接的访问到名字属性
print(self.__name)
return self.__name
def set_name(self, val):
# 通过该接口改变名字属性
if type(val) is not str:
print('小垃圾, 必须传入字符串类型')
self.__name = val
# 使用者
obj = People('ee')
obj.get_name()
obj.set_name('zpp')
obj.set_name(123)
- 隐藏函数属性、方法属性
- 目的的是为了隔离复杂度, 只在内部用, 例如ATM程序的取款功能,该功能有很多其他功能组成,比如插卡、身份认证、输入金额、打印小票、取钱等,而对使用者来说,只需要开发取款这个功能接口即可,其余功能我们都可以隐藏起来
# 隐藏 加__
class Foo():
__x = 1 # 变形成 _Foo__x
def __f1(self):
print('from f1')
def f2(self):
print(self.__x)
print(self.__f1())
f = Foo()
# print(f.x) # 报错找不到x
# print(f.__x) # 报错找不到x
print(Foo.__dict__)
print(Foo._Foo__x) # 不要这么做
print(f._Foo__f1) # 不要这么做
3 该隐藏需要注意的问题
- 在类外部无法直接访问双下滑线开头的属性,但知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如Foo._A__N,所以说这种操作并没有严格意义上地限制外部访问,仅仅只是一种语法意义上的变形。
- 这种隐藏对外不对内, 因为__开头的属性会在检查类体语法时统一发生变形
- 这种变形操作, 只在检查语法的时候发生一次,
二、继承
1. 什么是继承
- 继承是一种创建新类的方式, 新建的类可成为子类或者派生类, 父类又可称为基类或超类
# 封装
class Parent1:
pass
class Parent2:
pass
class Sub1(Parent1): # 单继承
pass
class Sub2(Parent1, Parent2): # 多继承
pass
# 查看继承谁
print(Sub1.__bases__)
print(Sub2.__bases__)
print(Parent2.__bases__)
print(Parent1.__bases__)
- 需要注意的是: Python支持多继承, 在Python中, 新建的类可以继承一个或多个父类
- Python多继承:
- 优点:子类可以同时遗传多个父类的属性, 最大限度的重用代码
- 缺点:
- 违背人的思维习惯: 继承表达的是一种什么“是”什么的关系
- 代码可读性会变差
- 不建议使用多继承, 扩展性变差, 有可能给会引发可恶的菱形问题, 扩展性变差 - 如果真的涉及到一个子类必须要重用多个父类的属性, 应该使用Mixins
2. 为什么要用继承
- 来解决类与类之间的代码冗余问题
2.1 单继承下的属性查找
class Foo():
def f1(self):
print('Foo.f1')
def f2(self):
print('Foo.f2')
self.f1() # obj.f1()
class Bar(Foo):
def f1(self):
print('Bar.f1')
obj = Bar()
obj.f2() # 打印Foo.f2 Bar.f1
# 需求: 如何让她打印自己的f1
class Foo():
def __f1(self):
print('Foo.f1')
def f2(self):
print('Foo.f2')
self.__f1() # self.__Foo.__f1
class Bar(Foo):
def f1(self): # __Bar.__f1
print('Bar.f1')
obj = Bar()
obj.f2() # 打印Foo.f2 Bar.f1
print(Bar.__mro__) # 查找的优先级
2.2 多继承与菱形继承
三、多态
1. 什么是多态?
- 同一事物的多种形态
class Animal:
pass
class People(Animal):
pass
class Dog(Animal):
pass
class Pig(Animal):
pass
2. 为什么要有多态=》多态会带来什么样的特性, 多态性
# 多态
class Animal: # 统一所有子类的方法
def say(self):
print('动物基本的发声频率。。。')
class People(Animal):
def say(self):
super().say()
print('嘤嘤嘤')
class Dog(Animal):
def say(self):
super().say()
print('汪汪汪')
class Pig(Animal):
def say(self):
super().say()
print('哼哼哼')
# 正常情况
obj1 = People()
obj2 = Dog()
obj3 = Pig()
obj1.say()
obj2.say()
obj3.say()
# 多态: 定义统一接口, 接收传入动物对象
def animal_say(animal):
animal.say()
animal_say(obj1)
animal_say(obj2)
animal_say(obj3)
# python内部多态据类
len('字符串, 列表, 字典')
print('hello'.__len__())
print([1, 2, 3].__len__())
print({'a': 1, 'b': 3}.__len__())
print(len('hello'))
print(len([1, 2, 3]))
print(len({'a': 1, 'b': 3}))
# 有了统一标准就能定义一个统一的接口
def my_len(val):
return val.__len__()
3. Python 的多态玩法
- 鸭子类型
# 不用继承的方法的好处, 继承是一种耦合思想, 没有继承也能实现, 达到了一个解耦合的思想
# 这就是鸭子类型, 不是什么是什么的关系, 长得像就可以了
class Cpu:
def read(self):
print('cpu read')
def wright(self):
print('cpu wright')
class Mem:
def read(self):
print('mem read')
def wright(self):
print('mem wright')
class Txt:
def read(self):
print('txt read')
def wright(self):
print('txt wright')
obj1 = Cpu()
obj2 = Mem()
obj3 = Txt()
obj1.read()
obj1.wright()
obj2.read()
obj2.wright()
obj3.read()
obj3.wright()
相关连接(笔记来自于视频课程的归类整理):
[1]: https://www.bilibili.com/video/BV1QE41147hU?p=17
[2]: https://www.zhihu.com/column/c_1189883314197168128
[3]: https://www.cnblogs.com/linhaifeng/articles/6384466.html