面向对象之 —— 封装

 

隐藏:

 在python中用双下划线开头的方式将属性/方法隐藏起来(设置成私有的),外部无法通过__x这个名字访问(正常情况下可以通过__dict__获取属性)。注:通常在防止重名(重写)方法时使用_x,但是其仍可以在外部通过这个名字访问,所以此时的x非隐藏。

类中所有双下划线开头的名称如__x都会在类定义时自动变形成:_类名__x的形式,但是这种变形操作只在类定义阶段发生,此后再添加的双下划线开头的名称不会发生变形。父类中定义的私有方法子类无法访问也无法覆盖(名称不再相同,子类的方法一定是子类独有的)

class A:
    def __fa(self): #在定义时就变形为_A__fa
        print('from A')
    def test(self):
        self.__fa() #只会与自己所在的类为准,即调用_A__fa

class B(A):
    def __fa(self):
        print('from B')
b=B()
b.test()
from A

封装

封装指的是:隐藏内部的实现细节,对外提供访问的接口。封装是隐藏,但不是单纯的隐藏

能封装的内容:属性和方法

封装能够区分内外,封装的属性可以直接在内部使用,而不能被外部直接使用,需要为其提供接口(可以在接口附加上对该数据操作的限制,以此控制对数据属性的操作)

封装的目的隔离复杂度(将复杂内容隔离到内部 外部只留下简单接口)

封装的方法

第一步:将内容封装到某处

class Atm:
    def __init__(self,type,size):  #__init__称为构造方法,根据类创建对象时自动执行
self.type=type
self.size=size
def __insert(self): print("step1") def __pwd(self): print("step2") def __get_money(self): print("step3") def withdraw(self): self.__insert() self.__pwd() self.__get_money() print("取款完成") # 根据类Atm创建对象
# 自动执行Atm类的__init__方法 atm1
= Atm("12306",120) # 讲"12306"和120分别封装到atm self的type和size属性中

# 执行Atm类的withdraw方法 atm2=atm1.withdraw() # 调用__insert()、__pwd()、__get_money()方法

 self 是一个形式参数

  • 当执行 atm1 = Atm("12306",120)时,self 等于 atm1 
  • 当执行atm2 = atm.withdraw()时,self 等于atm2

所以,内容其实被封装到了对象 atm1和 atm2 中,每个对象中都有 type和 size 属性

第二步:从某处调用被封装的内容

调用被封装的内容时,有两种情况:

  • 通过对象直接调用
  • 通过self间接调用

1、通过对象直接调用被封装的内容

根据保存格式可以调用被封装的内容:对象.属性名 

class Foo:
 
    def __init__(self, name, age):
        self.name = name
        self.age = age
 
obj1 = Foo('wupeiqi', 18)
print obj1.name    # 直接调用obj1对象的name属性
print obj1.age     # 直接调用obj1对象的age属性
 
obj2 = Foo('alex', 73)
print obj2.name    # 直接调用obj2对象的name属性
print obj2.age     # 直接调用obj2对象的age属性

2、通过self间接调用被封装的内容

执行类中的方法时,需要通过self间接调用被封装的内容

class Foo:
  
    def __init__(self, name, age):
        self.name = name
        self.age = age
  
    def detail(self):
        print self.name
        print self.age
  
obj1 = Foo('wupeiqi', 18)
obj1.detail()  # Python默认会将obj1传给self参数,即:obj1.detail(obj1),所以,此时方法内部的 self = obj1,即:self.name 是 wupeiqi ;self.age 是 18
  
obj2 = Foo('alex', 73)
obj2.detail()  # Python默认会将obj2传给self参数,即:obj1.detail(obj2),所以,此时方法内部的 self = obj2,即:self.name 是 alex ; self.age 是 78

综上所述,对于面向对象的封装来说,其实就是使用构造方法将内容封装到 对象 中,然后通过对象直接或者self间接获取被封装的内容。

 

使用json 和pickle将一个对象序列化保存在文件里并取出,反序列化
import pickle

class Student(object):
'''定义一个Student类'''
    school = "AUST"
    def __init__(self,name,gender,age,grade):
        self.name = name
        self.gender = gender
        self.age = age
        self.grade = grade

    def save(self):
        with open("%s.pkl"%self.name,"wb",) as f:
            pickle.dump(self,f)

    @classmethod
    def get_ob(cls,name):
        with open("%s.pkl"%name,"rb",) as f:
            obj = pickle.load(f)
            return obj

gc = Student("gc","man","20","5")

# 序列化 持久化
# gc.save()

# 反序列化
obj = Student.get_ob("gc")
print(obj.name,obj.gender)

# 如果用json 就不能直接将self 序列化,json不支持这个类型
# 将信息写到字典里,再序列化
# dict1 = {"name":self.name ......}  

#-------------------------------------
import json

class Student(object):
    school = "oldboy"
    def __init__(self,name,gender,age,grade):
        self.name = name
        self.gender = gender
        self.age = age
        self.grade = grade

    def save(self):
        dict1={"name":self.name,"gender":self.gender,"age":self.age,"grade":self.grade}
        with open("%s.json"%self.name,"wt",encoding="utf8") as f:
            json.dump(dict1,f)

    @staticmethod  
    def get_obj(name):
        with open("%s.json" %name, "rt", encoding="utf8") as f:
            dict1 = json.load(f)
            obj = Student(dict1["name"],dict1["gender"],dict1["age"],dict1["grade"])
            return obj
# ----------------------------------
    # @classmethod  # 也可以写成 staticmethod  通过类去调用
    # def get_obj(cls,name):
    #     with open("%s.json" %name, "rt", encoding="utf8") as f:
    #         dict1 = json.load(f)
    #         obj = cls(dict1["name"],dict1["gender"],dict1["age"],dict1["grade"])
    #         return obj


stu1 = Student("tony","man","20","5")
#
# stu1.save()
#
#类方法可以通过类 或者实例去调用
# stu2 = stu1.get_obj("gc")
#
#静态方法不访问类中的任何值 可以使用类 or 对象去调用
stu2 = Student.get_obj("gc")
print(stu2.name)
序列化一个对象

 

prooerty装饰器(一种特殊的属性,访问其时会执行一段功能/函数然后返回值)

  当一些属性的值,不是固定的而是通过计算得来的时候,必须为这个属性增加方法才能完成计算,但是一旦使用方法后,该属性的访问就变成了方法的调用,很明显与其他的属性访问方式不同,这样给使用者造成迷惑,所以需要将这个方法伪装成普通属性,这就用到了Property。   

  property可以将方法伪装成属性,利用这个特点,我们也可以将其使用到封装中。

  之前我们需要为私有的属性提供两个方法, 但是这样一来访问私有属性时的方式就发生了变化,这时候就可以使用property来进行伪装 使得访问私有属性与访问普通属性的方式一致。

  这样将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则

面向对象的封装有三种方式:
public:不封装,对外公开
protected:对外不公开,但对朋友(friend)???或者子类公开
private:对谁都不公开

另外 property还提供了 setter(用于修改属性的值) 和 deleter(删除属性的值)

 

posted @ 2019-05-29 11:37  呔!妖精。。。  阅读(109)  评论(0编辑  收藏  举报