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
    
  • 相关阅读:
    jquery图片播放弹出插件Fancybox
    D3js-API介绍【英】
    ZOJ 3156 Taxi (二分匹配+二分查找)
    linux权限之su和sudo的差别
    CareerCup之1.6 Rotate Image
    [oracle]pl/sql --分页过程demo
    已迁移到http://www.coffin5257.com
    C# 之 集合ArrayList
    Java 序列化Serializable具体解释(附具体样例)
    Android 短信验证码控件
  • 原文地址:https://www.cnblogs.com/gfeng/p/14264195.html
Copyright © 2011-2022 走看看