面向对象之封装

封装的概念

装指的是把属性装进一个容器,封指的是隐藏的意思,但是这种隐藏是对外隐藏,并不对内部隐藏。

如何封装

只需要在属性前加上 __ 开头,该属性就会被隐藏起来,该隐藏具备的特点有:

  1、只是一种语法意义上的变形,即 __ 开头的属性会在检测语法时发生 “ _类名__属性名 ” 的变形

  2、这种隐藏式是对外不对内的,因为在类的内部检测语法时所有的代码统一都发生了变形

  3、这种变形只在检测语法时发生一次,在类定义之后新增的 __ 开头的属性并不会发生变形

  4、如果父类不想让子类覆盖自己的属性,可以在属性前加 __ 开头

class Foo():

    __x = 111   # _Foo__x

    def __init__(self, m, n):
        self.__m = m    # self._Foo__m=m
        self.n = n

    def __func(self):  # _Foo__func
        print('Foo.func')

    def func1(self):
        print(self.__m)  # self._Foo__m
        print(self.__x)  # self._Foo__x

print(Foo.__dict__)
# Foo.__x   # 报错 AttributeError: type object 'Foo' has no attribute '__x'
print(Foo._Foo__x)
封装
class Foo:
    def __f1(self):  # _Foo__f1
        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
Foo.f1
封装

封装数据属性的真实意图

对于数据属性(属性),它的目的是将数据属性封装起来,在类外部的使用就无法直接操作该数据属性,而是需要从类的内部开一个接口给使用者,类的设计者可以在接口之上附加任意逻辑,从而严格控制使用者对属性的操作。

class People():
    def __init__(self, name):
        self.__name = name

    def tell_name(self):
        print('name: %s' %self.__name)

obj = People('湫兮')

obj.tell_name()

# 运行
name: 湫兮
查看

上面的查看名字,可以控制输出格式,既然是控制,那就不仅仅是只有查看,还有增加、删除、修改的操作,但要注意,针对某一属性没有增加的说法,比如说对于这个属性的名字,肯定没有为这个属性增加名字的说法,因此只能删除和修改

class People():
    def __init__(self, name):
        self.__name = name

    def tell_name(self):
        print('name: %s' %self.__name)

    def set_name(self, new_name):
        self.__name = new_name

obj = People('湫兮')

obj.tell_name()
obj.set_name('qiuxi')

obj.tell_name()
修改

上面是将名字 “湫兮” 改为 “qiuxi”,但这样写并不严谨,因为我不管改成什么类型都可以修改,而名字是一个字符串,因此要加上判断,如果修改的名字不是字符串类型那就输出提示信息,并且无法修改

class People():
    def __init__(self, name):
        self.__name = name

    def tell_name(self):
        print('name: %s' %self.__name)

    def set_name(self, new_name):
        if type(new_name) is not str:
            print("名字必须是字符串类型")
            return
        self.__name = new_name

obj = People('湫兮')

obj.tell_name()
obj.set_name(123)

obj.tell_name()

# 运行
name: 湫兮
名字必须是字符串类型
name: 湫兮
严谨的修改

删除也是一样的操作

class People():
    def __init__(self, name):
        self.__name = name

    def tell_name(self):
        print('name: %s' %self.__name)

    def set_name(self, new_name):
        if type(new_name) is not str:
            print("名字必须是字符串类型")
            return
        self.__name = new_name

    def del_name(self):
        del self.__name

obj = People('湫兮')

obj.tell_name()
obj.set_name("qiuxi")

obj.tell_name()

obj.del_name()

print(obj.__dict__)

# 运行
name: 湫兮
name: qiuxi
{}
删除

封装函数属性的真实意图

对于函数属性(方法),封装的目的是为了隔离复杂度。好比电脑开机,外部操作你只需要按下开机键,然后等待一会就可以使用了,但是在内部,从你按下开机键,它会做很多操作,比如电源供电、执行 BIOS 程序等,这些在内部是自动操作的。这就是一种封装的思想,按下开机键的背后封装了一堆的功能

# 取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
# 对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做隔离了复杂度,同时也提升了安全性

class ATM:
    def __card(self):
        print('插卡')

    def __auth(self):
        print('用户认证')

    def __input(self):
        print('输入取款金额')

    def __print_bill(self):
        print('打印账单')

    def __take_money(self):
        print('取款')

    def withdraw(self):
        self.__card()
        self.__auth()
        self.__input()
        self.__print_bill()
        self.__take_money()


