python开发基础----类(多态与封装)

什么是多态:由不同的类实例化得到的对象,调用同一个方法,执行的逻辑不同

多态的概念指出了对象如何通过他们共同的属性和动作来操作及访问,而不需要考虑他们具体的类。

多态表明了动态(又名:运行时)绑定的存在,允许重载及运行时类型确定和验证

比如:

水是一个类

不同温度,水被实例化成不同的状态,冰、水蒸汽、雾气(然而很多人就理解到这一步就认为此乃多态,这是不对的,多态是运行时绑定的存在)

(多态体现在由同一个类实例化出的多个对象,这些对象执行相同的方法时,执行的过程和结果是不一样的)

冰、水蒸汽、雾有一个共同的方法就是变成云,但是冰变云()与水蒸气变云是截然不同的两个过程,虽然调用的方法是一样的

class H2O:
    def __init__(self,name,temperature):
        self.name = name
        self.temperature = temperature

    def turn_ice(self):
        if self.temperature < 0 :
            print('%s 温度太低结冰了' % self.name)
        elif self.temperature > 0 and self.temperature < 100:
            print('%s 液化成水了' % self.name)
        elif self.temperature > 100:
            print('%s 温度太高变成水蒸气' % self.name)

class Water(H2O):
    pass
class Ice(H2O):
    pass
class Steam(H2O):
    pass

w1 = Water('',25)
i1 = Ice('',-20)
s1 = Steam('蒸汽',3000)

def func(obj):
    obj.turn_ice()

func(w1)
func(i1)
func(s1)

结果:
水 液化成水了
冰 温度太低结冰了
蒸汽 温度太高变成水蒸气

多态实际上是依附于继承的两种含义的:“改变”和“扩展”本身就意味着必须有机制去自动选用你改变/扩展过的版本,故无多态,则两种含义就不可能实现

多态就是类的这两层意义的一个具体的实现机制,即,调用不同的类实例化得到对象下的相同的方法,实现的过程不一样

所以,多态实质上是继承的实现细节,那么让多态与封装、继承两个概念并列,其实是有点不符合逻辑的

 

封装


第一个层面的封装:类就是麻袋,这本身就是一种封装

第二个层面封装:类中定义私有的,只在类的内部使用,外部无法使用

python不依赖语言特性去封装数据,而是通过遵循一定的数据属性和函数属性的命令约定来达到封的效果

约定一:任何以单下划线开头的名字都应该是内部的,私有的

  python并不会真的阻止你访问私有的属性。模块也遵循这种约定,如果模块名以单下划线开头,那么from module import * 时不能被导入,但是from module import _private_module依然可以导入成功

  其实很多时候你去调用一个模块的功能时会遇到单下划线开头的(socket._socket,sys._home等)这些都是私有的,原则上是供内部调用的,作为外部的你,一意孤行也是可以调用,只是显得稍微优点傻逼

约定二: 双下划线开头的名字

  双下划线开头的属性(python解释器会自动给双下划线属性重命名,实现伪私有,重命名规则是    _类名__变量名 )

第三个层面上的封装:明确区分内外,内部的实现逻辑,外部无法知晓,并且为封装到内部的逻辑提供一个访问接口给外部使用(这才是真正的封装)

 

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

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

#类的设计者
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()

 

 

总结: 

  上面提到两种不同的编码约定(单下划线和双下划线)来命名私有属性,一般而言,应该让非公共名称以单下划线开头,如果代码会涉及到子类,并且有些内部属性应该在子类中隐藏起来,那么才考虑使用双下划线方案。但是无论那种方案,python都没有从根本上限制访问

 

posted @ 2019-09-19 09:44  Mr-谢  阅读(163)  评论(0编辑  收藏  举报