如何在 Python 中将变量隐式传递给导入函数?
How to pass a variable to an imported function implicitly in Python?
module1
定义了 function1
和 function2
。 module2
使用了 module1
中定义的 2 个函数,并且需要传递可以描述为上下文配置参数的内容(实际上是可变自定义 class 的实例,而不是像字符串),每次在 module2
中调用这些函数时都是一样的。如果我可以避免使用普通的函数参数方式重复传递它并且可以只指定一次以便模块 (module2
) 中调用的所有函数都能够访问它,我将不胜感激。这在 Python 中可能吗?我用的是最新的Python 3.6。 module1
不是第 3 方库,也不是已建立的代码库模块,我可以在此阶段以任何必要的方式修改定义。
# --- module1.py ---
class Context:
def __init__(self, s: str):
self.s = s
def function1(cx: Context, s1: str):
print(f'{cx.s} {s1}!')
# --- module2.py ---
from module1 import Context
from module1 import function1
cx = Context('Hello')
# this works and prints 'Hello World!'
function1(cx, 'World')
# this doesn't work but I want it to work and do exactly the same
# (function1 definition can be changed whatever way necessary)
function1('World')
(编辑以匹配问题中的示例代码)如果您修改函数以使 cx
成为关键字参数(或者,最后一个位置参数),这可能是部分的好地方) 像这样:
def function1(s1: str, cx: Context = None):
print(f'{cx.s} {s1}!')
那么你可以这样做:
from module1 import Context
from module1 import function1
from functools import partial
cx = Context('hello')
function1 = partial(function1, cx = cx)
当您调用 function1
时,您将调用已经设置了 cx
参数的部分。
您可以使用模块级全局变量来做到这一点。
context = None
def function1(arg1, arg2, arg3):
# do something involving context and args
然后您只需从导入 module1
的位置执行 module1.context = whatever
。
但更好的方法是 class 保存对上下文的引用,在实例化对象时传入:
class MyFunctions(object):
def __init__(self, context):
self.context = context
def function1(self, arg1, arg2. arg3):
# do something with self.context and args
然后你这样使用它:
myfunctions = MyFunctions(configuration_object)
myfunctions.function1(1, 2, 3)
这样,您可以根据需要拥有任意数量的上下文。
一种完全不同的方法,可能是一个糟糕透顶的想法,但是很有趣(并且避免了module2
中的大部分跑腿工作):你可以做一个装饰器在调用者的上下文中找到 cx
并自动将其提供给函数。像这样:
import sys
from functools import wraps
def withCallerContext(fn):
@wraps(fn)
def wrapped(*a, **k):
frame = sys._getframe(0)
while 'cx' not in frame.f_globals:
frame = frame.f_back
if frame is None:
raise ValueError("must set cx in calling context")
cx = frame.f_globals['cx']
return fn(*a, cx = cx, **k)
return wrapped
@withCallerContext
def function1(s1: str, cx: Context = None):
print(f'{cx.s} {s1}!')
module1
定义了 function1
和 function2
。 module2
使用了 module1
中定义的 2 个函数,并且需要传递可以描述为上下文配置参数的内容(实际上是可变自定义 class 的实例,而不是像字符串),每次在 module2
中调用这些函数时都是一样的。如果我可以避免使用普通的函数参数方式重复传递它并且可以只指定一次以便模块 (module2
) 中调用的所有函数都能够访问它,我将不胜感激。这在 Python 中可能吗?我用的是最新的Python 3.6。 module1
不是第 3 方库,也不是已建立的代码库模块,我可以在此阶段以任何必要的方式修改定义。
# --- module1.py ---
class Context:
def __init__(self, s: str):
self.s = s
def function1(cx: Context, s1: str):
print(f'{cx.s} {s1}!')
# --- module2.py ---
from module1 import Context
from module1 import function1
cx = Context('Hello')
# this works and prints 'Hello World!'
function1(cx, 'World')
# this doesn't work but I want it to work and do exactly the same
# (function1 definition can be changed whatever way necessary)
function1('World')
(编辑以匹配问题中的示例代码)如果您修改函数以使 cx
成为关键字参数(或者,最后一个位置参数),这可能是部分的好地方) 像这样:
def function1(s1: str, cx: Context = None):
print(f'{cx.s} {s1}!')
那么你可以这样做:
from module1 import Context
from module1 import function1
from functools import partial
cx = Context('hello')
function1 = partial(function1, cx = cx)
当您调用 function1
时,您将调用已经设置了 cx
参数的部分。
您可以使用模块级全局变量来做到这一点。
context = None
def function1(arg1, arg2, arg3):
# do something involving context and args
然后您只需从导入 module1
的位置执行 module1.context = whatever
。
但更好的方法是 class 保存对上下文的引用,在实例化对象时传入:
class MyFunctions(object):
def __init__(self, context):
self.context = context
def function1(self, arg1, arg2. arg3):
# do something with self.context and args
然后你这样使用它:
myfunctions = MyFunctions(configuration_object)
myfunctions.function1(1, 2, 3)
这样,您可以根据需要拥有任意数量的上下文。
一种完全不同的方法,可能是一个糟糕透顶的想法,但是很有趣(并且避免了module2
中的大部分跑腿工作):你可以做一个装饰器在调用者的上下文中找到 cx
并自动将其提供给函数。像这样:
import sys
from functools import wraps
def withCallerContext(fn):
@wraps(fn)
def wrapped(*a, **k):
frame = sys._getframe(0)
while 'cx' not in frame.f_globals:
frame = frame.f_back
if frame is None:
raise ValueError("must set cx in calling context")
cx = frame.f_globals['cx']
return fn(*a, cx = cx, **k)
return wrapped
@withCallerContext
def function1(s1: str, cx: Context = None):
print(f'{cx.s} {s1}!')