第六种方式,python使用cached_property缓存装饰器和自定义cached_class_property装饰器,动态添加类属性(三),selnium webdriver类无限实例化控制成单浏览器。

使用 from lazy_object_proxy.utils import cached_property,使用这个装饰器。

由于官方的行数比较少,所以可以直接复制出来用自己的。

class cached_property(object):         # 这是官方的
    def __init__(self, func):
        self.func = func

    def __get__(self, obj, cls):
        print (obj,cls)
        if obj is None:

return self value = obj.__dict__[self.func.__name__] = self.func(obj) return value class cached_class_property(object): # 这是修改的 def __init__(self, func): self.func = func def __get__(self, obj, cls): print (obj,cls) if obj is None:

return self value = cls.__dict__[self.func.__name__] = self.func(obj) return value class A(): @cached_property # 使用这个实例属性的缓存装饰器,然后替换为property装饰器和自定义的cached_class_property测试 def result(self): print ('compute result') return 1+2 a1 = A() a1.result # 第一次 a1.result # 第二次 a2 = A() a2.result # 第三次

1、如果使用cached_property这个装饰器,是把result方法的结果绑定到实例的字典中,所以一共打印了两次 'compute result' ,分别是第一次 第三次打印的,第二次因为a1这个实例的字典中有result这个属性了,所以不执行这个方法了。

2、 如果使用property装饰器,毫无疑问就是会打印三次 'compute result' 

3、下面来个更激进一点的装饰器,cached_property这个实例属性装饰器虽然能缓存结果,但是新的实例还是会调用这个方法进行1+2计算。做一点小改变,把obj.__dict__换成cls.__dict__,也就是改成cached_class_property,这样就是类属性缓存器,这样就只会打印一次'compute result'了,perfect了,利用这个对基类添加动态属性太好了,好处包括:

1)不像之前的元类和那种装饰器写法,动态添加进来的属性和方法,使用时候不能在pycharm自动补全提示,说的补全指的是打 self.r 就会补全出self.result,打self.result.就会补全出一个整形int类的所有方法了,因为1+2等于3,3是整形。

2、相比于基类在__init__或者new中添加方法,如果动态添加属性的类作为基类被继承,当子类也写了init方法时候,必须显式手动去调用父类的init方法。

4、这只是举个例子,1 + 2这么简单的东西不需要使用缓存的,如果调用result属性不频繁是没必要使用这个装饰器的,但有的对象创建是经过了很久的运行步骤的,当频繁使用这个属性,造成很多没必要的计算,或者有的结果是一个io过程得到的,更要使用这样装饰器了。用这个作为selenium的webdriver包装类的driver属性也很不错,加上他,就可以十分任性的随意实例化你写的webdriver类了,不会每实例化一次都新弹出一次浏览器。  从包的名字就可以看出缓存器也叫延迟加载,如果直接写成类属性那么还没开始使用这个属性就已经执行运算了,比如直接写成类下面作为类属性,还没准备需要使用浏览器,就早早的弹出个浏览器出来,没什么必要。

5、把上一篇的webdriver改一下,控制为单浏览器就是这样了。

# coding:utf-8

import logging
import unittest
from selenium import webdriver


class DriverWrapper():
    def __init__(self):
        
        self.logger = logging.getLogger(self.__class__.__name__)
        self.logger.setLevel(logging.DEBUG)
        stream_handler = logging.StreamHandler()
        stream_handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(filename)s - %(lineno)d - %(levelname)s - %(message)s', "%Y-%m-%d %H:%M:%S"))
        self.logger.addHandler(stream_handler)
    
@cached_class_property ## 添加一个类属性缓存装饰器
def driver(self): self.driver = webdriver.Chrome() def open(self, url): self.driver.get(url) def find_element_by_css_selector(self, css_str): # 使用自定义的方法覆盖了原方法,比如先打印出一段话 self.logger.debug('要查找的元素的css选择器是 --> ' + css_str) self.driver.find_element_by_css_selector(css_str) def __getattr__(self, item): #想把其他的webdriver的操作方法直接添加进来,不一个一个的再写一个方法然后调用driver属性的方法,不想一直搞冗余的代码,可以这么做。python先使用__getattribute__,查不到才会调用__getsttr__方法,利用这个特性,来实现这个添加driver的属性到自己类里面 return getattr(self.driver, item) class _Test(unittest.TestCase): def test(self): driver_wrapper = DriverWrapper() driver_wrapper.open('https://www.baidu.com') # 有人不喜欢用get,可以叫open什么的 driver_wrapper.find_element_by_css_selector('#kw') # 当类中存在方法,使用了自己类里面的方法,所以每次使用css选择器查找元素时候会打印一个日志 driver_wrapper.find_element_by_id('kw') # 当类中不存在此方法,使用Chrome类的方法 driver_warpper.driver.close() # 这样做也可以,但不算是动态添加属性了,这是直接使用的该实例的driver属性的方法,driver属性是Chrome的一个实例。 if __name__ == '__main__': unittest.main()

6、此时就可以非常任性的在任何测试用例中任何函数中去实例化DriverWrapper类了,而不用依赖有webdriver实例化的测试用例方法必须先运行

7、无限实例化DriverWrapper类还想弄成单浏览器也可以借鉴享元模式或者单例模式。但抽成装饰器后,使用到其他的类中也是通用的,节约代码。不然很多代码中出现同一段相似的判断,看着稍微low点。

原文地址:https://www.cnblogs.com/ydf0509/p/9220593.html