python 面向对象三大特性之封装与多态
python 面向对象三大特性之封装与多态
在上一篇中,我们将继承这一重要的面向对象的特性进行了介绍。
这一篇是对剩下的封装和多态进行讲解。
面向对象之封装
- 封装:就是将数据和功能'封装'起来。
- 隐藏:将数据和功能隐藏起来不让用户直接调用,而是开发一些接口间接调用从而可以在接口内添加额外的操作。
- 伪装:将类里面的方法伪装成类里面的数据。
这第一点不必展开了,就是class关键字做的事,在python面向对象编程思想及语法基础中就已经介绍了。
隐藏
类在定义阶段类体中:
如果属性的名字前面加了两个下划线,那么它们的名字在后续的调用阶段就无法直接通过原本的名字调用到:
class A:
__name = 'leethon'
print(A.__name) # AttributeError: type object 'A' has no attribute '__name'
如上述代码中报出属性错误,明明定义了这个属性,却无法通过这个名字调用。
检查一下类A的属性:
print(A.__dict__) #
"""
{'__module__': '__main__', '_A__name': 'leethon', '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
"""
主要看这个'_A__name': 'leethon'
与我们之前在类体中定义的属性十分相像,但是属性名变了个形式:
_类名__属性名
,所以我们无法通过原本的__name
来找到这个属性。
而如果我们在类体中使用__name
来查找这个属性。
class A:
__name = 'leethon'
@classmethod
def get(cls):
print(cls.__name)
A.get()
"""
leethon # 访问到了这个数据
"""
也就是说,定义这个类的人可以在类体中使用这些数据。
除了类,对象也可以设置隐藏属性,但是只能在类体中设置,对象在类体中设置独有方式的方法就只有__init__
方法:
class A:
def __init__(self, name, age, hobby):
self.__name = name
self.__age = age
self.__hobby = hobby
以上就是隐藏数据与功能不让用户使用的一些方法。
但是数据和功能本身就是拿来用的,不能通过这个方式用,我们在设计这个类的人就要提供另外的方式去让用户可以以我们限定的方式去使用这些数据:
让用户只能读,不能改
# 常规属性:
class A:
def __init__(self, name):
self.name = name
a = A('leethon')
print(a.name) # leethon
a.name = 'lee'
print(a.name) # lee
"""leethon直接被改动,变短了,这怎么行呢,我这个对象的名字写了你就不要改了,看看就好"""
# 只读属性:
class B:
def __init__(self, name):
self.__name = name
def get_name(self):
print(self.__name)
a = B('leethon')
a.get_name() # leethon
a.__name = 'lee'
a.get_name() # leethon
""" leethon这次保持了原本的长度,因为外面使用的__name只是添加了个新属性,没有对原本的属性造成修改,不过用户还是可以通过get_name的函数查看对象的属性值 """
隐藏总结
- 在类体中以
__属性名
的方式定义属性,在类体中可以通过同样的方式调用 - 在类体外以
__属性名
的方式定义属性,在任何地方都可以通过同样的方式调用(没有隐藏) - 在类体中以隐藏方式定义的属性,理论上可以在类体外访问到,但不建议。
伪装
我们刚才的例子中,是用get_name()
提供了一个接口来访问__name
,但是我们原本是通过a.name
就能访问的,现在却只能用一种函数的方式来取,会给调用者一些困惑。
所以在python中我们可以通过装饰器(内置的)来将函数的功能属性,伪装为数据属性。
class C:
def __init__(self, name):
self.__name = name
@property # 属性伪装装饰器
def name(self):
return self.__name
a = C('leethon')
print(a.name) # leethon
a.name = 'lee' # AttributeError: can't set attribute
print(a.name)
我们通过原本的a.name
的调用方式就可以访问到name了,但是不能通过原本的方式对name属性进行更改。
除了把函数伪装成只可以查的数据属性,也可以伪装为可以改、可以删的数据属性:
class D:
def __init__(self, name):
self.__name = name
@property # 属性伪装装饰器
def name(self):
return self.__name
@name.setter # 让属性可更改
def name(self, value):
self.__name = value
@name.deleter # 让属性可删除
def name(self):
del self.__name
a = D('leethon')
print(a.name) # leethon
a.name = 'lee'
print(a.name) # lee
del a.name
print(a.name) # 提示没有这个属性的报错
在使用setter和deleter之前都需要加装property才可以加装这两个属性,即先能查后才能改或者删。
伪装总结
- 我们隐藏属性是为了限制用户对这个属性的增删改查的权限,开放接口函数让用户只能按照我们限定给他们的方式来使用这些属性。
- 通过property限制用户只能读取某个属性
- 加装property后可以通过在加装
函数名.setter
和函数名.deleter
的方式让数据可改和可删
面向对象之多态
鸭子模型
面向对象中多态是指一种事物可以有多种形态,但是针对相同的功能应该定义相同的方法。
这样无论我们拿到的是哪个具体的事物,都可以通过相同的方法调用功能。
比如,数据类型中的字符串、列表、字典,我们都可以使用数据.__len__
的方式得到它们的元素个数。
s1 = 'hello world'
l1 = [11, 22, 33, 44]
d = {'name': 'jason', 'pwd': 123}
print(s1.__len__()) # 11
print(l1.__len__()) # 4
print(d.__len__()) # 2
“鸭子测试”可以这样表述:一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟可以被称为鸭子。在鸭子类型中,关注点在于对象的行为,而不是关注对象所属的类型。
上述引用于一段对鸭子模型的表述。
在Linux系统中,一切皆文件,实际上以面向对象的表述来说,就是指所有的对象都是可以读写的。
那么我们可以定义一个父类,里面有读写功能,而所有类都继承这个类,也都应该有自己的读写功能。
class File:
def read(self): pass
def write(self): pass
class Memory(File):
def read(self): pass
def write(self): pass
class Disk(File):
def read(self): pass
def write(self): pass
python语言推崇鸭子模型,所以只要像就可以,但是你如果不像,也不会报错。
抽象类
如果想要通过父类对子类需要定义的函数做限制,也可以通过内置模块abc
中的元类装饰器来限制。
通过以下这种方式定义的类是抽象类,抽象类不能实例化产生对象。
import abc
class File(metaclass=abc.ABCMeta):
@abc.abstractmethod # 限制继承这个类的子类必须有read这个函数属性,否则报错
def read(self): pass
@abc.abstractmethod
def write(self): pass
class A(File): # 以上述类为父类
pass
obj = A()
# 运行报错,TypeError: Can't instantiate abstract class A with abstract methods read, write
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律