【问题标题】:How to pass a variable to an imported function implicitly in Python?如何在 Python 中将变量隐式传递给导入的函数?
【发布时间】:2018-01-24 22:17:06
【问题描述】:

module1 定义 function1function2module2 使用 module1 中定义的 2 个函数,并且需要将可以描述为上下文配置参数的内容(实际上是可变自定义类的实例,而不是像字符串这样不起眼的东西)传递给它们,每个都一样在module2 内调用这些函数中的任何一个。如果我能避免使用普通的函数参数方式重复传递它,并且可以只指定一次,以便模块内调用的所有函数 (module2) 能够访问它,我将不胜感激。这在 Python 中可能吗?我使用最新的 Python 3.6。 module1 不是 3-rd 方库,也不是已建立的代码库模块,我可以在此阶段以任何必要的方式修改定义。

# --- 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')

【问题讨论】:

  • 你能显示一些代码来更清楚你的意思吗?
  • 当然。也许展示你现在拥有的东西并想知道如何替换它。
  • 听起来你想要动态范围,Python 不支持。你也许可以一起破解一些东西,但我怀疑,你应该重新设计这种方法,以使用一个将这个配置对象作为参数并在那里封装状态的类,而不是依赖全局变量。
  • @mkrieger1 我已经添加了代码示例

标签: python python-3.6 implicit


【解决方案1】:

(已编辑以匹配问题中的示例代码)如果您修改函数以使 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 参数的部分。

【讨论】:

  • 你甚至可以在模块中提供一个函数,给定上下文选项,一举返回所有函数的部分。然后调用者为函数设置自己的名称,例如function1, function2, ... = module1.partials(cx)
  • 很好的答案(虽然对于我的情况来说太冗长了,因为我有很多功能)。我希望我能批准两者。
  • @kindall 听起来很奇怪。这怎么可能做到(我刚开始使用 Python,如果是一个愚蠢的问题,对不起)?
  • 类似def givecontext(cx): return partial(function1, cx=cx), partial(function2, cx=cx), partial(function3, cx=cx)。然后调用者会做func1, func2, func3 = givecontext(Context("foo"))
【解决方案2】:

一种完全不同的方法,可能是一个可怕、可怕的想法,但很有趣(并且避免了module2 中的大部分跑腿工作):您可以制作一个在调用者的上下文并自动将其提供给函数。像这样:

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}!')

【讨论】:

  • 啊啊啊啊,这是一个不错的(邪恶的)hack。
  • 你也可以只使用一个函数getcontext(),从每个函数中调用它来获取调用者的cx变量。
【解决方案3】:

可以使用模块级全局变量来做到这一点。

context = None

def function1(arg1, arg2, arg3):
    # do something involving context and args

然后,您只需从导入 module1 的位置执行 module1.context = whatever

但更好的方法是一个类,它持有对上下文的引用,在对象实例化时传入:

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)

这样,您可以根据需要拥有尽可能多的上下文。

【讨论】:

  • 使用类是一个明显的解决方案,但是会有很多函数,而且它们会很长并且相互独立,只共享一个变量(实际上我很乐意将每个函数放入一个单独的文件,也许我会做一个包),这个类不会代表一个逻辑上真实的实体,而且......我选择 Python 的一个主要原因是我可以在没有类的情况下定义一个函数:-)跨度>
  • 它们共享状态(那个变量),如果你不想将它传递给每个函数,将它们放在一个类中是完全合理的。
  • 我正在寻找的答案是module1.context = whatever。我之前尝试过的是from module1 import *,然后是context = whatever,但没有成功。现在我import module1 然后设置module1.context = whatever 然后from module1 import * 一切似乎工作正常。坦克!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-13
相关资源
最近更新 更多