7.14. 面对对象之封装特性

7.14.1. 为属性添加限制,访问限制(隐藏和封装)

  • 隐藏类的实现细节

  • 让使用者只能通过事先预定的方法来访问数据,可以在方法里面加入控制逻辑,限制对属性的不合理访问

  • 可以进行数据检查,有利于保证数据的完整性

  • 便于修改,提高代码的可维护性

良好的封装,有两个方面: * 将对象的属性和实现细节隐藏起来,不允许外部直接访问 * 把方法暴露出来,让方法控制对这些属性进行安全的访问和操作

该暴露的暴露出来,该隐藏的隐藏起来

eg

#!/usr/bin/env python
#-*- coding:utf8 -*-
# auther; 18793
# Date:2019/5/10 16:59
# filename: 封装.py
class User:
    def __hide(self):
        return ("示范隐藏的hide方法")

    def getname(self):
        return self.__name

    def setname(self,name):
        if len(name) <3 or len(name) > 8:
            raise ValueError("用户名长度必须在3~8之间")
        self.__name = name

    name = property(getname,setname)



    def setage(self,age):
        if age < 18 or age > 70:
            raise ValueError("用户年龄必须在18~80之间")
        self.__age = age

    def getage(self):
        return self.__age

    age = property(getage, setage)



if __name__ == '__main__':
    u = User()
    u.name = "hujianli"
    u.age = 19
    print(u.name)
    print(u.age)
    print(dir(u))
    print(u._User__hide())

eg

# !/usr/bin/env python
# -*- coding:utf8 -*-
# auther; 18793
# Date:2019/5/18 7:38
# filename: 封装性,私有方法和私有属性.py
class Animal(object):
    def __init__(self, age, sex=1, weight=0.0):
        self.age = age
        self.sex = sex
        self.__weight = weight

    def eat(self):
        self.__weight += 0.5
        self.__run()
        print("eat.....")

    def __run(self):
        """
        私有方法
        :return:
        """
        self.__weight -= 0.01
        print("run.....")


a1 = Animal(18, 0, 10.0)
# print(dir(a1))
a1.eat()
# a1.run()            #会报错,私有方法不能直接调用
# a1._Animal__run()
print(a1._Animal__weight)

输出结果:

run.....
eat.....
10.49

私有属性设置说明

_foo        #protected (保护类型)的成员,只允许类本身和子类可以访问,
单前置下划线,私有化属性或方法,禁止通过from modules import *导入,但是类对象和子类可以访问

__foo       #使用类型的成员,只允许定义它类本身可以访问,不可以通过实例名称来访问,
需要使用dir()查看后访问。通过“实例名.类名__xxxx”方式访问。

__foo__     #系统定义的名字,特殊的方法

所有双下划线“__”开始命名的成员都为私有成员。

类的成员与下划线总结:

_name、_name_、_name__:建议性的私有成员,不要在外部访问。
__name、 __name_ :强制的私有成员,但是你依然可以蛮横地在外部危险访问。
__name__:特殊成员,与私有性质无关,例如__doc__。
name_、name__:没有任何特殊性,普通的标识符,但最好不要这么起名。

代码示例 1

class Swan:
    '''
    天鹅类
    '''
    _neck_swan = '天鹅的脖子很长'      #受保护类型的属性
    __neck_swan2 = '天鹅的脖子很长'      #私有类型的属性


    def __init__(self):
        print('这是构造方法中受到保护类型的属性', Swan.__neck_swan2)  #访问保护类型的属性

    def my(self):
        print("my方法: ", Swan.__neck_swan2)

swan = Swan()   #创建Swan类的实例(对象)
print('直接访问:', swan._neck_swan) #通过实例类型来访问受保护类型的属性

print()
swan1 = Swan()
print('私有类型的属性: ', swan1._Swan__neck_swan2)

swan1._Swan__neck_swan2 = "我修改了私有属性,天鹅的脖子很很很长"
print('修改私有属性后:',swan1._Swan__neck_swan2)

print()
swan1.my()      #修改的私有属性,在方法中不会生效

代码示例 2

