day23-类的封装

1、封装

封装,顾名思义就是将内容封装到某个地方,以后再去调用被封装在某处的内容。
所以,在使用面向对象的封装特性时,需要:
1)将内容封装到某处
2)从某处调用被封装的内容

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

class Foo:
    def __init__(self, name, age):
        self.name = name
        self.age = age

#根据Foo类构造obj1对象,自动执行Foo类的__init__方法
#将Tom和20分别封装到self也就是obj1的name和age属性中
obj1 = Foo('Tom',20)

#根据Foo类构造obj2对象,自动执行Foo类的__init__方法
#将Mike和25分别封装到self也就是obj2的name和age属性中
obj2 = Foo('Mike',25)

self 是一个形式参数,当执行 obj1 = Foo('Tom', 20)时,self等于obj1
当执行 obj2 = Foo('Mike', 25)时,self等于obj2

所以,内容其实被封装到了对象obj1和obj2中,每个对象中都有name和age属性,在内存里类似于下图来保存。

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

调用被封装的内容时,有两种情况:
1)通过对象直接调用
2)通过self间接调用

1、通过对象直接调用被封装的内容
上图展示了对象 obj1 和 obj2 在内存中保存的方式,根据保存格式可以如此调用被封装的内容:对象.属性名

class Foo:
    def __init__(self, name, age):
        self.name = name
        self.age = age
 
obj1 = Foo('Tom', 20)
print(obj1.name)    # 直接调用obj1对象的name属性
print(obj1.age)     # 直接调用obj1对象的age属性
 
obj2 = Foo('Mike', 25)
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('Tom', 20)
obj1.detail()  # Python默认会将obj1传给self参数,即:obj1.detail(obj1),所以,此时方法内部的self = obj1,即:self.name是Tom,self.age是20
  
obj2 = Foo('Mike', 25
obj2.detail()  # Python默认会将obj2传给self参数,即:obj1.detail(obj2),所以,此时方法内部的self = obj2,即:self.name是Mike, self.age是25

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

2、封装与扩展性

封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。

#类的设计者
class Room:
    def __init__(self,name,owner,width,length,high):
        self.name=name
        self.owner=owner
        self.__width=width
        self.__length=length
        self.__high=high
    def tell_area(self): #对外提供的接口,隐藏了内部的实现细节,此时我们想求的是面积
        return self.__width * self.__length

#使用者
>>> r1=Room('卧室','egon',20,20,20)
>>> r1.tell_area() #使用者调用接口tell_area

#类的设计者,轻松的扩展了功能,而类的使用者完全不需要改变自己的代码
class Room:
    def __init__(self,name,owner,width,length,high):
        self.name=name
        self.owner=owner
        self.__width=width
        self.__length=length
        self.__high=high
    def tell_area(self): #对外提供的接口,隐藏内部实现,此时我们想求的是体积,内部逻辑变了,只需求修该下列一行就可以很简答的实现,而且外部调用感知不到,仍然使用该方法,但是功能已经变了
        return self.__width * self.__length * self.__high


#对于仍然在使用tell_area接口的人来说,根本无需改动自己的代码,就可以用上新功能
>>> r1.tell_area()

例子1:

class Parent:
    def func(self):
        print('in Parent func')

    def __init__(self):
        self.func()

class Son(Parent):
    def func(self):
        print('in Son func')

son1 = Son()
结果是打印了in Son func

在实例化Son类时的流程:
1、在内存中创建了一个空间存储实例化对象son1
2、将实例化对象son1的内存指针指向Son类
3、自动执行Son类中的__init__方法,并将实例化对象son1传给self
4、由于Son类中没有__init__方法,所以就从父类Parent中寻找__init__方法并执行
5、执行父类中的__init__方法,调用了self.func()函数,而此时self是实例化对象son1,所以执行的是son1的func方法
6、son1的func方法打印in Son func

例子2:

class A:
    a = 0
    b = 1
    def __init__(self):
        c = 222
d = A()
d.a = 1
d.b = 2
d.c = {'1':1}
e = A()
print(e.a)
print(e.b)
print(e.c)
结果:0 1 报错'A' object has no attribute 'c'

1、创建A类时,在A类中定义了静态属性a和b,创建了特殊方法__init__,c=222并不是创建了静态属性,也没有返回c,所以c在内存中并不存在
2、实例化对象d=A(),在d的空间内创建了静态属性a,b,c,但并不会修改A类中的属性
3、实例化对象e=A(),e可以调用A类中的静态属性a和b,但是A类中并不存在静态属性c,所以e.c会报错

猜你喜欢

转载自www.cnblogs.com/dxnui119/p/9973787.html
今日推荐