• 【python 第9日】上下文 类装饰器 元类 属性描述符扩展


    with 上下文管理

    包含__enter__/__exit__的模块

    class WithTest:
        def __init__(self, name):
            print("这里是初始化")
            self.name = name
    
        def __enter__(self):
            print("这里是%s的enter" % self.name)
            return self
        def __exit__(self, exc_type, exc_val, exc_tb):
            print("这里是%s的exit" % self.name)
            print("exc_type", exc_type)
            print("exc_val", exc_val)
            print("exc_tb", exc_tb)
            return True
    
    
    with WithTest("zgl") as f:
        print(f.name)
        print(sss)
        print("---------->")
    输出如下
    这里是初始化
    这里是zgl的enter
    zgl
    这里是zgl的exit
    exc_type <class 'NameError'>
    exc_val name 'sss' is not defined
    exc_tb <traceback object at 0x003FDE40>
    • 首先Withtest("zgl"),如果这里出错,上下文管理器是管理不到的,始终会报错,除非再外层加try/except
    • 然后进入__enter__, 要return self 否则f接受到的是None类型
    • 然后开始执行,上下文里面的内容,对于里面的变量赋值,外层函数也可以用,比如a =1 在最外层也是可以用的
    • 如果报错,立刻进入__exit__, exc_type返回的异常类型, exc_val返回的是异常信息, exc_tb返回的是异常栈
    • 如果__exit__返回True,则不报错,进行控制,编译器不发出异常,如果不返回True,则编译器仍然报错
    • 之后不在执行后面的代码,推出上下文管理

    类装饰器

    类装饰器定义

    在类外加上装饰器,使其增加附加功能,返回类

    #给每个类添加一个数据属性和一个函数属性
    def Decorator(obj):# print(School.__dict__)
        #添加数据属性
        obj.addr = "浙江省杭州市"
        def price():
            pass
        #添加函数属性
        obj.price = price
        return obj              #一定返回的是类本身
    
    @Decorator          #相当于执行 School = Decorator(School)
    class School():
        def __init__(self,name,price):
            self.name =name
            self.price =price
    
    #打印类的属性字典
    print(School.__dict__)
    #

    {'__module__': '__main__', '__init__': <function School.__init__ at 0x035D8AE0>, '__dict__': <attribute '__dict__' of 'School' objects>, '__weakref__': <attribute '__weakref__' of 'School' objects>, '__doc__': None, 'addr': '浙江省杭州市', 'price': <function Decorator.<locals>.price at 0x035D8588>}

    带参数的类装饰器

     添加可变参数,易于管理,

    #给每个类添加一个可变的数据属性
    def Decorator(**kwargs):
        def add(obj):
            "添加数据属性"
            # print('调用外部函数的**kwargs',kwargs)
            for key,val in kwargs.items():
                # 添加数据属性
                setattr(obj,key,val)       #不可以用obj.__dict__ 的修改操作,是不可变更字典类型
            return obj
        # print("外部传入的参数为:",kwargs)
        return add
    
    @Decorator(addr = "浙江省杭州市",name ="浙江大学")          #执行顺序:1.运行Decorator函数,先打印外部的传入的参数,返回add函数名;2.再执行School = add(School)
    class School():
        def __init__(self,price):
            self.price =price
    
    @Decorator(addr = "湖北省",price =12000)
    class School1():
        pass
    
    print(School.__dict__)
    print(School1.__dict__)

    装饰器类

      和上面不同,是把类作为装饰器,相当于类的初始化

    做一个Property类,把类中的函数变成静态变量

    class  Property:
    
    
        def __init__(self, func):
            self.func = func
    
        def __get__(self, instance, owner):
            # print(self, instance, owner)
            # self  调用对象,也就是score实例,   instance是test实例           owner归属的类Test
            if not instance:
                return self               #如果类调用返回对象
            return  self.func(instance)   #实例调用这
    
        def __set__(self, instance, value):
            # print(self, instance, value)
            instance.__dict__[ self.func.__name__ ] = value       #修改实例test字典
    
    class Test:
    
        def __init__(self,  mark, discount):
            self.mark  = mark
            self.discount = discount
        @Property         #实例f = Property(score)  这里是类,数据描述符
        def score(self):
            return self.mark * self.discount
        @property
        def score1(self):
            return self.mark * self.discount
    
    test = Test(10, 0.8)
    # print(Test.score1)           #<property object at 0x0076AAE0>
    # print(Test.score)           #<__main__.Property object at 0x00766090>
    # print(test.score1)           #8.0
    # print(test.score)           #8.0
    test.score = 1
    print(test.__dict__)
    print(test.__dict__["score"])
    print(test.score)                                #8.0 要想使用__dict__的,必须不加__set__, 在__get__里面修改    set(obj, name, value)  

     做一个classmethod类,把类中的方法变成类方法

    本来想通过__call__实现,但是最后,不会获取函数的外层类,所以失败,如果只写Test,那太固定了,没意思,希望厉害的人回复,怎么获取函数的外层类,不是函数的类
    class  Classmethond:
    
        def __init__(self, func):
            self.func = func
    
        def __get__(self, instance, owner):
            def wrap(*args, **kwargs):
                return self.func(owner, *args, **kwargs)
            return wrap
        # def __call__(self, *args, **kwargs):
        #         # print(self.cla , self, *args, **kwargs)
        #         return self.func(self.cla , *args, **kwargs)     #本来想通过__call__实现,但是最后,不会获取函数的外层类,所以失败,如果只写Test,那太固定了,没意思
    
    
    
    class Test:
        x = 1
    
        def __init__(self,  mark, discount):
            self.mark  = mark
            self.discount = discount
    
        @Classmethond         #实例f = Property(score)  这里是类,数据描述符
        def score(cls):
            print(cls, cls.x)
    
        @classmethod
        def score1(cls):
            print(cls, cls.x)
    
    Test.score1()   #<class '__main__.Test'> 1
    Test.score()   #<class '__main__.Test'> 1
    
    test  = Test(1, 0.8)
    test.score1()   #<class '__main__.Test'> 1
    test.score()    #<class '__main__.Test'> 1

     做一个staticmethod 类,把类中的方法变成静态方法

    class  Staticmethod:
    
        def __init__(self, func):
            self.func = func
    
        def __get__(self, instance, owner):
    
            return self.func
    
        # def __call__(self, *args, **kwargs):
        #         # print(self.cla , self, *args, **kwargs)
        #         return self.func(self.cla , *args, **kwargs)
    
    
    
    class Test:
        x = 1
    
        def __init__(self,  mark, discount):
            self.mark  = mark
            self.discount = discount
    
        @Staticmethod
        def score():
            print("静态方法")
    
        @staticmethod
        def score1():
            print("这是静态方法1")
    
    
    # Test.score1()
    Test.score1()                  #这是静态方法1
    Test.score()                    #静态方法
    
    test = Test(10, 0.8)    
    test.score1()                   #这是静态方法1
    test.score()                    #静态方法

     

     元类   type 自定义(类名,父类,属性)

    元类定义

    元类就是用来创建类的“东西”。你创建类就是为了创建类的实例对象,不是吗?但是我们已经学习到了Python中的类也是对象。好吧,元类就是用来创建这些类(对象)的,元类就是类的类,

    因此,元类就是创建类这种对象的东西。如果你喜欢的话,可以把元类称为“类工厂”(不要和工厂类搞混了:D) type就是Python的内建元类,当然了,你也可以创建自己的元类。

    type(类名, 父类的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))
    class Foo(object):
        __metaclass__ = something…
    […]

      Python做了如下的操作:

      Foo中有__metaclass__这个属性吗?如果是,Python会在内存中通过__metaclass__创建一个名字为Foo的类对象(我说的是类对象,请紧跟我的思路)。如果Python没有找到__metaclass__,它会继续在Bar(父类)中寻找__metaclass__属性,并尝试做和前面同样的操作。如果Python在任何父类中都找不到__metaclass__,它就会在模块层次中去寻找__metaclass__,并尝试做同样的操作。如果还是找不到__metaclass__,Python就会用内置的type来创建这个类对象。

    现在的问题就是,你可以在__metaclass__中放置些什么代码呢?答案就是:可以创建一个类的东西。那么什么可以用来创建一个类呢?type,或者任何使用到type或者子类化type的东东都可以。

    抽象函数

    实现方式有3种

    import abc
    class Abs(metaclass=abc.ABCMeta):
    # class Abs(object):
    #     __metaclass__ = abc.ABCMeta             #这个命名方式不行,不知道为什么 是python3 新修改的么
    
        @abc.abstractmethod
        def abstar(self):
            pass
    print(Abs.__dict__)
    abcd = Abs()

    自定义元类

    class UpperAttrMetaclass(type):
        def __new__(cls, name, bases, dct):
            attrs = ((name, value) for name, value in dct.items() if not name.startswith('__'))
            uppercase_attr = dict((name.upper(), value) for name, value in attrs)
            return super(UpperAttrMetaclass, cls).__new__(cls, name, bases, uppercase_attr)
    
    class A(metaclass= UpperAttrMetaclass):
        pass

    就是这样,除此之外,关于元类真的没有别的可说的了。使用到元类的代码比较复杂,这背后的原因倒并不是因为元类本身,而是因为你通常会使用元类去做一些晦涩的事情,依赖于自省,控制继承等等。确实,用元类来搞些“黑暗魔法”是特别有用的,因而会搞出些复杂的东西来。但就元类本身而言,它们其实是很简单的:

      1)   拦截类的创建

      2)   修改类

      3)   返回修改之后的类

    属性描述符、property(@setter,@deleter)和__setattr__

    我这是么理解的,

    • property用于的是少数属性的控制,1个2个
    • 属性描述符用于的是某些的相同属性的控制   具有相同属性
    • __setattr__ 全部属性的控制,当然也可以部分,不够代码结构复杂

    __get__  __getAttribute__ ___getattr__  __getitem__ 顺序

    •  __get__使用在数据描述符中,暂时不考虑,后面三个的优先级顺序为__getitem__ > __getAttribute__ >___getattr__  
    • 对类属性A.x的获取不触发,对a.x的获取才触发
    • 其他set  del的顺序和这个一致,
    • __del__是析构函数是在实例删除时候触发的
    class A:
        data = 1
        def __init__(self,  id):
            self.id  = id
        def __getattribute__(self, item):
            print("这里是getattribute",item)
            # raise AttributeError
            # print("这是getattribute后期")
            # return object.__getattribute__(self, item)
            return super(A, self).__getattribute__(item)
        def __getattr__(self, item):
            print("这里是getattr")
    
        def __getitem__(self, item):
            print("这里是getitem", item)
            # raise AttributeError
            return self.__dict__[item]
            # return object.__getattribute__(self, item)
            # if item in self.__dict__:
            #     return self.__dict__[item]
            # else:
            #     raise ValueError
    a = A("11")
    print(a.__dict__)
    #这里是getattribute __dict__
    #{'id': '11'}
    print(a.data)
    #这里是getattribute data
    #1
    print(a.x)
    #这里是getattribute x
    #这里是getattr
    #None
    print(a["x"])
    #这里是getitem x
    #这里是getattribute __dict__
    ###KeyError: 'x'
    print(a["data"])
    #这里是getitem data
    #这里是getattribute __dict__
    #KeyError: 'data'
    print(a["id"])
    #这里是getitem id
    #这里是getattribute __dict__
    #11

    支付宝     
    您的资助是我最大的动力!
    金额随意,欢迎来赏!
    微信

    如果,您希望更容易地发现我的新博客,不妨点击一下绿色通道的关注我

    如果,想给予我更多的鼓励,求打       付款后有任何问题请给我留言!!!

    ------------------------------------------------------------------------------------------
    作者:【周sir】
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    Executors源码之线程池
    Java序列化对字段名的影响
    Spring Cloud Alibaba(二)
    Security版本冲突,老版本共用服务接入新版本服务
    记一次虚拟机崩溃事件和解决方法(CentOS7)
    vue-cli 项目构建学习笔记(Vue3)
    IDEA插件-IDE Eval Reset
    Docker的学习
    Spring Security的学习
    Spring MVC框架的设计理念
  • 原文地址:https://www.cnblogs.com/zhouguanglu/p/10247569.html
Copyright © 2020-2023  润新知