• 多态与封装


    多态

    什么是多态?

    一种事物/一个类的多种形态。

    例如:动物有多种形态:人、猪、狗;文件有多种形态:执行文件、文本文件;水有多种形态:液态水、固态水、水蒸气

    import adc
    class File(metaclass=abc.ABCMeta):
        @abc.abstractmethod
        def click(self):
            pass
        
    class Text(File):
        def click(self):
            print('open file')
            
        
    class ExeFile(File):
        def click(self):
            print('execute file')
    

    多态性:多个不同类对象响应同一个方法,返回不同的结果

    多态性的好处?

    1、增加了程序的灵活性:以不变应万变,不论对象千变万化,使用者都是同一种方式去调用。如:fun(animal)

    2、增加了程序的可扩展性:通过继承Animal类创建一个新类,使用者无需更改代码,还是用fun(animal)调用

    '''多态性:一种调用方式,不同的执行效果(多态性)
        def func(obj):
            obj.run()
        func(peo1)
        func(pig1)
        func(d1)
        
        # 多态性依赖于:继承
    	# 多态性:定义统一的接口
    '''
    
    class Cat(Animal):  # 属于动物的另外一种形态:猫
        def talk(self):
            print('say miao')
    
    def func(animal):  # 对于使用者来说,自己的代码根本无需改动
        animal.talk()
    
    cat1 = Cat()  # 实例出一只猫
    func(cat1)  # 甚至连调用方式也无需改变,就能调用猫的talk功能
    

    多态小结

    多态:同一种事物的多种形态,动物分为人类,猪类(在定义角度)
    多态性:一种调用方式,不同的执行效果(多态性)

    封装

    什么是封装?

    隐藏对象的属性和实现细节,仅对外提供公共访问方式

    先装后封:

    ​ 装:把数据和方法装到类里面。

    ​ 封:把类里面的数据和方法封起来,不让人访问。

    封装原则:将不需要对外提供的内容都隐藏起来。把属性都隐藏,提供公共方法对其访问。

    为什么要封装:

    1、保护隐私,比如:你买了个充气娃娃,你能让人知道吗?这时候就把你买的娃娃封装起来,然后自己偷偷用。

    2、隔离复杂度,比如:你有尿尿这个功能(方法),你不需要知道你尿是经历了多少化学反应才流出来的,你只需要掏出你的接口直接用尿这个功能就行,这不就隔离了复杂度嘛。

    封装的两个层面:

    无论那种层面的封装,都要对外界提供接口,访问你内部隐藏的内容。使用者都只能通过这个接口访问。

    1、创建类和对象会分别创建二者的名称空间,我们只能使用类名.或者对象.的方式访问名称空间里面的名字,这本身就是一种封装。

    2、把某些属性隐藏起来(或者说定义成私有的),只在类内部使用,外部无法访问。或者留下少量接口,供外部访问。

    重要:在python中,使用双下划线 __的方式实现隐藏属性(设置成私有)
    
    重要:类中所有双下划线开头的名称如 __X 会自动变形 _类名__X 的形式
    

    私有属性:用双下划线开头的属性

    class A:
        __N = 0   #变形为_A__N
        def __init__(self):
            self.__X = 10   #变形成self._A__X
        
        def __foo(self):   #变形成:_A__foo
            pass
        
        def bar(self):
            self.__foo()   #只有在类内部可以通过__foo访问。
            
    #注意:A.__N访问不到,A._A__N是可以访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形  
    
    print(A.__dict__) # {'__module__': '__main__', '_A__N': 0, '__init__': <function A.__init__ at 0x1005704d0>, '_A__foo': <function A.__foo at 0x100570830>, 'bar': <function A.bar at 0x1004c2b00>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
    
    print(A._A__N)  # 0 
    a = A()
    print(a._A__N)  # 0 
    print(a.__N) # AttributeError: 'A' object has no attribute '__N'
    
    
    自动变形的特点:
    
    ​	1、类中定义的 __x 只能在内部使用,如 self.__x, 引用的就是变形的结果
    ​	2、这种变形是针对外部的变形,在外部无法通过 self.__x 这个名字访问到。
    ​   3、在类中定义的 __x 不会覆盖父类定义的 __x, 因为子类中的 __x 变形成了 _子类__x, 父类中的 __x 则变形成了 _父类__x 
    所以子类定义的私有属性是无法覆盖父类的。因为变形成了不同的属性
    

    在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的

    class A:
    	def __fa(self): #在定义时,变形为_A__fa
    		pritn('from A')
    	def test(self):
    		self.__fa()   #类内部调用自己的私有方法,实际上为self._A__fa()
            
    class B(A):
        def __fa(self):
            print('from B')
            
    b = B()
    b.test()  # from A
    
    

    封装在于明确区分内外,让类使用者可以修改封装内的东西而不影响外部调用。

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

    property属性

    什么是property属性?

    property属性是一种特殊的属性。使用@property装饰器可以将一个函数当做数据属性来使用。

    class A:
        def __init__(self, w, h):
            self.__w = w
            self.__h = h
    
        @property
        def bmi(self):
            return self.__w / self.__h ** 2
    
    a = A(67, 1.76)
    print(a.bmi) # 21.62964876033058
    
    import math
    class Circle():
        def __init__(self, radius):
            self.__radius = radius
    
        @property
        def area(self):
            return math.pi * self.__radius ** 2
    
        @property
        def perimeter(self):
            return 2 * math.pi * self.__radius
    
    
    c = Circle(10)
    print(c.area)
    print(c.perimeter)
    
    #注意:此时的特性area和perimeter不能被赋值
    c.area=3 #为特性area赋值
    '''
    抛出异常:
    AttributeError: can't set attribute
    '''
    

    为什么要用property?

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

    property属性是无法修改的。python中提供了 @定义成特性的函数.setter 装饰器来修改,提供了 @定义成特性的函数.deleter 装饰器来防止删除。

    class Foo:
        def __init__(self, name):
            self.__name = name
    
        @property
        def name(self):
            return self.__name
    
        @name.setter
        def name(self, value):
            if not isinstance(value, str):
                raise TypeError('输入的名字必须是字符串类型的')
            self.__name = value
    
        @name.deleter
        def name(self):
            raise TypeError('特性name不能被删除')
    
    
    f = Foo('大帅比')
    print(f.name)
    
    f.name = '我是你爹'
    print(f.name)
    
    f.name = 10  #TypeError: 输入的名字必须是字符串类型的
    
    del f.name  # TypeError: 特性name不能被删除
    
    '''
    	注意:只有在函数被 @property 装饰器修饰成一个特性以后,才能使用 函数名。setter, 函数名.dellter
    '''
    

    @classmethod

    类的绑定方法,当类里面的函数不需要用到对象的属性时可以使用类绑定方法

    class Classmethod_Demo():
        role = 'dog'
    
        @classmethod
        def func(cls):
            print(cls.role)
    
    Classmethod_Demo.func()
    

    @staticmethod

    静态方法,当类里面的函数不需要用到类属性、对象属性时,可以使用静态方法

    class Staticmethod_Demo():
        role = 'dog'
    
        @staticmethod
        def func():
            print("当普通方法用")
    
    Staticmethod_Demo.func()
    
  • 相关阅读:
    xshel链接linuxl安装nginx
    nginx学习笔记
    sweiper做一个tab切换
    bootstrap中tab切换的使用
    pc页面自动缩放到手机端
    日程表
    页面嵌套iframe时,怎样让iframe高度根据自身内容高度自适应
    mysql5.7版本以上下载安装
    电脑快捷键操作汇总
    关于.eslintrc.js代码检测的一些配置
  • 原文地址:https://www.cnblogs.com/KbMan/p/11265104.html
Copyright © 2020-2023  润新知