#!/usr/bin/env python
#-*- coding:utf8 -*-
'''
要让内部的属性不被外部直接访问
'''
class Person(object):
    def run(self):
        print("run")

    def eat(self,food):
        print("eat " + food)

    def __init__(self,name,age,height,weight,money,input_hu):
        self.name = name
        self.age = age
        self.height = height
        self.weight = weight
        self.__money = money  #此属性已经变为'_Person__money'
        self.__input__ = input_hu

    #通过内部的方法,去修改私有属性
    #通过自定义的方法实现对私有属性的赋值与取值
    def set_Money(self,money):
        #数据的过滤
        if money < 0:
            money = 0
        else:
            self.__money = money

    def get_Money(self):
        return self.__money

    def __del__(self):
        print("这里是析构函数")


per = Person("hujianli", 24, 180, 65,1000,"python")
print(dir(per))
print(dir(Person))
per.age = 18

#一般帅的人不这么使用,很不方便
per._Person__money = 22
print(per._Person__money)  #__money此时已经变成_Person__money了

#如果让内部属性不被外部直接访问,在属性前加__下划线
#如果在属性前加__下划线,那么这个属性就变成了私有属性,不能再实例化之后直接访问
# per.__money = 10


print(per.age)

print(per.get_Money())
print("开始赋值set_money" + " ====ing")
per.set_Money(100)
print("赋值之后的money是: {}".format(per.get_Money()))

#在python中__xxx___ 属于特殊变量,特殊变量的值可以直接访问
print("这是一个__xx__的特殊变量: %s" % per.__input__)

#一个下划线_xxx的变量,看到这样的变量时,表示当成私有属性,虽然可以直接在外部访问
#这是一个约定束城




class Student(object):
    def __init__(self,name,score):
        self.__name = name
        self.__score = score

    def info(self):
        print("学生:{}; 分数{}".format(self.__name,self.__score))

    def set_score(self,secore):
        self.__score = secore

    def get_score(self):
        return self.__score

if __name__ == '__main__':
    hu = Student("hujianli","100")
    hu.info()
    print("修改前的分数{}".format(hu.get_score()))
    hu.set_score(90)
    print("修改后的分数{}".format(hu.get_score()))

私有属性的保护和设置

#!/usr/bin/env python
#-*- coding:utf8 -*-
class Duck():
    def __init__(self, input_name): #构造函数
        self.__name = input_name

    @property
    def name(self):
        print("inside the getter")
        return self.__name

    @name.setter
    def set_name(self,put_name):
        print("开始设置属性:name的值")
        self.__name = put_name



if __name__ == '__main__':
    name = "hujianli"
    hu = Duck(name)
    print(hu.name)
    print("分割线".center(100, "-"))
    hu.set_name = "xiaojian"
    print(hu.name)
    print()
    print(hu._Duck__name)

代码示例2

#!/usr/bin/env python
#-*- coding:utf8 -*-

#通过装饰器方法来获取私有属性
class TVShow:       #电视节目类
    list_film = ["战狼2", "红海行动", "西游记女儿国", "熊出没变形记"]

    def __init__(self, show):
        self.__show = show

    @property
    def show(self):
        '''
        定义方法
        :return:私有属性
        '''
        return self.__show      #返回类的实例

    @show.setter                #让属性可以进行修改
    def show(self, value):
        if value in TVShow.list_film:       #判断值是否在列表中
            self.__show = '您选择了《'+ value + "》,稍后将播放"    #修改返回值
        else:
            self.__show = "您点播的电影不存在"


tvshow = TVShow("战狼2")  #创建类的实例
print("正在播放:《", tvshow.show, "》")     #获取属性值
print("您可以从", TVShow.list_film, "中选择台点播的电影")

tvshow.show = '红海行动'
print(tvshow.show)      #获取属性值


'''
tvshow = TVShow("正在播放《战狼2》")  #创建类的实例
print("默认输出: ", tvshow.show)     #获取属性值

'''


#修改装饰器的值会报错
'''
tvshow.show = "正在播放《红海行动》"
print("默认输出: ", tvshow.show)     #获取属性值
'''