zoukankan      html  css  js  c++  java
  • 19.面向对象:封装、绑定方法与非绑定方法、继承

    • 引子

    • 封装

    • 隐藏属性

    • 类内的装饰器:特性(property)

    • 绑定方法与非绑定方法

    • 继承

    • 先抽象再继承

    • 继承背景下的属性查找


    • 封装

      封装指的就是把数据与功能都整合到一起,听起来是不是很熟悉,没错,我们之前所说的”整合“二字其实就是封装的通俗说法。
      除此之外,针对封装到对象或者类中的属性,我们还可以严格控制对它们的访问,分两步实现:隐藏与开放接口.
    • 隐藏属性

      在python中用双下划线开头的方式将属性隐藏起来(设置成私有的),但其实这仅仅只是一种变形操作
    # 类中所有双下划线开头的名称如__x都会在类定义时自动变形成:_类名__属性名的形式:
    class People:
    # 类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的 ↓
        __country = "China"        # 变形为 _People__country = "china"
    
        def __init__(self,name,age):
            self.__name = name  # self._People__name = name
            self.__age = age    # self._people__age = age
    
        def __func(self):  # _People__func
            print('xx')
    
        def tell_name(self):
            print(self.__name)  # self._People__name
    
    print(People._People__country) # 只有在类内部才可以通过__开头的形式访问到.
    print(People._People__func)
    
    print(People.__dict__)
    obj1 = People("egon",18)
    print(obj1.__dict__)
    # __开头的属性的特点:
    # 1、并没有真的藏起来,只是变形了
    # 2、该变形只在类定义阶段、扫描语法的时候执行,此后__开头的属性都不会变形
    obj1.__gender = "male" 
    print(obj1.__dict__)   
    print(obj1.__gender )
    # 3、该隐藏对外不对内
    obj1.tell_name()
    
    
    
    • 封装的真谛在于明确地区分内外,封装的属性可以直接在内部使用,而不能被外部直接使用,然而定义属性的目的终归是要用,外部要想用类隐藏的属性,需要我们为其开辟接口,让外部能够间接地用到我们隐藏起来的属性,那这么做的意义何在???

      1:隐藏数据:将数据隐藏起来这不是目的。隐藏起来然后对外提供操作该数据的接口,然后我们可以在接口附加上对该数据操作的限制,以此完成对数据属性操作的严格控制。
    # 为何要隐藏属性
    # 1、隐藏数据属性为了严格控制类外部访问者对属性的操作
    class People:
    
        def __init__(self,name,age):
            self.__name = name  # self._People__name = name
            self.__age = age
    
        def tell_info(self):
            print("<%s:%s>" %(self.__name,self.__age))
    
        def set_info(self,name,age):
            if type(name) is not str:
                print("名字必须是字符串")
                return
            if type(age) is not int:
                print("年龄必须是数字")
                return
            self.__name = name
            self.__age = age
    
    obj1 = People("egon",18)  
    obj1.tell_info()     # <egon:18>
    
    obj1.set_info("tom",28)  
    obj1.tell_info()     # <tom:28>
    
    obj1.set_info(123123123123,28)  # 名字必须是字符串
    obj1.tell_info()     # <egon:18>
    
    2:隐藏方法:目的是隔离复杂度
    # 取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
    # 对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做
    # 隔离了复杂度,同时也提升了安全性
    
    # 隔离复杂度的例子
    class ATM:
        def __card(self):
            print('插卡')
        def __auth(self):
            print('用户认证')
        def __input(self):
            print('输入取款金额')
        def __print_bill(self):
            print('打印账单')
        def __take_money(self):
            print('取款')
    
        def withdraw(self):
            self.__card()
            self.__auth()
            self.__input()
            self.__print_bill()
            self.__take_money()
    
    a=ATM()
    a.withdraw()
    
    

    真正的封装是,经过深入的思考,做出良好的抽象,给出“完整且最小”的接口,并使得内部细节可以对外透明

    (注意:对外透明的意思是外部调用者可以顺利的得到自己想要的任何功能,完全意识不到内部细节的存在)

    • 类内的装饰器:

    • 特性(property)

      什么是特性property
      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.8)
    
    # print(p1.bmi())  # 正常访问p1.bmi()需要加括号
    # 23.148148148148145
    
    print(p1.bmi)    #  用@property将bmi功能伪装成了数据属性
    # 23.148148148148145
    
    property的使用:
    class People:
        def __init__(self,name):
            self.__name = name
            
            
    #    def get_name(self):
    #        return self.__name 
    
        @property            # 1.查看行为
        def name(self):      # 被property装饰的name函数,其实是__name被藏起来了
            return self.__name
    
        @name.setter           # 
        def name(self,x):       # 2.修改行为
            if type(x) is not str:
                raise Exception("名字必须是字符串类型")
            self.__name = x
    
        @name.deleter      # 3.删除行为
        def name(self):
            print("不允许删除")
    
    p1 = People('egon')
    # print(p1.get_name)  # 访问
    
    # 对应查看name行为   
    print(p1.name)   # p1.name通过@property访问到的self.__name的属性
    
    # 对应修改name行为
    p1.name = 123 # 抛出异常
    p1.name = "EGON"  # 正常修改
    print(p1.name)
    
    # 对应删除name行为
    del p1.name
    print(p1.name)
    
    # ps: 配合property可以先将一个属性藏起来,__name这个属性被藏起来了,藏起来之后定义三个函数都叫name
    #      这三个函数里面分别写上name的1、2、3的三种行为,跟操作一一对应
    
    
    
    # 了解性知识点:这种方式也可以达到上面操作的效果
    
    class People:
        def __init__(self,name):
            self.__name = name
    
        def get_name(self):
            return self.__name
    
        def set_name(self,x):
            if type(x) is not str:
                raise Exception("名字必须是字符串类型")
            self.__name = x
    
        def del_name(self):
            print("不允许删除")
    
        name = property(get_name,set_name,del_name)
    
    p1 = People('egon')
    
    

    • 绑定方法与非绑定方法

    • 一:绑定方法

      特点:绑定给谁就应该由谁来调用,谁来调用就会将自己当做第一个参数传入

        1. 但凡在类中定义一个函数,默认就是绑定给对象的,应该由对象来调用,
          会将对象当作第一个参数自动传入
        1. 如果想要将函数绑定给类的话就需要用到绑定到类的方法:用classmethod装饰器装饰的方法。
          类中定义的函数被classmethod装饰过,就绑定给类,应该由类来调用,
          类来调用会类本身当作第一个参数自动传入
    • 二:非绑定方法

      特点:不与类和对象绑定,意味着谁都可以来调用,但无论谁来调用就是一个普通

      函数,没有自动传参的效果

      • 类中定义的函数被staticmethod装饰过,就成一个非绑定的方法即一个普通函数,谁都可以调用,
        但无论谁来调用就是一个普通函数,没有自动传参的效果
      注意:与绑定到对象方法区分开,在类中直接定义的函数,没有被任何装饰器装饰的,都是绑定到对象的方法,可不是普通函数,对象调用该方法会自动传值,而staticmethod装饰的方法,不管谁来调用,都没有自动传值一说
    class People:
        def __init__(self,name):
            self.name = name
    
        # 但凡在类中定义一个函数,默认就是绑定给对象的,应该由对象来调用,
        # 会将对象当作第一个参数自动传入
        def tell(self):
            print(self.name)
    
        # 类中定义的函数被classmethod装饰过,就绑定给类,应该由类来调用,
        # 类来调用会类本身当作第一个参数自动传入
        @classmethod
        def f1(cls):  # cls = People
            print(cls)
    
        # 类中定义的函数被staticmethod装饰过,就成一个非绑定的方法即一个普通函数,谁都可以调用,
        # 但无论谁来调用就是一个普通函数,没有自动传参的效果
        @staticmethod
        def f2(x,y):
            print(x,y)
    
    p1 = People('egon')
    p1.tell()
    
    print(People.f1)
    People.f1()
    
    print(People.f2)
    print(p1.f2)
    
    People.f2(1,2)
    p1.f2(3,4)
    
    
    
    # 示例场景
    
    '''
    # settings.py 配置文件模块
    
    NAME = "xxx"
    AGE = 103
    GENDER = "male"
    
    '''
    
    import settings
    
    class People:
        def __init__(self,name,age,gender):
          #  self.id = self.create_id()
            self.name = name
            self.age = age
            self.gender = gender
    
        def tell_info(self):  # 打印详细信息
            print('<%s:%s:%s>' %(self.name,self.age,self.gender))
    
        @classmethod         # 绑定给类的方法
        def from_conf(cls):  # cls 自动传入类
            print(cls)
            return cls(settings.NAME, settings.AGE, settings.GENDER)
    
    #    @staticmethod    # 非绑定方法 谁都可以来调但没有自动传参效果
    #    def create_id():
    #        import uuid           # 调用随机产生id模块
    #        return uuid.uuid1()  # uuid.uuid1 可以产生随机编号
    
    
    
    p1 = People("egon",18,"male")  # 实例化得到一个对象
    
    p2 = People.from_conf()  # 通过配置文件完成实例化 绑定给类的方法应该由类去调
    print(p2.__dict__)
    
    
    # 需求:每个用户都要有一个自己的随机id号
    # print(p1.create_id())    
    # print(People.create_id())
    
    # print(p1.__dict__)  # 实例化后用户就有自己的id了
    

    • 继承

    • 什么是继承?
      继承是创建新类的一种方式,新建的类称之为子类。
      被继承的类称之为父类、基类、超类
      继承的特点:子类可以遗传父类的属性
    • 类是用来解决对象之间冗余问题的
    • 而继承则是解决类与类之间冗余问题的

    注意:新建的类可以继承一个或多个父类(python支持多继承)

    class Parent1(object):  # 定义父类
        pass
    
    class Parent2(object):  # 定义父类 
        pass
    
    class Sub1(Parent1):    # 定义子类,单继承,继承基类Parent1
        pass
    
    class Sub2(Parent1,Parent2): # 定义子类,多继承,既继承基类parent1又继承基类parent2
        pass
    
    # ps:__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类
    
    print(Sub1.__bases__)  # 查看基类
    # (<class '__main__.Parent1'>,)  # 单继承
    
    print(Sub2.__bases__)  # 查看基类
    # (<class '__main__.Parent1'>, <class '__main__.Parent2'>)  # 多继承
    
    
    
    
    
    # 但凡是继承了object类的子类以及该子类的子子孙孙类都是新式类
    # 反之就是经典类
    
    # 在python2中有新式类与经典类之分,在python3中全都是新式类
    # 在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类
    # 在python2中,显式地声明继承object的类,以及该类的子类,都是新式类
    # 在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类
    
    # print(Parent1.__bases__)
    # print(Parent2.__bases__)
    
    
    • 先抽象再继承

      继承描述的是子类与父类之间的关系,是一种什么是什么的关系。要找出这种关系,必须先抽象再继承

      抽象即抽取类似或者说比较像的部分。

      抽象分成两个层次:

      1.将奥巴马和梅西这俩对象比较像的部分抽取成类;

      2.将人,猪,狗这三个类比较像的部分抽取成父类。

      抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)

      继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。

      抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类

    • 继承背景下的属性查找

    # 示例1
    class Bar:    # 定义类
        def f1(self):      
            print('Bar.f1')
    
        def f2(self):
            print('Bar.f2')
            self.f1()  # obj.f1()
    
    class Foo(Bar):
        def f1(self):
            print("Foo.f1")
    
    obj = Foo()
    obj.f2()  # 先从对象obj找没有--》去对象的类Foo里面找没有--》去Bar里面找有,会打印'Bar.f2'
              # 紧接着找self.f1()调的是obj.f1(),按顺序查找打印结果'foo.f1'
    
    # 示例2
    class Bar:
        def __f1(self):  # 在定义阶段扫描语法变形成_Bar__f1
            print('Bar.f1')
    
        def f2(self):
            print('Bar.f2')
            self.__f1()  # 在类定义阶段扫描语法变形成 self._Bar_f1
    
    class Foo(Bar):
        def __f1(self):  # _Foo__f1
            print("Foo.f1")
    
    obj = Foo()
    obj.f2()  # 按顺序查找到'Bar.f2'并打印, 紧接着查找self.__f1(),打印结果Bar.f1
    
  • 相关阅读:
    Springboot 之 自定义配置文件及读取配置文件
    SQLSERVER系统视图 sql server系统表详细说明
    MySQL Workbench建表时 PK NN UQ BIN UN ZF AI 的含义
    使用Ecplise git commit时出现"There are no stages files"
    maven添加sqlserver的jdbc驱动包
    java将XML文档转换成json格式数据
    java将XML文档转换成json格式数据
    cannot be resolved. It is indirectly referenced from required .class files
    org.codehaus.jackson.map.JsonMappingException: Can not construct instance of java.util.Date from String value '2012-12-12 12:01:01': not a valid representation (error: Can not parse date "2012-12-
    @Autowired注解和静态方法 NoClassDefFoundError could not initialize class 静态类
  • 原文地址:https://www.cnblogs.com/gfeng/p/14264195.html
Copyright © 2011-2022 走看看