面向对象的三大特点是封装、继承、多态;本篇将详细解释一下封装
封装就是指隐藏对象中一些不希望外部所访问到的属性或方法,即为了保证安全
如何封装,或者说如何隐藏属性或方法?
在定义类时,将属性或方法名修改为外部并不知道的名称,如下定义类
class dog:
def __init__(self,name,age):
self.name = name
self.age = age
def hello(self):
print(f'大家好,我是{self.name}')
#实例化一个对象
d = dog('哈士奇',3)
d.hello()
#直接可以修改类中的属性,不安全
d.name = '金毛'
d.hello()
执行结果
大家好,我是哈士奇
大家好,我是金毛
那么我们可以这样定义,即内部使用hidden_name
来定义属性,外部就不能直接通过name
来修改访问类中的属性了
class dog:
def __init__(self,name,age):
self.hidden_name = name
self.hidden_age = age
def hello(self):
print(f'大家好,我是{self.hidden_name}')
#实例化一个对象
d = dog('哈士奇',3)
d.hello()
#外部不可以修改类中的属性
d.name = '金毛'
d.hello()
执行结果
大家好,我是哈士奇
大家好,我是哈士奇
这样外部虽然不能通过name
来修改属性值,但是还是可以通过hidden_name
来修改属性值,当然我们是站在上帝的视角来看待这个问题,对于实例化类的对象而言,其并不是完全知道类中的属性与方法
那么,此时对象如何知道和修改属性的值呢?
很简单,在类中定义用于修改和获取属性的方法 set_name
、get_name
需要提供一个getter和setter方法使外部可以访问到属性
getter 获取对象中的指定属性(get_属性名)
setter 用来设置对象的指定属性(set_属性名)
且看如下代码
class dog:
def __init__(self,name,age):
self.hidden_name = name
self.hidden_age = age
def hello(self):
print(f'大家好,我是{self.hidden_name}')
#用于实例化对象设置修改类属性的方法
def set_name(self,name):
self.hidden_name = name
#用于实例化对象获取得到类属性的方法
def get_name(self):
return self.hidden_name
#实例化一个对象
d = dog('哈士奇',3)
d.hello()
#外部使用get_name()方法来修改类中的属性
d.set_name('金毛')
d.hello()
执行结果
大家好,我是哈士奇
大家好,我是金毛
这样做有什么扩展的?
set_name
中判断输出的合法性,比如age
的大小,性别的合法;如下代码所示def set_age(self,age):
if age > 0:
self.hidden_age = name
getter
方法可以表示一些计算的属性 ;比如圆面积 矩形面积class Rectangle:
def __init__(self,width,height):
self.hidden_width = width
self.hidden_height = height
def get_width(self):
return self.hidden_width
def get_height(self):
return self.hidden_height
def set_width(self , width):
self.hidden_width = width
def set_height(self , height):
self.hidden_height = height
def get_area(self):
return self.hidden_width * self.hidden_height
d = Rectangle(2,3)
area = d.get_area()
print(area)
执行结果
6
这样做有什么好处呢?
使用封装,确实增加了类的定义的复杂程度,但是它也确保了数据的安全性
以上只是根据在面向对象中对封装的简单概述,但是在真实环境中用的不是以上的这些方式,用的是Python中自动的处理封装的属性,如下所示
可以为对象的属性使用双下划线开头,__xxx
双下划线开头的属性,是对象的隐藏属性,隐藏属性只能在类的内部访问,无法通过对象访问
其实隐藏属性只不过是Python自动为属性改了一个名字
实际上是将名字修改为了,_类名__属性名 比如 __name -> _Person__name,这样还是可以访问的
防君子不防小人
且看如下代码
class dog:
def __init__(self,name,age):
self.__name = name
self.__age = age
def hello(self):
print(f'大家好,我是{self.__name},我{self.__age}岁了')
#用于实例化对象设置修改类属性的方法
def set_name(self,name):
self.__name = name
#用于实例化对象获取得到类属性的方法
def get_name(self):
return self.__name
#实例化一个对象
d = dog('哈士奇',3)
d.hello()
print(d._dog__name)
# 其实可以通过如下方式修改私有属性的值,但是不会这么做,因为外部是不可以修改私有属性的值的
d._dog__name = '金毛'
d._dog__age = '4'
d.hello()
执行结果
大家好,我是哈士奇,我3岁了
哈士奇
大家好,我是金毛,我4岁了
使用 __ 开头的属性,实际上依然可以在外部访问,所以这种方式我们一般不用 __
如下代码
class dog:
def __init__(self,name,age):
self._name = name
self._age = age
def hello(self):
print(f'大家好,我是{self._name},我{self._age}岁了')
def set_name(self,name):
self._name = name
def get_name(self):
return self._name
#实例化一个对象
d = dog('哈士奇',3)
d.hello()
#当然还是可以访问的,但是不会这么做
print(d._name)
d._name = '罗威纳'
d.hello()
执行结果
大家好,我是哈士奇,我3岁了
哈士奇
大家好,我是罗威纳,我3岁了
如果以上的内容都掌握了,那么来加强一下吧,成为高手只差一步
什么是像调用属性一样使用get
方法,且看代码的运转
class dog:
def __init__(self,name,age):
self._name = name
self._age = age
def hello(self):
print(f'大家好,我是{self._name},我{self._age}岁了')
@property
def name(self):
print('get方法执行了~~~')
return self._name
# setter方法的装饰器:@属性名.setter
@name.setter
def name(self,name):
print('set方法执行了~~~')
self._name = name
@property
def age(self):
print('get方法执行了~~~')
return self._age
# setter方法的装饰器:@属性名.setter
@age.setter
def age(self,age):
print('set方法执行了~~~')
self._age = age
#实例化一个对象
d = dog('哈士奇',3)
d.hello()
print('--'*30)
# 调用set方法,修改name的值;注意看上去是修改属性名的方式,其实是调用的了方法,注意看输出
d.name = '德牧'
d.hello()
print('--'*30)
d.age = 4
d.hello()
print('--'*30)
print(d.name)
执行结果
大家好,我是哈士奇,我3岁了
------------------------------------------------------------
set方法执行了~~~
大家好,我是德牧,我3岁了
------------------------------------------------------------
set方法执行了~~~
大家好,我是德牧,我4岁了
------------------------------------------------------------
get方法执行了~~~
德牧
你学废了吗?