* args是将参数传递给装饰函数或从装饰函数传递参数的正确方法吗? [英] is *args the proper way to pass parameters to/from decorated functions?

查看:78
本文介绍了* args是将参数传递给装饰函数或从装饰函数传递参数的正确方法吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下(简单的家庭级)问题:我将程序的状态保存在JSON文件中,并具有几个使用该数据库"的功能.有些只需要加载数据库,有些则需要加载数据库,然后写回文件.

I have the following (simple, home-grade) problem: I keep the state of a program in a JSON file and have several functions that make use of that "database". Some just need to load the DB, some need to load it, and then write back to file.

我想在这些功能上使用修饰符来集中数据库的读写.下面是我的代码的简化版本,具有两个功能:一个仅消耗数据库,另一个也对其进行修改.此代码有效并返回期望值(*)

I wanted to use decorators on these functions to centralize the reading and writing of the database. Below is a simplified version of my code, with two functions: one that only consumes the DB, and another one that also modifies it. This code works and returns the expected values(*)

请注意如何在装饰器和函数之间传递数据库( db )

Please note how the database (db) is passed between the decorators and the function

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
        func(db)
    return wrapper

def load_and_write_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
        # we will get back the changed database
        db = func(db)
        # 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}")

@load_and_write_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()

# Output:
# 
# loading DB
# initial DB is (5,)
# loading DB
# initial DB is (5,)
# changed DB to 10
# writing DB: 10

这是在装饰器和函数之间传递信息的正确方法吗?具体来说,我应该依靠 * db 来指示仅装饰器将参数传递给函数,而实际上是从代码中调用时不传递任何东西(最后两行))?

Is this the proper approach to pass information between the decorators and the function? Specifically, should I rely on *db to indicate only the decorator passes an argument to the function, and nothing is passed when it is actually called from the code (last two lines)?

此答案很好地解释了应如何传递参数,只是未能解决我有关修饰函数某些问题的问题接受论证,有时不接受.

This answer explains very nicely how arguments should be passed, it just fails short to address my question about decorated functions sometomes receiving an argument, and sometimes not.

(*) 几乎.传递给函数的 db 作为元组到达,我可以忍受

(*) Almost. db passed to the function arrives as a tuple, something I can live with

推荐答案

装饰器的编写方式 do_stuff_load_only 可以使用常规参数进行定义,即使您实际上并没有传递参数当你打电话的时候.这是因为名称 do_stuff_load_only 不会绑定到一个参数的函数.它将绑定到 load_db 内部定义的 zero 参数函数 wrapper . wrapper 本身将负责将参数传递给要修饰的实际单参数函数.

The way your decorator is written, do_stuff_load_only can be defined with a regular parameter, even if you won't actually pass an argument when you call it. That's because the name do_stuff_load_only isn't going to be bound to a one-argument function; it's going to be bound to the zero-argument function wrapper defined inside load_db. wrapper itself will take care of passing an argument to the actual one-argument function being decorated.

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


do_stuff_load_only()

定义 do_stuff_load_only(* db)将有效,但会更改与 db 实际绑定的内容;在这种情况下,它将是单例元组(5,),而不是整数 5 .

Defining do_stuff_load_only(*db) would work, but changes what is actually bound to db; in this case, it would be the singleton tuple (5,) rather than the integer 5.

如果您认为这看起来很尴尬,那是因为. load_db 具有隐藏"状态,您不必担心的副作用.上下文管理器可能更适合这里:

If you think this looks awkward, that's because it is. load_db has a "hidden" side effect that you shouldn't have to worry about. A context manager would probably be more fitting here:

from contextlib import contextmanager


@contextmanager
def load_db():
    print("Initializing the database")
    yield 5  # provide it
    print("Tearing down the database")


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


with load_db() as db:
    do_stuff_load_only(db)

现在绑定到 do_stuff_load_only 的函数的定义和使用与上下文管理器隐藏数据库的创建和销毁方式有关.上面代码的输出是

Now the definition and use of the function bound to do_stuff_load_only agree, with the details of how the database is created and destroyed hidden by the context manager. The output of the above code is

Initializing the database
initial DB is 5
Tearing down the database

这篇关于* args是将参数传递给装饰函数或从装饰函数传递参数的正确方法吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