是否可以通过两个单独的装饰器来构建一个函数,以便它只运行一次?

问题描述

在编写 reference 时,我开始怀疑是否无法简化/合理化我的代码。这个想法是让装饰器集中读取和写入数据库

我有以下(简单的家庭级)问题:我保持状态 一个 JSON 文件中的程序,有几个函数可以使用 那个“数据库”。有些只需要加载数据库,有些需要加载它, 然后写回文件

我想在这函数上使用装饰器来集中 数据库的读写。下面是简化版 我的代码,有两个功能一个只消耗数据库,另一个一个修改它。此代码有效并返回 期望值

url = 'https://www.adidas.com/us/women-athletic_sneakers'
driver = webdriver.Chrome('D:/chromedriver')
driver.get(url)
time.sleep(3)
time.sleep(2)

w_shoes = driver.find_elements_by_class_name('grid-item___3rAkS')
for shoe in w_shoes:
    
title = shoe.find_elements_by_xpath('//*[@id="product-search-results"]/div[2]/div[3]/div[4]/div[1]/div/div/div[3]/div[2]/a')[0].text
print(title)
    

因此我有两个装饰器:

是否可以更改这些装饰器,以便一个进行读取,另一个进行 DB 的写入?用一个或两个来装饰函数

我不清楚的一点是,在每个装饰器中,都有对函数的实际调用。如果我有两个装饰器,我假设我需要调用函数两次(每个装饰器一个 - 我当然不想这样做)。或者有更好的方法吗?

解决方法

您可以将这些函数分解为两个仅完成一项工作的简单函数:

  1. 加载数据库
  2. 写入数据库
def load_db(func):
    def wrapper():
        print("loading DB")
        db = 5
        # the db was loaded and is now passed to the function actually making use of it
        ret_db = func(db)

        # Check if returns a DB,this is required if it is written
        if ret_db:
            return ret_db
        return db
    return wrapper


def write_db(func):
    def wrapper():
        db = func()
        # now we write the DB to the disk
        print(f"writing DB: {db}")
    return wrapper


@load_db
def do_stuff_load_only(*db):
    # a function that just consumes the DB,without changing it
    print(f"initial DB is {db}")


@write_db
@load_db
def do_stuff_load_and_write(*db):
    # a function that consumes and chnages the DB (which then needs to be updated on disk)
    print(f"initial DB is {db}")
    db = 10
    print(f"changed DB to {db}")
    # returning the new DB
    return db


do_stuff_load_only()
do_stuff_load_and_write()

这会打印出与您相同的输出。

在这种情况下,我们可以根据需要使用两个装饰器或一个。这里有两点需要注意:

  1. 如果进行更改,则需要返回数据库,否则将返回原始未修改的数据库(请参阅 load_db 的包装器)
  2. 您只需按以下顺序使用装饰器:write_dbload_db,由于包装函数需要参数,因此切换顺序将不起作用。