• 面向对象之封装与多态


    一、多态

    1.多态

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

    水有多种形态:冰 水雾 水

    动物有多种形态:人,狗,猪

    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('say wangwang')
    
    class Pig(Animal): #动物的形态之三:猪
        def talk(self):
            print('say aoao')
    
    多态
    多态

    文件有多种形态:文本文件,可执行文件

    import abc
    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')
    
    文件
    文件

    2.多态性

    多态性是指在不考虑实例类型的情况下使用实例

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

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

    比如:老师.下课铃响了(),学生.下课铃响了(),老师执行的是下班操作,学生执行的是放学操作,虽然二者消息一样,但是执行的效果不同

    peo=People()
    dog=Dog()
    pig=Pig()
    
    #peo、dog、pig都是动物,只要是动物肯定有talk方法
    #于是我们可以不用考虑它们三者的具体是什么类型,而直接使用
    peo.talk()
    dog.talk()
    pig.talk()
    
    #更进一步,我们可以定义一个统一的接口来使用
    def func(obj):
        obj.talk()
    
    多态性
    多态性

    3、鸭子类型

    Python崇尚鸭子类型,即‘如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子’python程序员通常根据这种行为来编写程序。例如,如果想编写现有对象的自定义版本,可以继承该对象也可以创建一个外观和行为像,但与它无任何关系的全新对象,后者通常用于保存程序组件的松耦合度。

    例1:利用标准库中定义的各种‘与文件类似’的对象,尽管这些对象的工作方式像文件,但他们没有继承内置文件对象的方法

    二、封装

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

    好处:

    1、 将变化隔离; 

    2.、便于使用;

    3.、提高复用性; 

    4.、提高安全性;

    封装原则:

    1、 将不需要对外提供的内容都隐藏起来;

    2、 把属性都隐藏,提供公共方法对其访问。

    1、私有变量

    #其实这仅仅这是一种变形操作
    #类中所有双下划线开头的名称如__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的形式访问到.
    
    #A._A__N是可以访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形
    
    私有变量
    私有变量

    特点:

    1、类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。

    2、这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。

    3、在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。

    注意:

    这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N

    2、私有方法

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

    #正常情况
    class A:
        def fa(self):
             print('from A')
        def test(self):
            self.fa()
     
    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): #在定义时就变形为_B__fa
             print('from B')
     
    b=B()
    b.test()
    #from A
    
    私有方法
    私有方法

    3、扩展性

    封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。

    4、property属性

    '''
    例一:BMI指数(bmi是计算而来的,但很明显它听起来像是一个属性而非方法,如果我们将其做成一个属性,更便于理解)
    
    成人的BMI数值:
    过轻:低于18.5
    正常:18.5-23.9
    过重:24-27
    肥胖:28-32
    非常肥胖, 高于32
      体质指数(BMI)=体重(kg)÷身高^2(m)
      EX:70kg÷(1.75×1.75)=22.86
    '''
    class People:
        def __init__(self,name,weight,height):
            self.name=name
            self.weight=weight
            self.height=height
        @property
        def bmi(self):
            return self.weight / (self.height**2)
    
    p1=People('egon',75,1.85)
    print(p1.bmi)
    
    BMI指数
    import math
    class Circle:
        def __init__(self,radius): #圆的半径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.radius)
    print(c.area) #可以像访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值
    print(c.perimeter) #同上
    '''
    输出结果:
    314.1592653589793
    62.83185307179586
    '''
    #注意:此时的特性area和perimeter不能被赋值
    c.area=3 #为特性area赋值
    '''
    抛出异常:
    AttributeError: can't set attribute
    '''
    
    圆的周长和面积
    圆的周长和面积

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

    5、setter,deleter

    class A:
        def __init__(self):
            self.__x = 10
        #返回__x的值
        @property
        def x(self):
            return self.__x
        #修改__x的值
        @x.setter
        def x(self,new_x):
            self.__x = new_x
        #删除__x的值
        @x.deleter
        def x(self):
            del self.__x
    a = A()
    print(a.x)
    a.x = 20
    print(a.x)
    del a.x
    print(a.x)
    
    私有属性的查看修改删除
    私有属性的查看修改删除

    6、classmethod,staticmethod

    class Student:
        f = open('student', encoding='utf-8')
        def __init__(self):
            pass
    
        def func(self):
            pass
    
        @classmethod   #类方法:默认参数cls,可以直接用类名调用,可以与类属性交互
        def show_student_info_class(cls):
            for line in cls.f:
                name, sex = line.strip().split(',')
                print(name, sex)
    
        @staticmethod   #静态方法  : 让类里的方法直接被类调用,就像正常的函数一样
        def show_student_info_static():
            f = open('student', encoding='utf-8')
            for line in f:
                name, sex = line.strip().split(',')
                print(name, sex)
    
    Student.show_student_info_class()
    Student.show_student_info_static()
    
    类方法和静态方法
    类方法和静态方法
    海宝,男
    海博,女
    海娇,男
    海燕,女
    海东,男
    海峰,男
    student文件

    相同:都可以直接被类调用,不需要实例化

    不同:类方法必须有一个cls参数表示这个类,可以使用类属性;静态方法不需要,静态方法不能直接使用

    普通方法 默认有一个self对象传进来,并且只能被对象调用——绑定到对象

    类方法 默认有一个cls传进来表示本类,并且可以被类和对象(不推荐)调用——绑定到类

    静态方法 没有默认参数,并且可以被类和对象(不推荐)调用——非绑定

  • 相关阅读:
    一个主板上连接两个都有引导的盘
    pytorch查看模型weight与grad
    linux终端窗口字体缩放快捷键
    vim选中多行缩进(python多行缩进)与删除多行前面的空格
    python import 包的路径以及相对路径加载的问题
    pycharm中添加PATH变量
    Atom选中多行操作
    php扩展 swoole的安装与使用
    12121212
    linux系统下清理所有Redis缓存
  • 原文地址:https://www.cnblogs.com/moning/p/7382611.html
Copyright © 2020-2023  润新知