ContextManager访问带有块的调用的locals()

时间:2018-02-09 11:40:02

标签: python scope

我正在尝试使用上下文管理器编写多线程帮助程序。我们的想法是在一个块内定义一堆函数,然后在上下文管理器中神奇地定义它们。负责安排和一切。简化的工作版本如下所示:

import contextlib

@contextlib.contextmanager
def multi_threaded(count):
    funcs = []
    yield funcs
    my_slice = int(count / len(funcs))
    for i, func in enumerate(funcs):
        start = my_slice * i
        func(start, start + my_slice)   


def spawn_many():
    dataset = [1, 2, 3, 4, 5]
    with multi_threaded(len(dataset)) as mt:
        def foo(start_idx, end):
            print("foo" + str(dataset[start_idx : end]))
        def bar(start_idx, end):
            print("bar" + str(dataset[start_idx : end]))
        mt.append(foo)
        mt.append(bar)

spawn_many()

这个例子有效,但我想摆脱这些界限:

        mt.append(foo)
        mt.append(bar)

这样用户只需要定义函数而不将它们添加到集合中。为什么?因为它不易出错,我无法控制用这个库编写的代码。

问题是,在收益率之后我已经超出def foo发生的范围,所以我不知道该范围内存在的locals(),这基本上就是我需要的知道在那里定义了哪些功能。任何想法/技巧/鼓励的话语?

感谢阅读!

2 个答案:

答案 0 :(得分:1)

装饰者可能会更好一些:

import contextlib

@contextlib.contextmanager
def multi_threaded(count):
    funcs = []
    yield funcs
    my_slice = int(count / len(funcs))
    for i, func in enumerate(funcs):
        start = my_slice * i
        func(start, start + my_slice)   

def add_to_flist(mt):
    def _add_to_flist(func):
        mt.append(func)
        return func
    return _add_to_flist

def spawn_many():
    dataset = [1, 2, 3, 4, 5]
    with multi_threaded(len(dataset)) as mt:
        @add_to_flist(mt)
        def foo(start_idx, end):
            print("foo" + str(dataset[start_idx : end]))
        @add_to_flist(mt)
        def bar(start_idx, end):
            print("bar" + str(dataset[start_idx : end]))

spawn_many()

答案 1 :(得分:0)

我读到了this is not possible,至少不是没有丑陋的骇客,但我认为我的解决方案最终并没有那么丑陋:

您在创建时将locals()词典传递到contextmanager中,contextmanager屈服后会查询该词典以收集所有可调用对象:

@contextlib.contextmanager
def multi_threaded(block_locals, count):
    yield

    funcs = [fn for fn in block_locals.values() if callable(fn)]

    my_slice = int(count / len(funcs))
    for i, func in enumerate(funcs):
        start = my_slice * i
        func(start, start + my_slice)   

def spawn_many():
    dataset = [1, 2, 3, 4, 5]
    with multi_threaded(locals(), len(dataset)):
        def foo(start_idx, end):
            print("foo" + str(dataset[start_idx : end]))
        def bar(start_idx, end):
            print("bar" + str(dataset[start_idx : end]))

        # Re-sync locals-dict handed earlier to multi_threaded().
        locals()

spawn_many()

请注意,该技巧之所以有效,是因为该块中最后一次调用locals()。似乎Python仅在调用locals()时才同步locals()-字典<->函数局部变量。没有最后一个呼叫,multi_threaded就会把{'dataset': [1, 2, 3, 4, 5]}视为本地人。