a = ATM()
a.withdraw()
封装函数属性

property 装饰器

成人的 BMI 数值:
过轻:低于18.5
正常:18.5-23.9
过重:24-27
肥胖:28-32
非常肥胖, 高于32
  体脂参数(BMI)= 体重(kg)÷ 身高^2(m)
  EX:70kg÷(1.75×1.75)= 22.86
class People():
    def __init__(self, name, weight, height):
        self.name = name
        self.weight = weight
        self.height = height

    # 体脂参数不是一个固定的值, 而是通过身高体重计算出来的
    # 所以不能写在__init__里面作为对象的属性
    def tell_bmi(self):
        return self.weight / (self.height ** 2)

obj = People("qiuxi", 66, 1.74)
print(obj.tell_bmi())
View Code

这样写是能计算BMI了,但仔细想想,BMI 更像是一个人的数据属性,它是一个人的特征,此时在这里获取 BMI 却要通过调用 tell_bmi() 才能得到,这样写并不妥。可以设计一个解决方案,首先它的背后一定是计算 BMI 的功能,但是想要在访问的时候,把它伪装成数据属性能够直接访问。这个解决方案就是使用 property 装饰器

class People():
    def __init__(self, name, weight, height):
        self.name = name
        self.weight = weight
        self.height = height

    @property
    def bmi(self):
        return self.weight / (self.height ** 2)

obj = People("qiuxi", 66, 1.74)
print(obj.bmi)
property

现在只是把函数属性伪装成数据属性去访问它,而对于数据属性,还有修改和删除操作。在数据属性中,修改就是直接赋值的操作,但在这里它不能直接赋值,因为它不是一个真正的数据属性,但我就是要修改,该怎么操作呢,这里我换一个例子

class People():
    def __init__(self, name):
        self.__name = name

    @property
    def name(self):
        return self.__name

obj = People("qiuxi")
# 访问伪装成数据属性的name
print(obj.name)
View Code

但凡是被 property 装饰过的函数,这个函数下面都会生成一个新的装饰器 setter,setter 下面的函数名需要相同,因为对于使用者来说,操作都是使用 name 属性,只是查看和修改对应的功能不同

class People():
    def __init__(self, name):
        self.__name = name

    # 查看
    @property
    def name(self):
        return self.__name

    # 修改
    @name.setter
    def name(self, new_name):
        if type(new_name) is not str:
            print("名字必须是字符串类型")
            return
        self.__name = new_name

obj = People("qiuxi")
print(obj.name)

obj.name = "湫兮"
print(obj.name)
View Code

修改也是相同的操作,被 property 装饰过的函数下面会生成一个新的装饰器 deleter

class People():
    def __init__(self, name):
        self.__name = name

    # 查看
    @property
    def name(self):
        return self.__name

    # 修改
    @name.setter
    def name(self, new_name):
        if type(new_name) is not str:
            print("名字必须是字符串类型")
            return
        self.__name = new_name

    # 删除
    @name.deleter
    def name(self):
        del self.__name

obj = People("qiuxi")
print(obj.name)

obj.name = "湫兮"
print(obj.name)

del obj.name
print(obj.__dict__)
View Code

上面这三种操作是一种新式写法,还有一种旧式写法

class People():
    def __init__(self, name):
        self.__name = name

    # 查看
    def get_name(self):
        return self.__name

    # 删除
    def del_name(self):
        del self.__name

    # 修改
    def set_name(self, new_name):
        if type(new_name) is not str:
            print("名字必须是字符串类型")
            return
        self.__name = new_name

    # 上面定义的顺序可以打乱, 但是这里property里面必须是查看、修改、删除的顺序
    name = property(get_name, set_name, del_name)


obj = People("qiuxi")
print(obj.name)

obj.name = "湫兮"
print(obj.name)

del obj.name
print(obj.__dict__)
View Code

建议使用新式写法

以上就是封装的全部介绍,对于封装好的一个属性或方法,没事不要去访问,既然它封装了,目的就是为了不让你去访问它,然而你还去访问,这显然是不合理的

原文地址:https://www.cnblogs.com/qiuxirufeng/p/9846258.html