*args 是传递参数 to/from 修饰函数的正确方法吗?
is *args the proper way to pass parameters to/from decorated functions?
我有以下(简单的家庭级)问题:我将程序的状态保存在 JSON 文件中,并有几个使用该“数据库”的函数。有些只需要加载数据库,有些需要加载它,然后写回文件。
我想在这些函数上使用装饰器来集中读取和写入数据库。下面是我的代码的简化版本,有两个功能:一个只使用数据库,另一个也修改它。此代码有效并且 returns 预期值(*)
请注意数据库(db
)是如何在装饰器和函数之间传递的
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
来指示装饰器只将参数传递给函数,而当它实际从代码中调用时什么也没有传递(最后两行) ?
This answer 很好地解释了应该如何传递参数,只是未能解决我关于装饰函数有时接收参数的问题,有时不接收参数的问题。
(*) 几乎。 db
传递给函数的是一个元组,我可以接受
装饰器的编写方式,do_stuff_load_only
可以使用常规参数定义,即使您在调用它时实际上不会传递参数。那是因为名称 do_stuff_load_only
不会绑定到单参数函数;它将绑定到 load_db
中定义的 zero 参数函数 wrapper
。 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}")
do_stuff_load_only()
定义 do_stuff_load_only(*db)
会 工作 ,但会更改实际绑定到 db
的内容;在这种情况下,它将是单例元组 (5,)
而不是整数 5
.
如果您认为这看起来很尴尬,那是因为它确实如此。 load_db
有一个您不必担心的“隐藏”副作用。上下文管理器可能更适合这里:
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
的函数的定义和使用一致,上下文管理器隐藏了数据库如何创建和销毁的详细信息。上面代码的输出是
Initializing the database
initial DB is 5
Tearing down the database
我有以下(简单的家庭级)问题:我将程序的状态保存在 JSON 文件中,并有几个使用该“数据库”的函数。有些只需要加载数据库,有些需要加载它,然后写回文件。
我想在这些函数上使用装饰器来集中读取和写入数据库。下面是我的代码的简化版本,有两个功能:一个只使用数据库,另一个也修改它。此代码有效并且 returns 预期值(*)
请注意数据库(db
)是如何在装饰器和函数之间传递的
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
来指示装饰器只将参数传递给函数,而当它实际从代码中调用时什么也没有传递(最后两行) ?
This answer 很好地解释了应该如何传递参数,只是未能解决我关于装饰函数有时接收参数的问题,有时不接收参数的问题。
(*) 几乎。 db
传递给函数的是一个元组,我可以接受
装饰器的编写方式,do_stuff_load_only
可以使用常规参数定义,即使您在调用它时实际上不会传递参数。那是因为名称 do_stuff_load_only
不会绑定到单参数函数;它将绑定到 load_db
中定义的 zero 参数函数 wrapper
。 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}")
do_stuff_load_only()
定义 do_stuff_load_only(*db)
会 工作 ,但会更改实际绑定到 db
的内容;在这种情况下,它将是单例元组 (5,)
而不是整数 5
.
如果您认为这看起来很尴尬,那是因为它确实如此。 load_db
有一个您不必担心的“隐藏”副作用。上下文管理器可能更适合这里:
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
的函数的定义和使用一致,上下文管理器隐藏了数据库如何创建和销毁的详细信息。上面代码的输出是
Initializing the database
initial DB is 5
Tearing down the database