返回Python中的范围变量

时间:2018-07-19 22:27:49

标签: python function scope

给出

我有一个函数scope,可以在一个封闭的命名空间中声明该函数,即 module class function 。感兴趣的变量位于该函数的外部。

我通常如何访问在任何封闭名称空间中声明的变量/参数及其值?

示例

用于捕获函数变量(b)和参数(c)的示例伪代码:

a = 1
def func(c=3)
    b = 2
    def scope():
        return ...
    return scope()

预期产量

func()
# {'c': 3, 'b': 2}

尝试

我在模块级别取得了一定的成功,例如a = 1

# module-level
a = 1
def scope():
    return {k: v for k, v in globals().items() if not k.startswith("_")}

scope()
# {'a': 1, ...}

我还可以从方法中访问类属性,例如b = 2

# class-level
a = 1
class Klass:
    b = 2
    def scope(self):
        obj = self.__class__
        return obj.__dict__

Klass().scope()
# {'b': 2, ...}

我只能部分访问封闭函数中的变量和参数:

# function-level
a = 1
def func(c=3):
    b = 2
    def scope():
        obj = func
        return obj.__code__.co_varnames
    return scope()

func()
# ('c', 'b', 'scope')

虽然__code__.co_varnames成功地给出了封闭变量(a除外),但我对这些值也很感兴趣(例如{'c': 3, 'b': 2})。

我做了许多未提及的尝试,包括inspect函数,其他代码对象方法,dir()和对象特殊方法。我的首选是实现更通用和惯用的代码,以检测封闭的命名空间中的变量,尽管可以理解任何建议。

我也知道Python习惯用法和这个问题的性质。我仍然对它的可能性着迷,并感谢任何愿意超越常规的人。

2 个答案:

答案 0 :(得分:1)

虽然python将为您提供实现此目的的方法,但您确实不希望这样做。函数/类/等不应将其内部信息公开给调用代码,因为这会破坏抽象并使代码易碎。函数应该接受参数并返回输出值,但是内部算法,尤其是变量名不应该公开。

答案 1 :(得分:1)

有点作弊,但是

def scope(outer=locals()):
    return outer

有效。 (之所以作弊,是因为默认参数是在定义时由定义代码求值的,因此locals()在封闭范围内运行,因此实际上不是scope函数扩展到封闭范围。)< / p>

请注意,在调用后修改相应范围后,locals()返回的目录可能会更改,也可能不会更改。如果要在Python实现之间(甚至在不同用例之间)保持一致的行为,请在默认参数或scope函数的正文中进行深拷贝。

# module-level

a = 1

def scope(outer=locals()):
    return outer

e = 5
result = scope()
f = 6

public_result = {k: v for k, v in result.items() if not k.startswith('_')}
assert (
    # allowed:
    public_result == dict(a=1)
    or
    # C-Python 3.6 behavior:
    public_result == dict(a=1, scope=scope, e=5, result=result, f=6)
)
# class-level

a = 1


class Klass:
    b = 2

    @staticmethod
    def _scope(outer=locals()):  # Hidden from public result, because I have
        return outer             # no idea how to refer to it from the outside.

    d = 4


e = 5
result = Klass._scope()
f = 6

public_result = {k: v for k, v in result.items() if not k.startswith('_')}
assert (
    # allowed:
    public_result == dict(b=2)
    or
    # CPython 3.6 behavior:
    public_result == dict(b=2, d=4)  # would also contain `scope` if it wasn't hidden
)
# function-level

a = 1


def func(c=3):
    b = 2

    def scope(outer=locals()):
        return outer

    return scope(), scope

    d = 4


e = 5
result, scope_fu = func()
f = 6

assert (
    # C-Python 3.6 behaviour:
    result == dict(b=2, c=3)
    or
    # also allowed:
    result == dict(b=2, c=3, scope=scope_fu, d=4)
)