类的封装

从封装本身的意思去理解,封装就好像是拿来一个麻袋,把小猫,小狗,小王八,还有egon一起装进麻袋,然后把麻袋封上口子。照这种逻辑看,封装=‘隐藏’,这种理解是相当片面的

一、为什么要封装

封装数据的主要原因是:保护隐私(作为男人的你,脸上就写着:我喜欢男人,你害怕么?)

封装方法的主要原因是:隔离复杂度(快门就是傻瓜相机为傻瓜们提供的方法,该方法将内部复杂的照相功能都隐藏起来了,比如你不必知道你自己的尿是怎么流出来的,你直接掏出自己的接口就能用尿这个功能)

提示:在编程语言里,对外提供的接口(接口可理解为了一个入口),就是函数,称为接口函数,这与接口的概念还不一样,接口代表一组接口函数的集合体。

二、如何使用代码实现隐藏

隐藏属性:通过 __变量名来隐藏

隐藏方法:通过 __方法名来隐藏

2.1 隐藏属性

隐藏属性是为了安全

class Person:
    def __init__(self,name,age):
        self.__name=name
        self.__age=age
    def get_name(self):
        # print(self.__name)
        return '[----%s-----]'%self.__name
    
p=Person('xc',18)
# print(p.age)	# 错误的使用方法
# print(p.__name)	# 错误的使用方法
print(p.get_name())

实际上 __变量名 是通过变形隐藏了属性

2.1.1 隐藏的属性访问不到?

实际上由方法可以访问到。但是大家不要去这样用。

之前说了是通过 变形 隐藏了属性

# 通过__dict查看对象私有属性
print(p.__dict__)

# 通过变形后的属性,一样可以拿到变量内容
print(p._Person__age) # 不要这样用

[----xc-----]

2.2 隐藏方法

隐藏方法,隔离复杂度

class Person:
    def __init__(self,name,age):
        self.__name=name
        self.__age=age
    def __speak(self):
        print('6666')
        
p=Person('xc',18)
p.__speak()	# 错误的使用方法

# 查看类中的私有属性
print(Person.__dict__)

{'module': 'main', 'init': <function Person.init at 0x000001E3C2CA29D8>, '_Person__speak': <function Person.__speak at 0x000001E3C2CA2A60>, 'dict': <attribute 'dict' of 'Person' objects>, 'weakref': <attribute 'weakref' of 'Person' objects>, 'doc': None}
6666

2.3 什么情况的属性变形

只要在类内部,以 __变量名 命名的变量,都会被隐藏,会发生变形。

但,如果在外部放入__变量名 属性是不隐藏的

class Person:
    def __init__(self,name,age):
        self.__name=name
        self.__age=age
    def set_xx(self,xx):
        self.__xx=xx

p=Person('xc',18)
p._p_aa="aaa"	# 没有被隐藏

p.set_xx('6688')	# 被隐藏
print(p.__dict__)

{'_Person__name': 'nick', '_Person__age': 18, '_p_aa': 'aaa', '_Person__xx': '6688'}

三、property装饰器

3.1 property装饰器有什么用

它可以把方法包装成数据属性

class Person:
    def __init__(self,name,height,weight):
        self.name=name
        self.height=height
        self.weight=weight
    @property	# 使用语法糖的方式 通过property装饰器进行装饰
    def bmi(self):
        return self.weight/(self.height**2)
    
p=Person('xc',1.82,75)
print(p.bmi)	# 使用查看对象属性的方式  查看方法的返回值
# print(p.bmi())	# 错误的使用方法
# p.bmi = 123   # 只能查看,不能进行修改

22.6421929718633

3.2 property之setter和deleter

使用property装饰器将方法包装成数据属性后,是无法进行修改的

  • 只要通过property装饰器中的方法 .setter,这样就可以修改了
class Person:
    def __init__(self,name,height,weight):
        self.__name=name
        self.__height=height
        self.__weight=weight
    @property
    def name(self):
        return '[我的名字是:%s]'%self.__name

    #用property装饰的方法名.setter,这样就可以修改了
    @name.setter
    def name(self,new_name):
        # if not isinstance(new_name,str):
        if type(new_name) is not str:
            raise Exception('改不了')
        if new_name.startswith('sb'):
            raise Exception('不能以sb开头')
        self.__name=new_name
        
p=Person('xc',1.82,70)
# 按照属性进行调用
print(p.name)	# 调用property装饰器后的方法 name,变为一个属性
# 按照属性进行调用,并修改
p.name='pppp'	# 调用property.setter装饰器后的方法,可以进行修改

# 改不了,直接抛异常
# p.name=999
# p.name='sb_xxx'
  • 只要通过property装饰器中的方法 .deleter,就可以删除了

一般没有这个需求。

class Person:
    def __init__(self, name, height, weight):
        self.__name = name
        self.__height = height
        self.__weight = weight

    @property
    def name(self):
        return '[我的名字是:%s]' % self.__name

    # 用property装饰的方法名.setter,这样就可以修改了
    @name.setter
    def name(self, new_name):
        # if not isinstance(new_name,str):
        if type(new_name) is not str:
            raise Exception('改不了')
        if new_name.startswith('sb'):
            raise Exception('不能以sb开头')
        self.__name = new_name


p = Person('xc', 1.82, 70)
# 按照属性进行调用
print(p.name)  # 调用property装饰器后的方法 name,变为一个属性
# 按照属性进行调用,并修改
p.name = 'pppp'  # 调用property.setter装饰器后的方法,可以进行修改
# 改不了,直接抛一异常
# p.name=999
# p.name='sb_xxx'

# 删除name,会调用property.deleter装饰的方法
del p.name
posted @ 2019-08-28 18:34  正在学习的Barry  阅读(237)  评论(0编辑  收藏  举报
-->