详解Python中的封装

详解Python中的封装

面向对象的三大特点是封装、继承、多态;本篇将详细解释一下封装

基础入门 —> setter、getter

封装就是指隐藏对象中一些不希望外部所访问到的属性或方法,即为了保证安全

如何封装,或者说如何隐藏属性或方法?

在定义类时,将属性或方法名修改为外部并不知道的名称,如下定义类

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_nameget_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

这样做有什么好处呢?

使用封装,确实增加了类的定义的复杂程度,但是它也确保了数据的安全性

  1. 隐藏了属性名,使调用者无法随意的修改对象中的属性
  2. 增加了getter和setter方法,很好的控制属性是否是只读的
    如果希望属性是只读的,则可以直接去掉setter方法
    如果希望属性不能被外部访问,则可以直接去掉getter方法
  3. 使用setter方法设置属性,可以增加数据的验证,确保数据的值是正确的
    在方法中判断年龄等属性的数值是否满足正常范围 >0
  4. 使用getter方法获取属性,使用setter方法设置属性;可以在读取属性和修改属性的同时做一些其他的处理
  5. 用getter方法可以表示一些计算的属性 圆面积 矩形面积

进阶使用 —> __ 私有属性 _

以上只是根据在面向对象中对封装的简单概述,但是在真实环境中用的不是以上的这些方式,用的是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岁了

如果以上的内容都掌握了,那么来加强一下吧,成为高手只差一步


强化加强 —> property

  • property装饰器,用来将一个get方法,转换为对象的属性
  • 添加为property装饰器以后,我们就可以像调用属性一样使用get方法
  • 使用属性的方式去调用方法,看上去是对属性赋值,但是本质上是调用方法的
  • 使用property装饰的方法,必须和属性名是一样的
  • 设置setter前必须设置getter

什么是像调用属性一样使用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方法执行了~~~
德牧

你学废了吗?


  • 27
    点赞
  • 96
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值