• DAY19 面向对象三大特性之多态、封装


    面向对象三大特性之:多态

      多态:指的是一类事物有多种形态。

      例如:动物具有多种形态。

    import abc
    class Animal(metaclass=abc.ABCMeta):  #同一类事物:动物
        @abc.abstractmethod
        def talk(self):
            pass
    
    class People(Animal):   #动物的形态之一:人
        def talk(self):
            print('say hello')
    
    class Dog(Animal):      #动物的形态之一:狗
        def talk(self):
            print('barking')
    
    class Cat(Animal):    #动物的形态之一:猫
        def talk(self):
            print('miao')
    

    多态性

      什么是多态动态绑定?(在继承的背景下使用,也称为多态性。)

      在面向对象方法中一般这样表述多态性:

      -----> 向不同的对象发送同一条消息,不同的对象在接受时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数。不同的行为就是指不同的实现,即执行不同的函数。

      -----> 换句人话说,就是在子类和父类中有同名的方法,接受相同的方法调用,都执行自己的同名方法,表现出不一样的行为。

    import abc
    class Animal(metaclass=abc.ABCMeta):  #同一类事物:动物
        @abc.abstractmethod
        def talk(self):
            pass
    
    class People(Animal):   #动物的形态之一:人
        def talk(self):
            print('say hello')
    
    class Dog(Animal):      #动物的形态之一:狗
        def talk(self):
            print('barking')
    
    class Cat(Animal):    #动物的形态之一:猫
        def talk(self):
            print('miao')
    
    p = People()
    d = Dog()
    c = Cat()
    
    p.talk()
    d.talk()
    c.talk()
    >>>
    say hello
    barking
    miao

    鸭子类型(duck Typing)

      在程序设计中,鸭子类型是一种动态类型的一种风格。python崇尚鸭子类型,即“如果看起来像,叫声像而且走路像鸭子的,那么它就是鸭子。”

      例子一:在java语言中,定义一个函数,传递参数的类型必须得到控制,需要定义一个类来定义这些参数的数据类型,参数必须都继承这个类。换成python类型,例如:内置函数len()中的参数,可以是字符串,容器类型,iterator类型。因为这些数据类型的类中都有一个__len__()方法,并没有像java一样定义一个类用来继承,而是通过模糊的概念,并不是通过明确的继承来实现多态,认为拥有__len__()方法的就可以当做参数,符合鸭子类型。

    面向对象三大特性之:封装

       封装:隐藏对象的属性以及实现方法,仅对外提供公共的访问方法。

      封装的优点:

        (1)将变化隔离

        (2)便于使用

        (3)提高安全性

      封装的原则:

        (1)将不需要对外提供的内容隐藏起来。

        (2)把属性都隐藏起来,提供一个公共访问的接口来对其进行访问。

    广义的封装和狭义的封装

      (1)广义的封装:是为了只有这个类的对象才能够使用定义在类中的方法。

    class 类名:
        def func1(self):pass
        def func2(self):pass
        def func3(self):pass
    

      (2)狭义的封装:把一个名字藏在类中

      ##### 私有静态变量/私有对象属性/私有方法 #####

      在python中用双下划线__开头的方式将属性隐藏起来(设置成私有的)

      为什么要定义一个私有变量?

      (1)不想让你看到这个值。

      (2)不想让你修改这个值。

      (3)想让你在修改这个值得时候有一些限制。

      (4)有些属性或者方法不希望被子类继承。

      ##### 私有静态变量/私有对象属性/私有方法 #####

    私有变量

      一般来说类中的属性就应该是共享的,但是语法上可以把类的属性设置成私有的。

    #其实类中的__私有属性,都只是一个变形,变形为“_类名__属性”
    
    #一。设置私有属性
    class A:
        __N = 0 #类的数据属性就应该是共享的,但是语法上是可以把类的属性设置成私有的。
    
    print(A._N)  #在类的外部就不能引用私有的静态变量
    >>>
    AttributeError: type object 'A' has no attribute '_N'
    
    
    #二。但是谨记,都只是变形。
    print(A.__dict__)
    >>>
    {'__module__': '__main__', '_A__N': 0, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
    
    #通过__dict__方法,可以巧妙地打破类的外部不能访问私有属性,但是这种方法被约定俗成的禁止了。
    print(A.__dict__['_A__N'])
    >>>
    0
    
    #其实这仅仅这是一种变形操作
    #类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式:
    
    class A:
        __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
        def __init__(self):
            self.__X=10 #变形为self._A__X
        def __foo(self): #变形为_A__foo
            print('from A')
        def bar(self):
            self.__foo() #只有在类内部才可以通过__foo的形式访问到.
    
    

      总结:

      (1)类中定义的私有变量只能在类的内部使用,如:self.__x,其实已经变形为_类名__x

      (2)这种变形其实是针对外部的变形,令外部无法通过self.__x的方式访问到私有方法。

      (3)在子类中定义__x是无法覆盖父类中的__x属性的,因为在子类中定义就会变形为"_子类__x",在父类中"_父类__x"。

    私有方法

      在继承中,父类如果不想让子类覆盖自己的方法,可以把方法设置成私有方法。

    #正常情况
    >>> class A:
    ...     def fa(self):
    ...         print('from A')
    ...     def test(self):
    ...         self.fa()           #看self代表谁
    ... 
    >>> class B(A):
    ...     def fa(self):
    ...         print('from B')
    ... 
    >>> b=B()
    >>> b.test()
    from B
     
    
    #把fa定义成私有的,即__fa
    >>> 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

    内置函数补充之:property

      property是一个装饰器,它的作用是将一个方法伪装成一个属性。

    class Student:
        def __init__(self,name,age):
            self.__name = name
            self.age = age
    
        @property     #将一个方法伪装成一个属性
        def name(self):
            return self.__name
    
    zhuge = Student('诸葛',20)
    print(zhuge.name)
    >>>
    诸葛
    

      为什么要用property?

       将一个类的函数定义成一个属性后,对象在去使用obj.name的时候,根本不会想到这个是经过函数运算后返回的,遵循了统一访问的原则。

    ps:面向对象中封装的三大方式:

     【public】:这种就是对外公开,不封装。

     【protected】:这种封装方式就是对外不公开,但是对子类公开。

      【private】:这种封装方式就对谁的不公开

    在python中并没有在语法上把它们三种都内建到class机制中,在c++里面一般会把所有的数据都设置成私有的,然后提供set和get方法去修改和获取,在python中可以通过property去实现。

    class Foo:
        def __init__(self,val):
            self.__NAME= val    #将所有数据属性都转化为私有属性存起来。
    
        @property
        def name(self):
            return self.__NAME
    
        @name.setter
        def name(self,value):
            if not isinstance(value,str):
                raise TypeError('%s must be str'%value)
            self.__NAME = value  # 通过类型检测后,将值value存放到真实的位置self.__NAME
    
        @name.deleter
        def name(self):
            raise TypeError('can not delete')
    
    f = Foo('egon')
    # print(f.name)   #通过name函数返回self.__NAME
    
    # f.name = 10      #TypeError: 10 must be str
    del f.name         #TypeError: can not delete
    

      property总结:

     (1)一个静态属性property本质就是实现了get,set,delete三种方法。

     (2)只有在属性xxx定义了property后,才能使用xxx.setter,xxx.deleter。

  • 相关阅读:
    Algorithm --> KMP算法
    Algorithm --> 快速排序
    Algorithm --> 阶乘和因子
    Algorithm --> Dijkstra和Floyd最短路径算法
    Algorithm --> 二分图最大匹配
    Algorithm --> 邮票连续组合问题
    Algorithm --> 字母重排
    Algorithm --> 6174问题
    Algorithm --> 字符串中最长不重合子串长度
    Algorithm --> DFS和BFS
  • 原文地址:https://www.cnblogs.com/hebbhao/p/9555340.html
Copyright © 2020-2023  润新知