• Python面向对象之封装、property特性、绑定方法与非绑定方法


    一、封装

    '''
    1、什么封装
        封:属性对外是隐藏的,但对内是开放的(对内是开放的是因为在类定义阶段这种隐藏已经发生改变)
        装:申请一个名称空间,往里装入一系列名字/属性
    
    2、为什么要封装
        封装数据属性的目的
            首先定义属性的目的就是为了给类外部的使用使用的,
            隐藏之后是为了不让外部使用直接使用,需要类内部开辟一个接口(就是定义一个接口的函数,然后利用隐藏的原理对其进行封装,这样外部就可以间接使用被隐藏的属性)
            然后让类外部的使用通过接口来间接地操作隐藏的属性。
            精髓在于:我们可以在接口之上附加任意逻辑,从而严格控制使用者对属性的操作
    
        封装函数属性
            首先定义属性的目的就是为了给类外部使用的,
            隐藏函数属性是为了不让外不直接使用,需要类内部开辟一个接口(最终还是给外部用的,只是封装成接口,对于使用者来说方便了)
            然后在接口内去调用隐藏的功能
            精髓在于:隔离了复杂度
    
    
    
    3、如何封装
    
    '''
    # 如何隐藏:在属性前加上__开头(在类定义阶段这种隐藏已经发生变化为:_类__属性(数据属性/函数属性),这样外部的对象或类也可以使用这种方式直接用,但是没有意义)
    
    
    #1、 这种隐藏仅仅只是一种语法上的变形操作
    #2、 这种语法上的变形只在类定义阶段发生一次,因为类体代码仅仅只在类定义阶段检测一次
    #3、 这种隐藏是对外不对内的,即在类的内部可以直接访问,而在类的外则无法直接访问,原因是
    #    在类定义阶段,类体内代码统一发生了一次变形
    
    #4、 如果不想让子类的方法覆盖父类的,可以将该方法名前加一个__开头
    
    
    class People:
        __country='China' #_People__country='China'
        __n=100 #_People__n=100--------------------------------这种类的数据隐藏只是一种语法上的变形
        def __init__(self,name,age,sex):
            self.__name=name #self._People__name=name----------将对象独有的数据属性进行隐藏,隐藏的本质就是语法上的变形而已
            self.age=age
            self.sex=sex
    
        def eat(self):
            print('eat.....')          #可以在封装的接口之上添加任意逻辑
            print(People.__country) #People._People__country----类内部任然可以通过该隐藏的形式进行调用类隐藏的数据属性
            print(self.__name) #self._People__name--------------在内部可以直接用,是因为在定义阶段,这种内部的调用,也发生了语法上的变形
    
    # People.eat(123)                 #类来调用类的函数属性,就是一个普通的函数,按照函数的传参方式传参即可
    '''会报类没有__country这个属性,因为在定义阶段这个属性已经变形为_People__country,所以想条用该属性,就要以变形后的属性调用,才能在类的名称空间中找到这个属性的名字'''
    print(People.__country)        #-----------在定义阶段隐藏的数据属性已经发生变形,所以此时在类的外部直接调用类隐藏的属性,就会报错
    
    peo1=People('egon',18,'male')
    peo1.eat()                     #------------此时对象直接掉封装接口的函数属性,就可以间接的调用隐藏的数据数据属性
    print(peo1.__name)             #------------对象的独有的数据属性在定义阶段已经变形,所以此时在类的外部直接调用任然会报错,找不到该属性
    
    print(People.__dict__)         #------------通过查看类的字典属性,可以看到,数据隐藏的数据属性,都已经发生变形
    print(People.__country)        #-----------该类的数据属性在定义阶段已经发生变化,所以此时来调用会报错,没有这个属性
    print(People._People__country) #类直接调用类定义阶段已经变形后的数据属性,此时在类的名称空间中就可以找到这么一个属性名,所以能拿到属性名对应的属性值
    
    People.__n=11                  #-----------此时看似是对隐藏的属性值进行修改,实际上在类的名称空间中,添加类一个新的属性,因为此时隐藏的属性名已经发生了变形
    print(People.__dict__)         #字典属性有{'_People__n': 100,'__n': 11}---------可以看到并没有修改隐藏的数据属性的值,可以通过People._People__n=11,完成真正意义的修改
    
    
    peo1=People('egon',18,'male')
    print(peo1.__dict__)            #-----{'_People__name': 'egon', 'age': 18, 'sex': 'male'}
    peo1.__x=111                    #------只是单纯的在对象的名称空间中添加了一个__x的数据属性名字
    print(peo1.__dict__)            #-----{'_People__name': 'egon', 'age': 18, 'sex': 'male', '__x': 111}
    peo1.__name=111                 #------只是单纯的在对象的名称空间中添加了一个__name的数据属性名字,并不会改变被隐藏了数据属性的值,因为此时被隐藏的数据属性名已经发生变形
    print(peo1.__dict__)            #-----{'_People__name': 'egon', 'age': 18, 'sex': 'male', '__x': 111, '__name': 111}
    
    
    
    # 属性封装在应用
    class Foo:
        def __f1(self): #_Foo__f1
            print('Foo.f1')
    
        def f2(self):           #对隐藏类的函数属性封装成接口
            print('Foo.f2')
            self.__f1() #self._Foo__f1
    
    class Bar(Foo):
        def __f1(self): #_Bar__f1
            print('Bar.f1')
    
    # obj=Bar()
    # obj.f2()   #从对象自己找f1函数属性,没有到自己的类找,自己的类没有到父类中找,找到了是一个封装的接口,
    # 调用后又调用了self__f1,此时的属性已经变成了self._Foo__f1,所以不会回到子类中去找,最终找的是Foo中的__f1
    
    '''
    Foo.f2
    Foo.f1
    
    '''
    
    
    
    
    class People:
        def __init__(self,name,age):
            self.__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('用户名必须为str类型')
                # return
                raise TypeError('用户名必须为str类型')        #条件成立,raise就会被执行,抛出我们设定的异常
            if type(age) is not int:
                # print('年龄必须为int类型')
                # return
                raise TypeError('年龄必须为int类型')          #条件成立,raise就会被执行,抛出我们设定的异常
            '''-----------------在接口之上添加任意逻辑-----------------'''
            self.__name=name          #在定义阶段已经发生变化,外部对象或类直接调用接口,就可以间接调用隐藏的数据属性
            self.__age=age
    
    
    peo1=People('egon',18)          #调用类产生空对象,并给对象的数据属性进行初始化
    # peo1.name                      #对象的数据属性已经被隐藏,此时直接调用会报错,没有该属性
    # peo1.age
    peo1.tell_info()               #通过接口,我们通过对象直接调用接口,就可以很容易拿到被隐藏的数据属性
    #
    peo1.set_info('egon',19)       #通过接口也很容易对隐藏的数据属性的值进行修改
    peo1.tell_info()

    二、property

    #property装饰器用于将被装饰的方法伪装成一个数据属性,在使用时可以不用加括号而直接引用
    class People:
        def __init__(self,name,weight,height):
            self.name=name
            self.weight=weight
            self.height=height
    
        @property       #把一个函数属性伪装成一个数据属性,这样的好处就是无论是对象还是类来调用,就和访问数据属性一样,不用加括号,就和加括号访问的效果一样
        def bmi(self):  #将bim这个函数属性伪装成了一个数据属性
            return self.weight / (self.height ** 2)
    
    peo1=People('egon',75,1.8)
    
    peo1.height=1.85
    print(peo1.bmi)          #用@property 装饰之后,再访问类的函数属性,就不需要加括号了
    
    
    
    class People:
        def __init__(self,name):
            self.__name=name
        #伪装到底
        @property # 查看obj.name
        def name(self):                          #将name这个函数属性,伪装成了数据属性
            return '<名字是:%s>' %self.__name    #类内部引用隐藏的数据属性,自身也在定阶段发生了变形
    
        @name.setter #修改obj.name=值
        def name(self,name):
            if type(name) is not str:
                raise TypeError('名字必须是str类型傻叉')
            self.__name=name
    
        @name.deleter #删除del obj.name
        def name(self):
            # raise PermissionError('不让删')
            print('不让删除傻叉')
            # del self.__name
    
    peo1=People('egon')
    print(peo1.name)              #<名字是:egon>----查看伪装的函数属性,此时不用加括号
    
    peo1.name='EGON'
    print(peo1.name)             #<名字是:EGON>----修改伪装的函数属性,像修改数据属性一样修改即可
    
    del peo1.name
    print(peo1.name)            #不让删除傻叉、<名字是:EGON>,像删除数据属性一样
    
    
    
    
    
    # 了解:以前的property以前的用法,和上面装饰器的方法效果一样
    class People:
        def __init__(self,name):
            self.__name=name
    
    
        def tell_name(self):
            return '<名字是:%s>' %self.__name
    
        def set_name(self,name):
            if type(name) is not str:
                raise TypeError('名字必须是str类型傻叉')
            self.__name=name
    
        def del_name(self):
            print('不让删除傻叉')
    
        name=property(tell_name,set_name,del_name)
    
    peo1=People('egon')
    print(peo1.name)
    peo1.name='EGON'
    print(peo1.name)
    del peo1.name

     三、绑定方法和非绑定方法

    '''
    1、绑定方法
        特性:绑定给谁就应该由谁来调用,谁来调用就会将谁当作第一个参数自动传入
             《《《精髓在于自动传值》》》
    
        绑定方法分为两类:
            1.1 绑定给对象方法
                在类内部定义的函数(没有被任何装饰器修饰的),默认就是绑定给对象用的
            1.2 绑定给类的方法:
                在类内部定义的函数如果被装饰器@classmethod装饰,
                那么则是绑定给类的,应该由类来调用,类来调用就自动将类当作第一个参数自动传入
    
    
    2、非绑定方法
        类中定义的函数如果被装饰器@staticmethod装饰,那么该函数就变成非绑定方法
        既不与类绑定,又不与对象绑定,意味着类与对象都可以来调用
        但是无论谁来调用,都没有任何自动传值的效果,就是一个普通函数
    
    
    3 应用
        如果函数体代码需要用外部传入的类,则应该将该函数定义成------------------------------绑定给类的方法
        如果函数体代码需要用外部传入的对象,则应该将该函数定义成----------------------------绑定给对象的方法
        如果函数体代码既不需要外部传入的类也不需要外部传入的对象,则应该将该函数定义成-------非绑定方法/普通函数
    
    
    '''
    
    class Foo:
        @classmethod
        def f1(cls):
            print(cls)
    
        def f2(self):
            print(self)
    
    
    obj=Foo()
    print(obj.f2)         #没有被@classmethod装饰,默认就是绑定给对象用的,<bound method Foo.f2 of <__main__.Foo object at 0x0000018370835DD8>>
    print(Foo.f1)         #被@classmethod装饰,则是绑定给类用的,<bound method Foo.f1 of <class '__main__.Foo'>>
    #
    Foo.f1()             #绑定给类用的,类来调用,会将类自动传出,所以打印结果是:<class '__main__.Foo'>
    print(Foo)           #直接打印类的结果也是:--------------------------------<class '__main__.Foo'>
    
    
    #1、f1绑定给类的
    # 了解:绑定给类的应该由类来调用,但对象其实也可以使用,只不过自动传入的仍然是类
    # print(Foo.f1)
    # print(obj.f1)
    # Foo.f1()
    # obj.f1()
    
    #2、f2是绑定给对象的
    # obj.f2()             #没有被装饰,默认是绑定给对象的,会将对象自动传入,<__main__.Foo object at 0x000001BE8FB55DD8>
    # Foo.f2(obj)          #类来调用,就是一个普通的函数,需要将对象手动传入,打印结果也是:<__main__.Foo object at 0x000001BE8FB55DD8>
    
    
    
    # 配置文件信息:
    '''
    IP='1.1.1.10'
    PORT=3306
    '''
    
    import settings
    import uuid
    
    class Mysql:
        def __init__(self,ip,port):
            self.uid=self.create_uid()   #--------给对象添加一个uid属性,这个属性即create_uid() ,是一个函数
            self.ip=ip
            self.port=port
    
        def tell_info(self):
            print('%s:%s' %(self.ip,self.port))
    
        @classmethod     #-------------绑定给类的方法
        def from_conf(cls):
            return cls(settings.IP, settings.PORT)
    
        @staticmethod    #-------------非绑定方法
        def func(x,y):
            print('不与任何人绑定')
    
        @staticmethod     #-------------非绑定方法
        def create_uid():
            return uuid.uuid1()        #uuid1是基于时间戳
    
    # 默认的实例化方式:类名(..)
    obj=Mysql('10.10.0.9',3307)
    
    # 一种新的实例化方式:从配置文件中读取配置完成实例化
    obj1=Mysql.from_conf()
    obj1.tell_info()       #将对象自动传入,从配置文件中读取的结果是:1.1.1.10:3306
    
    # obj.func(1,2)          #不与任何人绑定-----非绑定方法,就是一个普通的函数,对象和类都可以来调用,没有自动传值这么一说,函数需要几个参数就要传几个参数
    # Mysql.func(3,4)        #不与任何人绑定
    # print(obj.func)        #<function Mysql.func at 0x0000018974B6BA60>------非绑定方法,无论是对象还是类来调用都只是一个普通的函数
    # print(Mysql.func)      #<function Mysql.func at 0x0000018974B6BA60>
    
    print(obj.uid)           #9e20bdde-7aae-11e8-9065-485ab6d4e9f8
  • 相关阅读:
    C# 安装包中添加卸载
    如何提取json里面的数据
    JSON写入
    在Net下处理Json
    Linq To Json
    衡量视频序列特性的TI(时间信息)和SI(空间信息)
    DotCMS安装步骤
    【12c OCP】最新CUUG OCP071考试题库(52题)
    【ocp12c】最新Oracle OCP071考试题库(44题)
    【Oracle 12c】最新CUUG OCP071考试题库(53题)
  • 原文地址:https://www.cnblogs.com/sui776265233/p/9239499.html
Copyright © 2020-2023  润新知