zoukankan      html  css  js  c++  java
  • 通过类的装饰器以及各种单例模式(修复版本)。

    今日框架又用到了元编程,故再来复习一遍。

    又学了半个小时,对装饰器的理解感觉又上了一个阶段,装饰器真的是一个牛逼的工具,不改变原函数的基础上想如何折腾原函数,就如何折腾原函数。

    @装饰器名称,这个语法糖都知道了,其实@后面的变量名是个可调用的参数就可以,函数可以变调用,当然类也可以被调用,callable函数能够测试该对象能否被调用,粗糙的讲后面有()这括号的就算能被调用

    我下面下一个简单的装饰器模块,其实装饰器能够装饰所有能被调用的对象,所有后面会有类装饰器的单例,和函数装饰器的单例。

    class Demo:
    
        def __init__(self, func):
            self.func = func
    
        def __call__(self, *args, **kwargs):
            print('我进来了')
            res = self.func(*args, **kwargs)
            print('执行完毕,我出去了')
            return res
    
    
    @Demo
    def foo(x, y):
        return x + y
    
    
    print(foo.__dict__)
    print('_' * 80)
    print(foo(3, 4))
    
    {'func': <function foo at 0x10db3dc20>}
    ________________________________________________________________________________
    我进来了
    执行完毕,我出去了
    7
    

    整个简单的类装饰器,能看懂执行,我后面写的就不用看了,其实还是跟前面讲的一样,foo已经没有变,只不过foo函数将作为实例化的参数,传给Demo类。

    传参进去以后需要执行这个对象,由于普通的对象没有__call__方法,故在装饰器类中需要定义__call__,使该类的实例,称为一个可调用的对象

    我们后面执行foo(3, 4)的时候,其实是直接用对象调用方法,为了能够让对象直接被调用,

    在类里面加入了__call__方法,让实例可以被调用。

    唯一的遗憾是,我这里没地方找到functools的wrap使用,就是无法保证了被装饰的对象的原来属性。

    既然是这样,我后面就可以直接写三个单例来练手了,最后来一个带参数的类装饰器,看它是如何将原函数传入实例里面的。(学到后面感觉类只不过就是一个高级版本的闭包函数)

    第一种:通过__new__来实现,单例。

    前面已经说过了,类的实例过程,首先调用__calll__方法,然后__new__创建对象,最后__init__初始化对象属性,返回给变量。

    class Dli:
        def __init__(self, x, y):
            self.x = x
            self.y = y
    
        def __new__(cls, *args, **kwargs):
            if not hasattr(cls, '_instance'):          # 如果没有这个属性
                cls._instance = super().__new__(cls)   # 给类一个对象属性,创建实例的时候,其实这个就是调用父类object.__new__(Dli)
            return cls._instance
    
    
    d1 = Dli(1, 2)
    d2 = Dli(7, 8)
    print(d2 is d1)
    print(d2.x, d1.x)
    
    7
    True
    7 7
    

     Python中没有锁定属性的对象,可以里面随便添加属性,Python就是这么没有节操,越学到后面,感觉越没节操,现在开始基础学JAVA,感觉Java规矩太多了。

    第二种,通过函数装饰器来做单例

    def doo(cls):  # 装饰的是个类,就写cls,其实写什么无所谓,就传参用而已
        _instance = {}
        # @functools.wraps(cls)
        def wrap(*args, **kwargs):
            if cls not in _instance:
                _instance[cls] = cls(*args, **kwargs)
            return _instance[cls]
    
        return wrap
    
    # 这样写也可以不要外部定义字典,直接通过函数属性赋值
    def doo(cls):  # 装饰的是个类,就写cls,其实写什么无所谓,就传参用而已
    # @functools.wraps(cls)
    doo._instance = None
    def wrap(*args, **kwargs):
    if doo._instance is None:
    doo._instance = cls(*args, **kwargs)
    return doo._instance

    return wrap

    @doo class Demo: def __init__(self, x, y): self.x = x self.y = y print() print(Demo) d1 = Demo(1, 2) d2 = Demo(7, 8) print(d2.x, d1.x, d1 is d2)
    <function doo.<locals>.wrap at 0x1095f4c20>
    1 1 True
    

    通过输出可以看到,用一个函数内部的字典接收实例对象,如果已经存在实例了,后面产生的实例全部参考指向第一个。

    functools.wrap还是可以使用的,能够基本保证类的内部一些属性。

    第三,写两个类,用一个类装饰另外一个类来产生一个单例。

    class Warp:
        def __init__(self,cls):
            self.cls = cls
    
        def __call__(self, *args, **kwargs):
            if not hasattr(Warp, '_instance'):
                # 产生实例直接放到类属性,更加方便
                Warp._instance = self.cls(*args, **kwargs)
            return Warp._instance
    
    @Warp
    class Demo:
        '''This is Demo docstring'''
        def __init__(self, x, y):
            self.x = x
            self.y = y
    
    d3 = Demo(1, 5)
    d4 = Demo(6, 9)
    print(d3 is d4)
    print(d3.x, d4.x)
    

     上下两个类,上面哪个类装饰下面这个类,用自身的类属性来保存及返回实例。

    最后说一个带参数的类装饰器如何使用,其实用类的装饰器还是非常强大的,因为可以直接调用对象里面的很多方法。本来还想试一下有没有类的多层装饰器,看后续,有时间就写个玩玩,应该跟函数的差不多。

    class NewWarp:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def __call__(self, func):
            # 这个在call里面传递func进来,而且竟然可以用@functools.wraps保护原函数属性
            @functools.wraps(func)
            def warp(*args, **kwargs):
                # 由于传入的产生是对象属性,哪里使用都很方便。
                print(f'My name is {self.name}, age is {self.age}')
                res = func(*args, **kwargs)
                return res
            return warp
    
    
    @NewWarp('sidian', 5)
    def foo(x, y):
        return x + y
    
    exec('print();' * 3)
    print(foo.__name__)
    print(foo(3, 5))
    
    foo
    My name is sidian, age is 5
    8
    

     从上面看出,带参数的装饰器类,传入的func地址要注意,是在__call__的地方传入。

    最后我尝试写一个,多类装饰器的模式。

    class Demo2:
    
        def __init__(self, func):
            print(self.__class__.__name__)
            self.func = func
    
        def __call__(self, *args, **kwargs):
            print('demo2_call我进来了')
            res = self.func(*args, **kwargs)
            print('demo2_call执行完毕,我出去了')
            return res
    
    class Demo1:
    
        def __init__(self, func):
            print(self.__class__.__name__)
            self.func = func
    
    
        def __call__(self, *args, **kwargs):
            print('demo1_call我进来了')
            res = self.func(*args, **kwargs)
            print('demo1_call执行完毕,我出去了')
            return res
    
    @Demo2
    @Demo1
    def foo(x, y):
        return x + y
    
    # print(foo.__name__)
    print(foo.__dict__)
    print('_' * 80)
    print(foo(3, 4))
    
    Demo1
    Demo2
    {'func': <__main__.Demo1 object at 0x10e3e53d0>}
    ________________________________________________________________________________
    demo2_call我进来了
    demo1_call我进来了
    demo1_call执行完毕,我出去了
    demo2_call执行完毕,我出去了
    7
    

     从输出来看,逻辑跟多层的函数装饰器差不多,首先也是进行方法调用,这里是类,就是先进行初始化

    foo = Demo1(foo)

    然后传递给Demo2

    foo = Demo2(Demo1(foo))

    所以执行f00就是先执行Demo2的实例对象,Demo2里面的func就是Demo1(foo)实例,执行Demo1(foo)实例,就是执行foo,然后通过return层层返回出来。

    总算写完了,感觉脑细胞死了好多。

    (已进行修改)

    我的单例写的不是很好:

    先复制一个参考链接

    https://www.cnblogs.com/xt12321/p/10784478.html

    # ----------------------------------------------------------------------
    # 方式一:模块
    
    class Songs():
        pass
    
    # s1 = Songs()
    # s2 = Songs()
    # print(s1, s2)
    
    # 对外提供的对象
    song = Songs()
    
    
    # ----------------------------------------------------------------------
    # 方式二:类方法
    # 单例赋值在
    class Songs():
        __instance = None
        @classmethod
        def getInstance(cls):
            # 对象没有创建返回,有直接返回
            if cls.__instance == None:
                cls.__instance = cls()
            return cls.__instance
    
    
    # 约定别用 类名() 来实例化对象,用类方法来获取唯一对象
    # s1 = Songs()
    # s2 = Songs()
    # print(s1, s2)
    
    s1 = Songs.getInstance()
    s2 = Songs.getInstance()
    print(s1, s2)
    
    # ----------------------------------------------------------------------
    # 方式三:类初始化时对标志进行判断
    
    class Songs:
        __instance = None
        def __new__(cls, song_name, *args, **kwargs):
            if cls.__instance == None:
                cls.__instance = super().__new__(cls)   #用super更加好。
                cls.__instance.song_name = song_name
            return cls.__instance
    
        def change_song(self, song_name):
            self.song_name = song_name
    
    s1 = Songs('菊花爆满山')
    s2 = Songs('感觉身体被掏空')
    print(s1.song_name, s2.song_name)  # 菊花爆满山  菊花爆满山
    s2.change_song('感觉身体被掏空')
    print(s1.song_name, s2.song_name)  # 感觉身体被掏空 感觉身体被掏空
    
    # ----------------------------------------------------------------------
    # 方式四: 装饰器装饰类
    
    def outer(cls):
        _instance = None
        def inner(*args, **kwargs):
            nonlocal _instance
            if _instance == None:
                _instance = cls(*args, **kwargs)
            return _instance
        return inner
    
    @outer  # Songs = outer(Songs)
    class Songs:
        pass
    
    
    s1 = Songs()
    s2 = Songs()
    print(s1, s2)
    
    
    # ----------------------------------------------------------------------
    # 方式五: 元类实现
    class SingleMeta(type):
        __instance = None
        def __call__(cls, *args, **kwargs):
            if SingleMeta.__instance == None:
                SingleMeta.__instance = super().__call__(*args, **kwargs)
            return SingleMeta.__instance
    
    
    class Songs(metaclass=SingleMeta):
        def __init__(self):
            pass
        pass
    
    
    s1 = Songs()
    s2 = Songs()
    print(s1, s2)
    

    回头来看,全部拿下。准备把元类全部拿下,元类其实也没那么抽象。

  • 相关阅读:
    看懂SqlServer查询计划
    jQuery 插件autocomplete自动完成应用(自动补全)(asp.net后台)
    MVC Html.AntiForgeryToken() 防止CSRF攻击
    iOS开发UI篇—transframe属性(形变)
    iOS开发UI基础—手写控件,frame,center和bounds属性
    iOS开发UI篇—Button基础
    iOS开发UI篇—UITableviewcell的性能优化和缓存机制
    iOS开发UI篇—UITableview控件基本使用
    iOS开发UI篇—UITableview控件简单介绍
    iOS开发UI篇—推荐两个好用的Xcode插件(提供下载链接)
  • 原文地址:https://www.cnblogs.com/sidianok/p/11945749.html
Copyright © 2011-2022 走看看