• 装饰器和单例模式


      一、装饰器

      装饰器本质就是一个python函数,它可以让其他函数在不需要做任何代码变动的前提下,增加额外的功能,装饰器的返回值也是一个函数对象。装饰器的应用场景:插入日志,性能测试,事务处理,缓存等场景

      二、装饰器的形成过程

      现在有个需求,想让你测试这个函数的执行时间,在不改变这个函数的情况下。

      1,简单版装饰器

    import time
    def fun():
        time.sleep(2)
        print('你好啊')
    
    def test(f):
        def inner():
            start_time=time.time()
            f()
            end_time=time.time()
            print(end_time-start_time)
        return inner
    fun=test(fun)          #这个版本的虽然可以实现任何功能的测试,但每次函数执行前都得写上这一句很是麻烦
    fun()

      2,语法糖

    import time
    def fun():
        time.sleep(2)
        print('你好啊')
    
    def test(f):
        def inner():
            start_time=time.time()
            f()
            end_time=time.time()
            print(end_time-start_time)
        return inner
    @test           #相当于fun=test(fun),只是里面封装好了的,以后测试函数就不用写fun=test(fun),只需要在定义函数上面写上@test就行
    fun()

      上面的装饰器装饰的函数都没有参数,下面装饰一个带参数的装饰器。

      3,装饰带参数的函数

    import time
    def test(f):
        def inner(*args,**kwargs):
            start_time=time.time()
            f(*args,**kwargs)
            end_time=time.time()
            print(end_time-start_time)
        return inner
    @test
    def fun():
        time.sleep(2)
        print('你好啊')

      4,装饰带参数和有返回值的函数

    import time
    def test(f):
        def inner(*args,**kwargs):
            start_time=time.time()
            res=f(*args,**kwargs)
            end_time=time.time()
            print(end_time-start_time)
            return res
        return inner
    @test
    def fun1(a,s,d,f):
        return a+s+d+f

       对于上面的装饰器来说,如果我要看函数的信息,比如fun.__doc__查看函数注释的方法,fun.__name__查看函数名,由于加上装饰器后fun=inner,所以结果会有错误。下面解决

      5,可以查看函数信息的装饰器

    import time
    from functools import wraps
    def test(f):
        @wraps(f)
        def inner(*args,**kwargs):
            start_time=time.time()
            res=f(*args,**kwargs)
            end_time=time.time()
            print(end_time-start_time)
            return res
        return inner
    @test
    def fun1(a,s,d,f):
        return a+s+d+f

      三、装饰器主要功能和固定结构

      1,固定写法

    def timer(func):
        def inner(*args,**kwargs):
            '''执行函数之前要做的'''
            re = func(*args,**kwargs)
            '''执行函数之后要做的'''
            return re
        return inner

      2,可以查看函数信息的

    from functools import wraps
    
    def deco(func):
        @wraps(func) #加在最内层函数正上方
        def wrapper(*args,**kwargs):
            return func(*args,**kwargs)
        return wrapper

      四、带参数的装饰器(是装饰器袋参数)

      比如说之前要求你为10个函数加上装饰,你得在每个函数上面写上@;一个月之后,不需要装饰器了,你又要把每个函数上的@给去掉;没过多久又让你加上装饰器;反复添加,反复删除,这可是10万个函数,很庞大的工程。其实现在给装饰器加一个参数,很轻松就解决问题。

    import time
    def outer(flg):
        def test(f):
            def inner(*args,**kwargs):
                if flg:
                    start_time=time.time()
                res=f(*args,**kwargs)
                if flg:
                    end_time=time.time()
                    print(end_time-start_time)
                return res
            return inner
        return test
    flg=True       #flg为True时加上装饰器,为False时不加装饰器
    @outer(flg)
    def fun1(a,s,d,f):
        return a+s+d+f
    r=fun1(2,3,4,5)
    print(r)

      五、多个装饰器装饰一个函数

    def wrapper1(func):  func=f
        def inner1():
            print('wrapper1 ,before func')
            func()
            print('wrapper1 ,after func')
        return inner1
    
    def wrapper2(func):  func=inner1
        def inner2():
            print('wrapper2 ,before func')
            func()
            print('wrapper2 ,after func')
        return inner2
    
    @wrapper2     #f=wrapper2(f),现在后面的f已经变为inner1,f=inner2
    @wrapper1     #f=wrapper1(f),f=inner1
    def f():
        print('in f')
    
    f()      #此时f为inner2
    所以结果为:
    wrapper2,before func
    wrapper1,before func
    in f
    wrapper1,after func
    wrapper2,after func

      六、单例模式

      单例模式就是一个类只能产生一个对象,就算产生多个对象,多个对象都是指向同一地址

      方法一:用__new__

    class Person:
        __status=None
        def __new__(cls, *args, **kwargs):
            if not Person.__status:
                Person.__status=object.__new__(Person)
            return Person.__status
    ob1=Person()
    ob2=Person()
    print(ob1)
    print(ob2)

       方法二:使用模块

      python的模块就是天然的单例模式,因为模块在第一次导入时,会生成.pyc文件,当第二次导入时,就会直接加载.pyc文件,而不会再次执行模块代码。因此,我们只需要把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。如果我们真的想要一个单例类,可以如下:

      mysingleton.py

    class Singleton(object):
        def foo(self):
            pass
    singleton = Singleton()

      要使用时,直接在其他文件中导入此文件中的对象,这个对象就是一个单例模式的对象

    from mysingleton.py import singleton

      方法三:使用装饰器

    def Singleton(cls):
        _instance = {}
    
        def _singleton(*args, **kargs):
            if cls not in _instance:
                _instance[cls] = cls(*args, **kargs)
            return _instance[cls]
    
        return _singleton
    
    
    @Singleton
    class A(object):
        a = 1
    
        def __init__(self, x=0):
            self.x = x
    
    
    a1 = A(2)
    a2 = A(3)     #后面不管你给的参数是多少,都不会再执行了,只会把a1对象返回给他

      方法四:使用类

    class Singleton(object):
    
        def __init__(self):
            pass
    
        @classmethod
        def instance(cls, *args, **kwargs):
            if not hasattr(Singleton, "_instance"):
                Singleton._instance = Singleton(*args, **kwargs)
            return Singleton._instance

      这种情况下,含有io的多线程就会有问题,当前面的对象还没创建成功,后面的线程就开始,not hasattr(Singleton,'_instance')也为真,所以,第二个线程也会重新创建一个对象,从而就不是一个对象,就不是单例模式。解决办法是加锁。加锁的部分串行执行,没加锁部分并发执行。

    import time
    import threading
    class Singleton(object):
        _instance_lock = threading.Lock()
    
        def __init__(self):
            time.sleep(1)
    
        @classmethod
        def instance(cls, *args, **kwargs):
            with Singleton._instance_lock:
                if not hasattr(Singleton, "_instance"):
                    Singleton._instance = Singleton(*args, **kwargs)
            return Singleton._instance
    
    
    def task(arg):
        obj = Singleton.instance()
        print(obj)
    for i in range(10):
        t = threading.Thread(target=task,args=[i,])
        t.start()
    time.sleep(20)
    obj = Singleton.instance()
    print(obj)

      方法五:基于metaclass方式实现

      相关知识:

    """
    1.类由type创建,创建类时,type的__init__方法自动执行,类() 执行type的 __call__方法(类的__new__方法,类的__init__方法)
    2.对象由类创建,创建对象时,类的__init__方法自动执行,对象()执行类的 __call__ 方法
    """
    
    class Foo:
        def __init__(self):
            pass
    
        def __call__(self, *args, **kwargs):
            pass
    
    obj = Foo()
    # 执行type的 __call__ 方法,调用 Foo类(是type的对象)的 __new__方法,用于创建对象,然后调用 Foo类(是type的对象)的 __init__方法,用于对对象初始化。
    
    obj()    # 执行Foo的 __call__ 方法    

      元类的使用:

    class SingletonType(type):
        def __init__(self,*args,**kwargs):
            super(SingletonType,self).__init__(*args,**kwargs)
    
        def __call__(cls, *args, **kwargs): # 这里的cls,即Foo类
            print('cls',cls)
            obj = cls.__new__(cls,*args, **kwargs)
            cls.__init__(obj,*args, **kwargs) # Foo.__init__(obj)
            return obj
    
    class Foo(metaclass=SingletonType): # 指定创建Foo的type为SingletonType
        def __init__(self,name):
            self.name = name
        def __new__(cls, *args, **kwargs):
            return object.__new__(cls)
    
    obj = Foo('xx')

      实现单例模式:

    import threading
    
    class SingletonType(type):
        _instance_lock = threading.Lock()
        def __call__(cls, *args, **kwargs):
            if not hasattr(cls, "_instance"):
                with SingletonType._instance_lock:
                    if not hasattr(cls, "_instance"):
                        cls._instance = super(SingletonType,cls).__call__(*args, **kwargs)
            return cls._instance
    
    class Foo(metaclass=SingletonType):
        def __init__(self,name):
            self.name = name
    
    
    obj1 = Foo('name')
    obj2 = Foo('name')
    print(obj1,obj2)
  • 相关阅读:
    jmeter接口测试二
    jmeter 插件入口
    Python正则匹配中的最小匹配和贪婪匹配
    python中的url编码和解码(encode与decode)乱码
    python2.7+pyqt+eric基本控件操作(制作界面化程序)
    python2.7+PyQt4+eric6 界面开发环境配置
    centos配置静态ip地址
    分片,步长,索引
    我看过的几本书籍
    软件测试工程师的成长之路(个人看法)
  • 原文地址:https://www.cnblogs.com/12345huangchun/p/10158463.html
Copyright © 2020-2023  润新知