Python知识——封装、property

  • 时间:
  • 浏览:
  • 来源:互联网

Python知识

    • 封装
      • 将封装的属性进行隐藏操作
      • 为何要隐藏属性?
    • 特性(property)
      • property第一种使用方式
      • property第二种使用方式

本文参考:面向对象之封装

封装

面向对象三大特性最核心的一个特性

封装<—>整合

将封装的属性进行隐藏操作

如何隐藏属性:在属性名前加__前缀,就会实现一个对外隐藏属性效果

class Foo:
    __x = 1

    def __f1(self):
        print('hello world')

'''
    print(Foo.__x)
AttributeError: type object 'Foo' has no attribute '__x'
'''

其实这仅仅只是一种变形操作且仅仅只在类定义阶段发生变形,查看一下:

print(Foo.__dict__)

'''
'_Foo__x': 1, '_Foo__f1': <function Foo.__f1 at 0x0000013977EC9430>
'''

可以发现:类中所有双下划线开头的名称如__x都会在类定义时自动变形成:_类名__x的形式
变形后的属性格式,依然可以被访问

print(Foo._Foo__x)
print(Foo._Foo__f1)

'''
1
<function Foo.__f1 at 0x0000019647CB9430>
'''

并且这种变形对外不对内,在类内部可以直接访问.
为何呢? 类在定义阶段会进行语法检测,一旦发现了_ _开头的属性就会直接变形为 _Foo__,而到了类外部时,类的变形已经完成了,所以在类外部无法访问

class Foo:
    __x = 1

    def __f1(self):
        print('hello world')

    def f2(self):
        print(self.__x)
        print(self.__f1)


obj = Foo()
obj.f2()

'''
1
<bound method Foo.__f1 of <__main__.Foo object at 0x0000016660539AF0>>
'''

为何要隐藏属性?

封装的真谛在于明确地区分内外,封装的属性可以直接在内部使用,而不能被外部直接使用,然而定义属性的目的终归是要用,外部要想用类隐藏的属性,需要我们为其开辟接口,让外部能够间接地用到我们隐藏起来的属性,那这么做的意义何在???

  1. 隐藏数据属性

将数据隐藏起来这不是目的。隐藏起来然后对外提供操作该数据的接口,然后我们可以在接口附加上对该数据操作的限制,以此完成对数据属性操作的严格控制。

如果不进行隐藏数据:外部使用者就可以直接对对象的name属性进行修改,会直接绕过字符格式限时。

class Teacher:
    def __init__(self, name, age):
        if not isinstance(name, str):
            raise TypeError('姓名必须是字符串类型')
        if not isinstance(age, int):
            raise TypeError('年龄必须是整型')
        self.name = name
        self.age = age

    def tell_info(self):
        print('姓名:%s,年龄:%s' % (self.name, self.age))


t = Teacher('egon', 18)
t.tell_info()

t.name = 18
t.age = 'egon'
t.tell_info()

'''
姓名:egon,年龄:18
姓名:18,年龄:egon
'''

如果隐藏数据:t.name 无法直接使用.name,使用修改名字就必须通过set_info,就会受到字符类型限制

class Teacher:
    def __init__(self, name, age):

        # self.name = name
        # self.age = age
        self.set_info(name, age)

    def tell_info(self):
        print('姓名:%s,年龄:%s' % (self.__name, self.__age))

    def set_info(self, name, age):
        if not isinstance(name, str):
            raise TypeError('姓名必须是字符串类型')
        if not isinstance(age, int):
            raise TypeError('年龄必须是整型')
        self.__name = name
        self.__age = age


t = Teacher('egon',18)
t.tell_info()

#t.name  无法直接使用.name,使用修改名字就必须通过set_info,就会受到字符类型限制
  1. 隐藏函数/方法属性

目的是隔离复杂度,有一些方法只需要在类内部使用就可,降低了操作者的操作难度,操作者只需要使用一些大的功能,小的功能在设计时就已经完成,且不被操作者看到。

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

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

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)

property是一个装饰器,是用来干什么?

property第一种使用方式

例子:
BMI指数(bmi是计算而来的,但很明显它听起来像是一个属性而非方法,如果我们将其做成一个属性,更便于理解)

成人的BMI数值:
过轻:低于18.5
正常:18.5-23.9
过重:24-27
肥胖:28-32
非常肥胖, 高于32
  体质指数(BMI)=体重(kg)÷身高^2(m)

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

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


obj1 = People('egon', 66, 1.5)
print(obj1.bmi())

将bmi定义成函数的原因:

  1. 从bmi的公式上看,bmi应该是触发功能计算得到的
  2. bmi是随着身高,体重的变化而动态变化的,不是一个固定的值,也就是每次都需要计算而得来

所以在使用的时候需要(),但是bmi听起来更像是一个数据,而非功能
就会造成使用者在使用bmi时,会缺少( ),所以需要减少误解。就引入了property装饰器,这样使用者在使用过程中,就可以不使用( )

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


obj1 = People('egon', 66, 1.5)
print(obj1.bmi)
'''
29.333333333333332
'''

property第二种使用方式

例子:
通过封装的学习之后,会了解到开发者会将某些属性隐藏起来,而限制使用者对数据查看,修改。但是这种限制也有一点的缺点,就是增加了操作者对于数据操作的复杂度,如果操作者像通过对数据进行修改,就一定要调用开发者设置的功能,而每个开发者设置的功能名也许不同,就导致不必要的麻烦。

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

    def get_name(self):
        return self.__name

    def set_info(self, name, age):
        if not isinstance(name, str):
            raise TypeError('姓名必须是字符串类型')
        self.__name = name

    def del_name(self):
        print('不让删除')
        pass


#产生对象
t = People('egon')
#修改对象
t.set_info('haha')
#查看对象
print(t.get_name())

有没有一种统一的方法,使得使用者在操作数据时,无需先理解开发者的功能名,而可以按照以往的逻辑方式进行修改,如下例子:

#查看对象
print(t.name)
#修改对象
t.name = 'haha'
#删除对象
del t.name

使用property装饰器

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

    @property   # name=propert(name)
    def name(self):
        return self.__name

    @name.setter
    def name(self, name):
        if not isinstance(name, str):
            raise TypeError('姓名必须是字符串类型')
        self.__name = name

    @name.deleter
    def name(self):
        print('不让删除')
        pass
	# name = p()

t = People('egon')

t.name = 'haha'
print(t.name)
del t.name

'''
haha
不让删除
'''

本文链接http://metronic.net.cn/metronic/show-22231.html