美文网首页
面向对象 五

面向对象 五

作者: 吃可爱长大鸭 | 来源:发表于2021-03-04 18:53 被阅读0次

    目录

    1.isinstance和isinstance
    2.反射
      2.1 反射模块
      2.2 反射实际应用案列
      2.3 反射隐藏属性and设置属性
    3.反射内置方法 __setattr__  __delattr__  __getattr__
    4.__getattribute__ 属性被访问时行为
    5.__setitem__ __getitem__ __delitem__  []获取 设置 删除
    6.__format__格式化打印格式
    7.__str__ 显示内容
    8.__del__ 关闭系统资源
    9.__doc__ 类的注释信息
    10.__call__ 预执行
    11.__new__ 对象生成
    12.__init__ 初始化
    13.__all__ 限制属性
    14.__len__ 计算长度
    15.__hash__ 转换类型
    16.__eq__ 对象 == 触发
    17. __class__ 显示对象类
    18.__module__ 显示对象所属模块
    19.__slots__ 控制对象可以使用的属性
    20. __dict__ 对象属性
    21.__next__和__iter__ 实现迭代器
    22.__enter__和__exit__ 上下文管理器
    23.描述符
        23.1 描述符小案例
        23.2 描述符的分类
        23.3描述符机制来实现类型限制功能
    24.类的装饰器
         24.1 自制property装饰器
         24.2 @classmethod类方法
         24.3 @staticmethod 静态方法
    

    1.isinstance和isinstance

    # 判断某个对象是不是某个类的实例
    # isinstance() 
    class Person:
        pass
    class Student(Person):
        pass
    
    stu = Student()
    
    #判断 两个对象是不是同一个类型
    print(type(1) == type(1))
    
    # 判断stu对象是不是Student类的实例
    print(isinstance(stu,Student))
    
    # 是不是子类
    # issubclass()  
    
    # 判断一个类是不是 另一个类子类 (所有类都是object的子类或子子类)
    print(issubclass(Student,object))
    
    
    # 单继承(父类--》父类)
    class Animal:
        pass
    
    
    class Dog(Animal):
        pass
    
    class TDog(Dog):
        pass
    
    print(issubclass(TDog,Dog))
    print(issubclass(TDog,Animal))
    
    
    
    # 多继承下
    import json
    
    class Foo:
        pass
    # 多继承
    class Mydic(dict,Foo):
         def to_json(self):
            return json.dumps(self)
    
    #  判断多继承类是不是子类
    print(issubclass(Mydic,dict))
    print(issubclass(Mydic,Foo))
    
    
    # py3中:所有的类都是由type构造来的
    # py3中:所有类都继承object
    # print(type(object))
    # print(type(type))
    
    

    2.反射

    """
      反射:通过字符串来操作类或者对象的属性
          hasattr()  # 判断一个对象中是否由某个属性或方法
          getattr()  # 通过字符串获取对象中的属性或方法
          setattr()  # 通过字符串设置对象的属性或方法
          delattr()  # 通过字符串删除对象的属性或方法
    
    """
    #  一、hasattr()  # 判断一个对象中是否由某个属性或方法
    
    class Person:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def print_name(self):
            print(self.name)
    
    
    p = Person('mzk', 18)
    
    # 判断p对象中是否有name属性和print_name方法
    
    # p是对象判断字符串name属性是否存在
    print(hasattr(p,'name'))
    # print('name' in p.__dict__)
    
    # p是对象判断字符串print_name方法是否存在
    print(hasattr(p,'print_name'))
    # print('print_name' in Person.__dict__)
    
    
    
    # 二、getattr()  # 通过字符串获取对象中的属性或方法
    
    class Person:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def print_name(self):
            print(self.name)
    
    
    p = Person('mzk', 18)
    
    # 一个通过字符串获得(反射),一个通过点:正常的对象调用方法
    f=getattr(p,'print_name')
    f=p.print_name
    print(f)  # 函数内存地址
    f() # 执行
    
    
    # 三、setattr()  # 通过字符串设置对象的属性或方法
    
    class Person:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def print_name(self):
            print(self.name)
    
    
    p = Person('mzk', 18)
    
    # 反射属性到对象名称空间
    p=setattr(p,'sex','男')
    # 对象属性的设置
    p.sex='男'  
    print(p)
    
    # 反射属性到类名称空间
    setattr(Person,'sex','男')
    # 类的属性设置
    Person.sex='未知'
    print(p.sex)
    
    # 反射方法到类的内部
    def xxx(self):
        print('xxx')
        print(self.age)
    
    # 绑定给对象的方法是写在类中,如果使用反射,要反射到类内部
    setattr(Person,'print_age',xxx)
    p.print_age()
    
    # 反射对象 
    def xxx(obj):
        print(obj.age)
    
    setattr(p,'print_age',xxx)
    
    p.print_age(p)
    
    # 四、delattr()  # 通过字符串删除对象的属性或方法
    
    class Person:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def print_name(self):
            print(self.name)
    
    
    p = Person('mzk', 18)
    
    delattr(p,'name')  # 通过反射删除了属性
    print(p.name) # 查询没有
    
    delattr(p,'print_name')  # 通过反射删除了方法
    p.print_name() # 查询没有
    

    2.1 反射模块

    方法: __import__('字符串')
    
    # 使用小方法
    i=input('请输入使用哪个模块:')
    # 模块以字符串形式传入
    s1=__import__(i)
    # 调用模块方法
    s1.test()
    # 打印模块中的变量
    print(s1.name)
    
    # 拓展,带路径的模块导入,点路径
    i = input('请输入使用哪个模块:')
    s1 = __import__(i, fromlist=True)
    s1.test()
    print(s1.name)
    
    # 反射模块(py3中用的比较多,框架中)
    import importlib
    i=input('请输入您要使用的模块:')
    a=importlib.import_module(i)
    a.test()
    

    2.2 反射实际应用案列

    class Person:
        def __init__(self, name, age, sex):
            self.age = age
            self.sex = sex
            self.name = name
    
        def print_age(self):
            print(self.age)
    
        def print_name(self):
            print(self.name)
    
        def print_sex(self):
            print(self.sex)
    
        def print_info(self):
            print('我的名字是%s,年龄是%s,性别是%s' % (self.name, self.age, self.sex))
    
        def server_forever(self):
            while True:
                ss = input('请输入您要执行的方法,按q退出')  #
                if 'q' == ss.lower(): break
                if hasattr(self, ss):
                    # f=getattr(self,ss)
                    # f()
                    # 下面这一步等同于上面注释的那两个
                    getattr(self, ss)()
                else:
                    print('您输入的指令不执行,我正在学习中')
    
    
    p = Person('mzk', 18, '男')
    
    p.server_forever()
    

    2.3 反射隐藏属性and设置属性

    # 反射成隐藏属性
    class Person:
        def __init__(self, name, age, sex):
            self.age = age
            self.sex = sex
            self.__name = name
    
    
    p = Person('lqz', 18, '男')
    
    # name=getattr(p,'__name')  # 这个方法拿不到隐藏属性
    
    # __dict__方法中的写法可以拿到
    name=getattr(p,'_Person__name')
    print(name)
    
    # 设置成隐藏属性
    class Person:
        def __init__(self, name, age, sex):
            self.age = age
            self.sex = sex
            self.__name = name
    
        def print_wife(self):
            print(self.__wife)
    
    
    p = Person('lqz', 18, '男')
    
    # 对象方法:设置隐藏属性
    setattr(p,'__wife','刘亦菲')
    print(p.__wife)  # 能拿到属性
    
    # 对象和字符串类名加属性隐藏 
    setattr(p,'_Person__wife','刘亦菲')
    print(p.__dict__)
    print(p.__wife) # 拿不到  
    
    # 直接从方法中拿到返回值的隐藏属性
    p.print_wife()
    
    # 类的方法:设置隐藏属性
    # 类和字符串类名加属性隐藏 
    setattr(Person,'_Person__wife','刘亦菲')
    print(Person.__dict__)
    print(Person._Person__wife) # 拿到隐藏属性
    
    # 直接从方法中拿到返回值的隐藏属性
    p.print_wife() 
    

    3.反射内置方法 __setattr__ adn __delattr__ and __getattr__

    '''
    # 面向对象中的魔法方法,在某种情况下就会触发它的执行
    
    # __setattr__  :在  对象.属性=值  会触发它的执行
    # __getattr__  :在  对象.属性   获取值会触发它的执行
    # __delattr__  :在 del 对象.属性  会触发它的执行
    
    setattr(对象,key,value)
    内部:对象.__setattr__(key,value)
    保证对象必须要有__setattr__,所有类的基类,object中有__setattr__
    '''
    
    class Person:
        def __setattr__(self, key, value):
            # print('我执行了')
            # print(key)
            # print(value)
           
            # setattr(self,key,value)  # 本质会调用对象自己的__setattr__ ,出递归,报错
            object.__setattr__(self,key,value)
            
    
        def __getattr__(self, item):  # 当属性不在对象中,会触发它的执行
            print('我执行了')
    
        def __delattr__(self, item):
            # delattr(self,item)
            print('删除会触发我')
    
    
    p = Person()
    p.name = 'mzk'
    
    # print(p.age)
    
    del p.name
    

    4.__getattribute__ (了解即可)

    '''
    无论属性在对象中是否存在,都先触发__getattribute__的执行(用的比较少)
    
    对象.属性 调用顺序 
          先执行:__getattribute__----》类的名称空间----》__getattr__(本质是去对象自己的名称空间拿东西)
    
    对象.属性的查找顺序
          当getattribute与getattr同时存在,只会执行getattrbute,
          除非getattribute在执行过程中抛出异常AttributeError
    
    重点: __getattribute__:  对象.属性---》__getattribute__---》__getattr__
    '''
    
    class Person:
        name = 'lqz'
        # 属性不存在,才触发
        def __getattr__(self, item):
            print('getattr触发了')
    
        def __getattribute__(self, item):
            print('__getattribute__触发了')
    
    
    p = Person()
    print(p.name)
    

    5. __setitem__ 、__getitem__ 、__delitem__(了解即可)

    '''
    __getitem__:通过中括号取值触发它的执行
    __setitem__:通过中括号赋值,触发它的执行
    __delitem__:通过中括号删除值,触发它的执行
    
    item系列
        __getitem__: 对象['name'],触发它的执行
        __setitem__: 对象['name']='value',触发它的执行
        __delitem__: del 对象['name'],触发它的执行
    
        应用:把一个对象做成既能.取值赋值,又能[] 取值赋值
        -自己定义这种类(会出现递归现象,反射setattr)
            -对象.__dict__
            -object.__setattr__(self,key,value) 普通函数,有几个值,就传几个值
            -object.__setattr__(self,key,value)
            -对象.__name='mzk'
    
        后期封装+setattr的使用(注意)
          后期源码中常出现的操作(flask框架源码中使用)
            -object.__setattr__(self,'_类名__属性名',value)
            -对象.__dict__[_类名__属性名]='mzk'
            -类内部使用  self.__属性名
            -类外部使用: 对象._类名__属性名
    '''
    
    
    class Person:
        def __getitem__(self, item):
            print('__getitem__执行')
    
        def __setitem__(self, key, value):
            print('__setitem__执行')
    
        def __delitem__(self, key):
            print('__delitem__执行')
    
    p=Person()
    p['name']='lqz'
    p['name']
    del p['age']
    
    
    # 自己定义一个字典,但是不继承字典
    # 作业:写一个字典,不继承字典,支持 [] 取值赋值,不支持 . 取值赋值
    class Mydict:
        def __init__(self,**kwargs):
            # 方案一
            # for key,value in kwargs.items():
            #     setattr(self,key,value)
    
            # 方案二
            self.__dict__.update(kwargs)
    
            # 方案三
            # for key,value in kwargs.items():
            #     self.__dict__[key]=value
    
        def __getitem__(self, item):
            # 方案一
            # return self.__dict__[item]
    
            # 方案二
            return getattr(self,item)
    
        def __setitem__(self, key, value):
            # 方案一
            # setattr(self,key,value)
    
            # 方案二
            self.__dict__[key]=value
    
        def __delitem__(self, key):
            del self.__dict__[key]
    
    
    
    
    dic=Mydict(name='lqz',age=18)
    
    # 打印属性
    print(dic.name)
    
    # 触发__getitem__方法
    print(dic['name'])
    
    # 触发__setitem__方法
    dic['age']=18
    
    print(dic.age)
    
    # 触发__delitem__方法
    del dic.age
    
    print(dic.__dict__)
    

    6.__format__ 格式化打印格式(了解即可)

    '''
    __format__
        -format(对象)  ---》触发类中__format__的执行
        -'{:n-m}'.format(对象) ---》触发类中__format__的执行
    '''
    
    # 一 字符串格式
    s = '我的名字是:%s' % 'mzk'
    print(s)
    
    # 字符串的format方法支持的格式化方式
    
    res = '我的名字是{},年龄是{}'.format('mzk', 18)
    res = '我的名字是{0},年龄是{1}'.format('mzk', 18)
    
    ll = ['mzk', 19]
    l2 = ['男']
    res = '我的名字是{0[0]},年龄是{0[1]},性别是{1[0]}'.format(ll, l2)
    dic = {'name': 'mzk', 'age': 19, 'sex': '男'}
    res = '我的名字是{name},年龄是{age},性别是{sex}'.format(**dic)
    
    
    class Person:
        def __init__(self):
            self.name = 'mzk'
            self.age = 19
            self.sex = '男'
    
    
    p = Person()
    # res='我的名字是{0.name},年龄是{0.age},性别是{0.sex}'.format(p)
    res = '我的名字是{obj.name},年龄是{obj.age},性别是{obj.sex}'.format(obj=p)
    print(res)
    
    
    # 对象的__format__
    
    class Person():
        __dic = {
            'n-a': '名字是:{obj.name}-----年龄是:{obj.age}',  # 名字是:mzk-年龄是:18
            'n:a': '名字是:{obj.name}:::::年龄是:{obj.age}',  # 名字是:mzk:年龄是:18
            'n/a': '名字是:{obj.name}/////年龄是:{obj.age}',  # 名字是:mzk/年龄是:18
        }
    
        def __init__(self, name, age, sex):
            self.name = name
            self.sex = sex
            self.age = age
    
        def __format__(self, format_spec):
            # print(format_spec)
            if format_spec and format_spec in self.__dic:
                s = self.__dic[format_spec]
            else:
                s = self.__dic['n-a']
            return s.format(obj=self)
    
    
    p = Person('mzk', 18, '男')
    
    # 内置函数
    # 第一种触发它执行
    # res=format(p)
    # res=format(p,'n-a')
    # res = format(p,'n/a')
    # res = format(p,'n:a')
    print(format(p, 'n-a'))
    
    # 第二种触发方式
    # print('n-a'.format(p))
    
    
    # 写一个Date类,实例化的时候传入年  月   日,格式化方法
    # d=Date(2020,12,23)
    # print('y-m-d'.format(p)) # 2020-12-23
    # print('y/m/d'.format(p)) # 2020/12/23
    # print('y:m:d'.format(p)) # 2020:12:23
    # print('y年m月d日'.format(p)) # 2020年12月23日
    

    7.__str__显示内容

    """
    
        __str__
        前后带杠杠的都是特殊的内置函数  会在某些时机自动执行  一般情况我们不应该直接调用他们
    
        当我们需要自定义打印显示内容时 就需要实现__str__方法
        该方法必须返回一个字符串  返回的是什么 打印出来就是什么
    
    """
    class Test:
        def __init__(self,name):
            self.name = name
        def __str__(self):
            print("str run....")
            return self.name
    t = Test("安米")
    
    #  __str__ : print(对象) 触发对象的 __str__
     print(t)
    
    # 在讲一个对象转换字符串时  本质就是在调用这个对象 __str__方法
    print(str(t))
    

    8.__del__关闭系统资源

    """
        __del__
    
        当对象被从内存中删除时会自动执行
        另一种情况时  程序员手动删除了这个对象 也会自动执行
    
        什么时候使用它
        在python中 有自动内存管理机制 所以 python自己创建的数据 不需要我们做任何操作
        但是有一种情况 我们使用python打开了一个不属于python管理的数据
        比如打开了一个文件  这个文件一定是操作系统在打开 会占用系统内存  而python解释器无法操作系统内存的
        所以 当你的python解释器运行结束后  文件依然处于打开状态  这时候就需要使用__del__来关闭系统资源
    
        简单地说 当程序运行结束时 需要做一些清理操作 就使用__del__
    
        __del__也称之为 析构函数
    
        分析构造 并拆除这个对象
    """
    
    
    class TextFile:
    
        def __init__(self,filepath,mode="rt",encoding="utf-8"):
            self.file = open(filepath,mode=mode,encoding=encoding)
    
        def read(self):
            return self.file.read()
    
        def write(self,text):
            self.file.write(text)
    
        # 该方法其实就是一个通知性质 仅仅是告诉程序员 对象即将被删除
        def __del__(self):
            # 在这里关闭系统的文件 妥妥的
            self.file.close()
    
    
    tf = TextFile("今日内容.txt")
    
    tf.read()
    tf.write()
    
    # tf.file.close() 不需要手动关闭了  在对象删除时会自动关闭
    

    9. __doc__ 类的注释信息

    '''
     __doc__: 类下面的注释  类名.__doc__ ,把注释打印出来
    '''
    
    
    class Person:
        '''
        类的提示信息,注释
        '''
         pass
    
    
    print(Person.__doc__)
    
    
    from bs4 import BeautifulSoup
    
    print(BeautifulSoup.__doc__)
    
    
    # 注意:继承关系下,类的注释不能继承
    
    class Animal:
        '''
        Animal的注释,动物类,所有属于动物的都继承我
        '''
    
    class Person(Animal):
        pass
    
    
    print(Animal.__doc__)
    print(Person.__doc__)
    

    10.__call__预执行

    """
    __call__  调用的意思
    在对象被调用时 执行 函数 类
    
    自定义元类的目的
    1.可以通过__call__ 来控制对象的创建过程
    2.可用控制类的创建过程
    """
    
    # 自定义一个元类 元类也是一个类   但是需要继承type
    class MyMeta(type):
    
        # self 表示要创建对象的那个类(Person)  *args是调用Person类时传入的参数
        def __call__(self, *args, **kwargs):
            print("MyMte中的 call run'")
            print(self, *args, **kwargs)
    
            # 下面的三步是固定写法 一个模板 只要你需要控制对象的创建过程 就应该先把模板写出来
    
            # 1.创建空对象
            obj = object.__new__(self)
            # 2.调用初始化方法
            self.__init__(obj, *args, **kwargs)
            # self.__init__(obj)
            # 3.得到一个完整的对象
            return obj
    
    
    # 修改Person类的元类为MyMeta
    class Person(metaclass=MyMeta):
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def __call__(self, *args, **kwargs):
            print("call run...")
    
    
    # 调用Person这个对象时 执行的是 Person的类(type)中__call__ 方法
    p = Person("张三疯", 80)
    
    # print(p)
    # 当调用对象时 会执行该对象所属类中的__call__方法
    p()
    
    # print(p.name)
    # print(p.age)
    

    11.__new__ 对象生成

    '''
    __new__   对象生成 ,类() 会触发,是在__init__之前触发
    类实例化得到对象的本质是先调用__new__生成一个空对象,然后再调用__init__完成初始化
    总结:类实例化得到对象,本质是在执行类的 __new__,返回什么对象,p拿到的就是什么对象,在__new__内部,调用了__init__
    
    
    '''
    class Person:
        def __init__(self, name):
            # 初始化,self这个对象,已经造出来了
            # self是在哪里造出来的? __new__中造出来的
            print('__init__执行了')
            print(self)
            self.name = name
    
        # def __new__(cls, *args, **kwargs):
        #     print('我出生了')
        #     print(args)
        #     print(kwargs)
        #     # return 1
        #     cls.__init__(1,*args)
        #     ## 如果一个类,没有重写 __new__方法,在__new__中自动触发了对象的__init__的执行
    
        def __new__(cls, *args, **kwargs):
            print('我出生了')
            # cls(*args,**kwargs)  # 出递归
    
            # obj=object.__new__(cls) # 静态方法之类来调用
            obj = super().__new__(cls)  # 静态方法之对象来调用
    
            cls.__init__(obj, *args, **kwargs)  # 穿衣服,调用__init__
            ## 如果一个类,没有重写 __new__方法,在__new__中自动触发了对象的__init__的执行
            return obj  # 必须要return,造出来的对象
    
    
    p = Person('mzk')
    print(p)
    print(p.name)
    
    
    # 列子
    '''
    Person()---->本质触发的是__new__,因为__new__返回什么对象就是什么
    __init__是初始化方法,在__new__后执行的
    
    __new__:创造出一个空对象,没有初始化
    __init__:创造完空对象后,再触发它,完成初始化
    
    '''
    class Person:
        def __init__(self,name):
            print('我执行了,我是__init__')
            self.name=name
        def __new__(cls, *args, **kwargs):
            print('我出生了')
            # __new__内部会触发 self.__init__的执行
            obj=object.__new__(cls)
            # return {'name':'l1z'}  # 不会触发__init__
            return obj  # 触发 __init__
    
    
    p=Person('lqz')  # 触发__new__,返回 1 对象
    
    print(p)
    

    12.__init__ 初始化

    '''
     __init__  对象初始化调用   类() 会触发
    '''
    

    13.__all__ 限制属性

    # a.py
    __all__=['age','name']
    age=10
    name='lqz'
    sex='男'
    
    # __all__:使用from  a import  *导入模块时,限制能够使用的属性
    # 在使用from a import * 导入模块时,只能导入__all__指定的属性
    from  a import  *
    
    print(age)
    print(name)
    # print(sex)
    

    14.__len__ 计算长度

    '''
     __len__:计算对象长度,返回几,len(对象),结果就是几
    len(对象)----》触发对象的__len__()
    '''
    
    class Foo:
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def __len__(self):
            return len(self.name)
    
    
    f = Foo('lqz', 19)
    
    print(len(f))
    

    15.__hash__ 转换类型(了解即可)

    '''
    __hash__
        -字典类型---》hash类型---》key值必须可hash
        -可变类型(字典,列表)不可hash,不可变类型(数字,字符串,元组)可以hash,可以作为字典的key
        -重写自己定制的类的hash方法,使对象可以hash
    
    __hash__   ,hash(对象)触发执行,返回什么就是什么
    如果一个对象不可hash,通过重写 __hash__让它变得可hash
    '''
    # 普通用法
    l=['1',3,54,56]
    dic={'name':'lqz'}
    
    a=10
    
    print(hash(a))
    # print(hash(dic))
    print(hash(l))
    
    # 魔法方法
    class Foo:
        def __init__(self):
            self.name = 'lqz'
            self.l = ['1', 3, 4]
    
        def __hash__(self):
            return hash(self.name)
    
    
    f = Foo()
    print(hash(f))
    

    16.__eq__对象 == 触发(了解即可)

    '''
    两个对象 == 比较的时候,触发 __eq__的执行,在内部,自定制比较规则即可
    '''
    
    class Foo:
        def __str__(self):
            return self.name
        def __init__(self):
            self.name='lqz'
    
        def __eq__(self, other):
            # print(self)
            # print(other)
            # 自己定制比较规则
            if self.name == other.name:
                return True
            else:
                return False
    
    
    f1=Foo()
    # f1.name='mzk'
    f1.age=999
    
    f2=Foo()
    f2.age=888
    print(f1==f2)
    # print(f1 is f2)
    

    17.__class__ 显示对象类(了解即可)

    '''
     __class__   对象.__class   查看对象的类
    '''
    class Animal:
        pass
    class Person(Animal):
        pass
    
    
    print(Person.__bases__) # 查看所有父类
    
    p=Person()
    
    print(p.__class__) # 查看对象的类
    
    

    18.__module__ 显示对象所属模块(了解即可)

    '''
     __modules__:看对象属于哪个模块
    '''
    from a import C
    
    obj = C()
    
    class Person():
        pass
    
    p=Person()
    print(obj.__module__)
    print(p.__module__)
    

    19.__slots__ 控制对象可以使用的属性

    '''
    __slots__:控制对象可以使用的属性
        -控制对象的属性(__dict__没了)
        -对象很多,对象属性比价少
        -本质:对象没有__dict__,统一被类的__dict__管理起来了,在dict内部使用更紧凑的存储方式,节约内存空间
    '''
    slots的使用
    
    对象只拥有name和age属性,其它放不进去
    class Person:
        __slots__ = ['name', 'age']  # 对象只拥有name和age属性
        # __slots__ = 'name'
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def __str__(self):
            return self.name
    
    
    p = Person('mzk', 18)
    print(p.name)
    
    p.sex = '男'
    

    20. __dict__ 对象属性

    '''
    __dict__ 查看对象中的属性
    '''
    
    class Person:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def __str__(self):
            return self.name
    
    
    p = Person('mzk', 18)
    p.sex = '男'
    print(p.__dict__)
    

    21.__next____iter__实现迭代器

    '''
    0 python中的for循环,是依赖于迭代的循环,不依赖于索引
        # 1  for i in 可迭代对象
        # 2 java中 for(i=0;i++;i<10){}
        # 3 python中 for循环的本质
        for i in 可迭代对象:会调用可迭代对象的__iter__,得到一个迭代器对象,不停的调用对象.__next__,得到一个值,直到抛出异常,for循环结束
    
    1 可迭代对象:对象有__iter__方法的,就是可迭代对象,调用__iter__,得到迭代器对象
    2 迭代器对象:既有__iter__又有__next__对象,就是迭代器对象
    
    __iter__和__next__
        -可迭代对象:对象有__iter__方法,这个对象就是一个可迭代对象
            -for循环本质就是在循环可迭代对象
            -for i in 对象(必须是可迭代对象)
            -for循环的本质:先调用对象的__iter__得到一个迭代器对象,然后调用迭代器对象的__next__ 方法,每次获得一个值(next这种获取值的方式不依赖于索           引,又叫迭代循环)
        -迭代器对象:
            -既有__iter__又有__next__的对象,就是迭代器对象
            -迭代器对象一定是可迭代对象
        -定制自己的可迭代对象
            -重写类中的__iter__和__next__
            -让它支持for循环直接循环(做出一种不依赖于索引取值的方式)
            -不停的return 值,当要停下时,抛出异常
    '''
    # 链式调用(跟语言无关)
    f.test().test2().test3()  # java,js:中广泛应用,设计程序的模式
    
    # 如何实现支持链式调用的对象
    class Foo:
        def __init__(self,name,age,sex):
            self.name=name
            self.age=age
            self.sex=sex
        def print_name(self):
            print(self.name)
            return self
    
        def print_age(self):
            print(self.age)
            return self
    
        def print_sex(self):
            print(self.sex)
            return self
    
    f=Foo('lqz',16,'男')
    f.print_name()
    f.print_sex()
    f.print_age()
    
    # 链式调用
    f.print_name().print_sex().print_age()
    
    # 1 给一个类,重写__iter__,__next__
    class Foo:
        def __init__(self):
            self.__x = 1
    
        def __iter__(self):
            return self
    
        def __next__(self):
            self.__x += 1
            return self.__x
    
    
    f = Foo()  # f就是一个可迭代对象,所以他就能够被for 循环
    
    # 自定制可迭代对象(自己写的对象,可以迭代,可以被for循环)
    for i in f:  # f.__iter__()----->每循环一次,调用一下 f.__next__()
        print(i)
    
    
    # 2 加入异常,for循环该对象,可以停止
    class Foo:
        def __init__(self,start,end):
            self.__start = start
            self.__end=end
    
        def __iter__(self):
            return self
    
        def __next__(self):
            self.__start+=1
            if self.__start>=self.__end:
                raise StopIteration('')  # 抛出一个异常,程序就结束了
            return self.__start
    
    for i in Foo(1,20):
        print(i)
    
    
    # 实现自己的range
    
    class MyRange:
        def __init__(self, start, end,step=1):
            self.__start = start
            self.__end = end
            self.__step = step
    
        def __iter__(self):
            return self
    
        def __next__(self):
            self.__start += self.__step
            if self.__start >= self.__end:
                raise StopIteration('')  # 抛出一个异常,程序就结束了
            return self.__start
    
    
    for i in MyRange(1, 20):
        print(i)
    
    
    # 斐波那契数列(通过__iter__和__next__实现)
    # 产生小于多少的斐波那契数列
    class Fib:
        def __init__(self, count):
            self.start = 0
            self.second = 1
            self.count = count
    
        def __iter__(self):
            return self
    
        def __next__(self):
            self.start,self.second=self.second,self.start+self.second
            if self.start >= self.count:
                raise StopIteration()
            return self.start
    
    
    for i in Fib(100):
        print(i)
    

    22.__enter____exit__上下文管理器

    '''
    
    with open() as f:
    
    with 上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明enter和exit方法
    
    优点:
    使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
    在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在exit中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处
    
    -with:上下文管理协议
        -with 对象 as f:   # 处理异常,不被with管理后,会清理资源
            代码
            代码
            
        -学过的 open,支持上下文管理协议
        -自定制一些对象,支持with管理
            -在类中重写 __enter__和__exit__
            -with 你写的对象 as f:   
                # 触发对象的__enter__ 方法执行,做一些初始化的工作,返回一个对象,会给f
                # 写代码,可以有异常,不用咱们手动处理异常
            -脱离了with的管理,会执行对象的__exit__,处理异常,资源清理工作
    '''
    #  __enter__ 和 __exit__
    
    class Open:
    
        def __enter__(self):
            print('enter执行了')
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('--',exc_type)
            print('--',exc_val)
            print('--',exc_tb)
            print('exit执行了')
    
    # with 对象,触发对象的 __enter__的执行
    # 顶格写,脱离了with,就会执行 __exit__
    with Open() as f:
        print('with上下文管理器')
        print(f)
    
    print('with结束了')
    
    
    class Open:
        def __str__(self):
            return 'f对象'
        def __enter__(self):
            print('enter执行了')
            return self
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('--',exc_type)
            print('--',exc_val)
            print('--',exc_tb)
            print('exit执行了')
    
    f=Open()
    # print(f)
    with  f as ff1:
        print('with上下文管理器')
        print(f)
        print(ff1) # enter的执行返回结果赋值给ff1
        print(f is ff1)
    
    print('with结束了')
    
    # exit的参数解释:exit()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行
    
    class Open:
        def __enter__(self):
            print('enter执行了')
            return self
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('异常类型',exc_type)
            print('异常信息',exc_val)
            print('追溯信息',exc_tb)
            print('exit执行了')
            return True  # 忽略掉异常,不爆红
    
    
    with Open() as f:
        f.witer()  # 没有
    
    print('结束')
    
    
    # 自定制一个open,打开文件
    
    class Open:
        def __init__(self, path, mod='rt', encoding='utf-8'):
            self.f = open(path, mod, encoding=encoding)
    
        def __enter__(self):
            return self.f
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            # 如果exc_tpye 不为空,表示with上下文内部出了错误,处理错误
            if exc_type:
                print('程序出错了,但是我处理好了,继续执行就好了')
                print(exc_val)
                return True
            # 没有错误,正常结束,资源清理工作(关闭文件)
            print('文件关闭了,放心好了')
            self.f.close()
        # def read(self):
        #     return self.f.read()
    
    
    # 后期会使用with ,处理数据库链接,redis链接
    
    with Open('a.txt', 'r', encoding='utf-8') as f:
        res = f.read()
        print(res)
    

    23.描述符

    '''
    描述符是什么?  
        就是一个新式类,实现了 __get__   __set__   __delete__ 其中一个,就被称为描述符类
    
    作用: 代理类属性
        class Person:
            name=描述符()   # 代理类属性
    
    描述符
        -新式类,__get__  __set__  __delete__
        -代理类的属性(一旦使用描述符代理,对象的__dict__中没有这个属性了)
        -数据描述符和非数据描述符(__get__)
                1.类属性
                2.数据描述符
                3.实例属性(对象属性)
                4.非数据描述符
                5.找不到的属性触发getattr()
         -通过描述符写了一个控制对象类型的案例
         -通过类装饰器+描述符实现对象类型控制
         -自定制 property
         -自定制 classmethod
         -自定制 staticmethod
    '''
    

    23.1 描述符小案例

    # 小案例,写一个描述符类(get,set,delete 参数都是固定的)
    class Foo:
        def __get__(self, instance, owner):
            print('触发get')
    
        def __set__(self, instance, value):
            print('触发set')
    
        def __delete__(self, instance):
            print('触发delete')
    
    
    f1 = Foo()
    f1.name = 'mzk'  # 不会触发 __set__的执行
    
    
    
    # 这种情况会触发
    
    class Str:
        def __get__(self, instance, owner):
            print('触发get')
    
        def __set__(self, instance, value):
            print('触发set')
    
        def __delete__(self, instance):
            print('触发delete')
    class People:
        age=19
        name = Str()
        def __init__(self, name):  # name被Str类代理,age被Int类代理
            self.name = name   # 触发描述符类,set的执行
    
    p=People('lqz')
    print(p.name) # 触发描述符类,get的执行,由于get返回None,所以打印出None
    

    23.2 描述符的分类

    描述符分类:两类
    '''
    数据描述符: 除了get以外的,还写了别的
    非数据描述符: 只有get,非数据描述符
    '''
    
    # 注意:
    '''
    描述符本身应该定义成新式类,被代理的类也应该是新式类
    必须把描述符定义成这个类的类属性,不能为定义到构造函数中
    要严格遵循该优先级,优先级由高到底分别是
        1.类属性
        2.数据描述符
        3.实例属性(对象属性)
        4.非数据描述符
        5.找不到的属性触发getattr()
    
    '''
    #举例
    class Person:
        def __init__(self):
            name='mzk'  # 在函数中定义了一个局部变量,用完就没了,不属于任何对象或类
            self.name='mzk'  # 属于对象,放在对象的名称空间中
            Person.name='mzk' # 属于类,放在类的名称空间中,对象也能使用的到
    
    p=Person()
    print(p.name) 
    print(Person.name)
    
    
    
    # 举例
    class Str:
        def __get__(self, instance, owner):
            print('触发get')
    
         def __set__(self, instance, value):
             print('触发set')
        
         def __delete__(self, instance):
             print('触发delete')
    
    
    class People:
        age = 19
        name = Str()
    
        def __init__(self, name):  # name被Str类代理
            self.name = name   # 触发描述符类,set的执行
            pass
    
    
    p=People('lqz')
    p.name='xxx'  # 放到对象的名称空间
    print(p.name)
    

    23.3描述符机制来实现类型限制功能

    # python是强类型动态语言
    '''
    1 强类型:不同类型直接不允许直接运算
    res=1+'sddd'  # python中不允许,类型转换后操作,js中允许,java:隐士类型转换,go语言强类型
    
    2 动态语言(静态语言)
        -动态语言---->解释型:js,php,python,不使用显示数据类型声明,声明变量不需要指定变量类型 var a='ddd'
        -静态语言---->编译型:java,c,go,   
    '''
    # 这种只是一个提示作用,不会限制(公司里通常这么做)
    def test(a:int,b:str)->int:
        print(a)
        print(b)
        return 18
    test('1',89)
    
    # 了解
    class Typed:
        def __init__(self, name, expected_type):
            self.name = name
            self.expected_type = expected_type
    
        def __get__(self, instance, owner):
            # print('get--->', instance, owner)
            if instance is None:
                return self
            return instance.__dict__[self.name]
    
        def __set__(self, instance, value):
            if not isinstance(value, self.expected_type):
                print('类型不匹配')
                return
    
            instance.__dict__[self.name] = value
            print(instance.__dict__)
    
        def __delete__(self, instance):
            print('delete--->', instance)
            instance.__dict__.pop(self.name)
    
    
    class People:
        name = Typed('name', str)
        age = Typed('age', int)
        salary = Typed('salary', float)
    
        def __init__(self, name, age, salary):
            self.name = name  # 触发Typed的 __set__,
            self.age = age
            self.salary = salary
    
    
    # 类People中的属性  name:str   age:int   salary:float
    
    p = People('mzk', '19', 1000.1)
    
    p.salary = '99'
    print(p.__dict__)
    

    24.类的装饰器

     # 1.类的装饰器:无参
    def decorate(cls):
        print('类的装饰器开始运行啦------>')
        return cls
    
    
    @decorate  # 类的装饰器    People=decorate(People)  仍旧是 People=People
    class People:
        pass
    
    
    People()  # 等同于 decorate(People)()--等同于-->People()
    
    
    # 2.类的装饰器:有参
    def typeassert(**kwargs):
        def decorate(cls):
            print('类的装饰器开始运行啦------>', kwargs)
            return cls
    
        return decorate
    
    
    @typeassert(name='mzk', age=19)  # 执行typeassert(name='mzk',age=19)--->decorate内层函数--->People=decorate(People)
    class People:
        pass
    
    
    p = People()
    
    
    # 3.使用类装饰器+描述符,实现对对象类型的控制
    class Typed:
        def __init__(self, name, expected_type):
            self.name = name
            self.expected_type = expected_type
    
        def __get__(self, instance, owner):
            print('get--->', instance, owner)
            if instance is None:
                return self
            return instance.__dict__[self.name]
    
        def __set__(self, instance, value):
            print('set--->', instance, value)
            if not isinstance(value, self.expected_type):
    
                print('类型不匹配怕')
                return
            instance.__dict__[self.name] = value
    
        def __delete__(self, instance):
            print('delete--->', instance)
            instance.__dict__.pop(self.name)
    
    
    def typeassert(**kwargs):
        def decorate(cls):
            print('类的装饰器开始运行啦------>', kwargs)
            
            for name, expected_type in kwargs.items():
                '''
                # 通过反射,把 name,Typed的对象,放到类中
                # People.'name'=Typed('name',str)
                # People.'age'=Typed('age',int)
                # People.'salary'=Typed('salary',float)
                本质就是如下
                name = Typed('name', str)
                age = Typed('age', int)
                salary = Typed('salary', float)
                '''
                setattr(cls, name, Typed(name, expected_type))
    
            return cls
    
        return decorate
    
    # 这种写法和下面的写法,效果,完全一致
    @typeassert(
        name=str, age=int, salary=float
    )  # 有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)
    class People:
        def __init__(self, name, age, salary):
            self.name = name    # 描述符类的 __set__
            self.age = age
            self.salary = salary
    
    
    # 这种写法跟上面的写法,效果,完全一致
    class People:
        name = Typed('name', str)
        age = Typed('age', int)
        salary = Typed('salary', float)
        def __init__(self, name, age, salary):
            self.name = name
            self.age = age
            self.salary = salary
    print(People.__dict__)  # {'name':Typed的对象(属性不一样),'age':Typed的对象,'salary':Typed的对象}
    
    
    # 实例化得到一个People对象
    p=People('mzk',19,100.1)
    
    p.age='15'  ## 描述符类的 __set__
    p.age=15
    print(p.__dict__)
    

    24.1 自制property装饰器

    class Lazyproperty:
        def __init__(self, func):
            self.func = func
    
        def __get__(self, instance, owner):
            # self.func是 Room类的area的内存地址,普通函数---需要传对象  area(instance)
            print('我被执行')
            return self.func(instance)  # 此时你应该明白,到底是谁在为你做自动传递self的事情
    
    
    class Room:
        def __init__(self, name, width, length):
            self.name = name
            self.width = width
            self.length = length
    
        @Lazyproperty  # area=Lazyproperty(area)--->Lazyproperty类的__init__执行,把area放到了Lazyproperty的对象中然后返回了-->area代指Lazyproperty的对象
        def area(self):
            return self.width * self.length
    
    
    r1 = Room('alex', 2, 3)
    # r1.area是Lazyproperty的对象,类是描述符类---->r1.area会触发描述符类的 __get__方法
    print(r1.area)  # 触发描述符类的 __get__ 的执行
    print(r1.area)  # 触发描述符类的 __get__ 的执行
    print(r1.area)  # 触发描述符类的 __get__ 的执行
    
    
    # 想缓存一下
    
    class Lazyproperty:
        def __init__(self, func):
            self.func = func
    
        def __get__(self, instance, owner):
            print('我被执行')
            res = self.func(instance)
            # instance是r1对象,向r1对象中放了一个 r1.'函数名字'=6
            instance.__dict__[self.func.__name__] = res
            # 相当于
            # instance.__dict__['area']=6
            return res
    
        # def __set__(self, instance, value):  # 加了它,变成数据描述符,查找顺序就不对了,不能加它
    
    
    class Room:
        def __init__(self, name, width, length):
            self.name = name
            self.width = width
            self.length = length
    
        @Lazyproperty  # area=Lazyproperty(area)--->Lazyproperty类的__init__执行,把area放到了Lazyproperty的对象中然后返回了-->area代指Lazyproperty的对象
        def area(self):
            return self.width * self.length
    
    
    r1 = Room('alex', 2, 3)
    # r1.area是Lazyproperty的对象,类是描述符类---->r1.area会触发描述符类的 __get__方法
    print(r1.__dict__)
    print(r1.area)  # 触发描述符类的 __get__ 的执行
    print(r1.__dict__)  # {'name': 'alex', 'width': 2, 'length': 3,area:6}
    
    print(r1.area)  # 直接先从对象自身找
    print(r1.area)  # 直接从对象自身找
    

    24.2 @classmethod类方法

    @classmethod
    class ClassMethod:
        def __init__(self, func):
            self.func = func
    
        def __get__(self, instance,owner):  #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身,
            def feedback():
                print('在这里可以加功能啊...')
                return self.func(owner)
    
            return feedback
    
    class People:
        def __init__(self,name):
            self.name=name
    
        @ClassMethod    # get_obj=ClassMethod(get_obj)--->ClassMethod的对象
        def get_obj(cls):
            print(cls)
            print('这是类的绑定方法')
    
    
    # People.get_obj   # 触发 描述符类的__get__,--->返回feedback的内存地址
    People.get_obj() # 其实执行的是feedback()--->self.func--->People类的get_obj方法,self.func(类)--->get_obj(People传入了)
    

    24.3 @staticmethod 静态方法

    @staticmethod
    class StaticMethod:
        def __init__(self, func):
            self.func = func
    
        def __get__(self, instance, owner):
            def feedback():
                print('在这里可以加功能啊...')
                return self.func()
    
            return feedback
    
    
    class People:
        def __init__(self, name):
            self.name = name
    
        @StaticMethod  # get_obj=StaticMethod(get_obj)--->StaticMethod的对象
        def get_obj():
            print('这是静态方法')
    
    
    # People.get_obj # --->触发__get__执行----feedback内存地址
    
    People.get_obj()  # feedback()
    
    # 带参数情况下
    class StaticMethod:
        def __init__(self, func):
            self.func = func
    
        def __get__(self, instance, owner):
            def feedback(*args, **kwargs):
                print('在这里可以加功能啊...')
                return self.func(*args, **kwargs)
    
            return feedback
    
    
    class People:
        def __init__(self, name):
            self.name = name
    
        @StaticMethod  # get_obj=StaticMethod(get_obj)--->StaticMethod的对象
        def get_obj(a, b):
            print(a, b)
            print('这是静态方法')
    
    
    # People.get_obj # --->触发__get__执行----feedback内存地址
    
    People.get_obj(7, 8)  # feedback()
    

    相关文章

      网友评论

          本文标题:面向对象 五

          本文链接:https://www.haomeiwen.com/subject/njwkqltx.html