是否可以在Python中创建动态本地化范围?

Nov*_*ark 5 python scope

我有一个场景,我在运行时动态运行函数,需要跟踪"本地化"范围.在下面的示例中,"startScope"和"endScope"实际上将创建"嵌套"级别(实际上,此本地化作用域中包含的内容不是打印语句...它是在其他地方发送数据的函数调用和嵌套跟踪那里.startScope/endScope只设置用于开始/结束当前嵌套深度的控制标志.

这一切都适用于跟踪嵌套数据,但是异常是另一回事.理想情况下,异常会导致当前本地化范围"掉出"并且不会结束整个函数(下面示例中的myFunction).

def startScope():
    #Increment our control object's (not included in this example) nesting depth
    control.incrementNestingDepth()

def endScope():
    #Decrement our control object's (not included in this example) nesting depth
    control.decrementNestingDepth()

def myFunction():
    print "A"
    print "B"

    startScope()
    print "C"
    raise Exception
    print "D"
    print "This print statement and the previous one won't get printed"
    endScope()

    print "E"

def main():
    try:
        myFunction()
    except:
        print "Error!"
Run Code Online (Sandbox Code Playgroud)

运行它会(理论上)输出以下内容:

>>> main()
A
B
C
Error!
E

>>>
Run Code Online (Sandbox Code Playgroud)

我很确定这是不可能的,因为我已经在上面写了 - 我只是想描绘一下我想要实现的那种最终结果.

Python中可能有这样的东西吗?

编辑: 一个更相关(尽管很长)的实例如何使用它的例子:

class Log(object):
    """
    Log class
    """

    def __init__(self):
        #DataModel is defined elsewhere and contains a bunch of data structures / handles nested data / etc...
        self.model = DataModel()

    def Warning(self, text):
        self.model.put("warning", text)

    def ToDo(self, text):
        self.model.put("todo", text)

    def Info(self, text):
        self.model.put("info", text)

    def StartAdvanced(self):
        self.model.put("startadvanced")

    def EndAdvanced(self):
        self.model.put("endadvanced")

    def AddDataPoint(self, data):
        self.model.put("data", data)

    def StartTest(self):
        self.model.put("starttest")

    def EndTest(self):
        self.model.put("endtest")

    def Error(self, text):
        self.model.put("error", text)


#myScript.py

from Logger import Log

def test_alpha():
    """
    Crazy contrived example

    In this example, there are 2 levels of nesting...everything up to StartAdvanced(),
    and after EndAdvanced() is included in the top level...everything between the two is
    contained in a separate level.
    """

    Log.Warning("Better be careful here!")
    Log.AddDataPoint(fancyMath()[0])

    data = getSerialData()

    if data:
        Log.Info("Got data, let's continue with an advanced test...")

        Log.StartAdvanced()

        #NOTE: If something breaks in one of the following methods, then GOTO (***)
        operateOnData(data)
        doSomethingCrazy(data)
        Log.ToDo("Fill in some more stuff here later...")
        Log.AddDataPoint(data)

        Log.EndAdvanced()

    #(***) Ideally, we would resume here if an exception is raised in the above localized scope
    Log.Info("All done!  Log some data and wrap everything up!")
    Log.AddDataPoint({"data": "blah"})

    #Done


#framework.py

import inspect
from Logger import Log

class Framework(object):

    def __init__(self):
        print "Framework init!"
        self.tests = []

    def loadTests(self, file):
        """
        Simplifying this for the sake of clarity
        """

        for test in file:
            self.tests.append(test)

    def runTests(self):
        """
        Simplifying this for the sake of clarity
        """

        #test_alpha() as well as any other user tests will be run here
        for test in self.tests:
            Log.StartTest()

            try:
                test()
            except Exception,e :
                Log.Error(str(e))

            Log.EndTest()

#End
Run Code Online (Sandbox Code Playgroud)

Bre*_*arn 3

您可以使用语句通过上下文管理器实现类似的效果with。这里我使用contextlib.contextmanager装饰器:

@contextlib.contextmanager
def swallower():
    try:
        yield
    except ZeroDivisionError:
        print("We stopped zero division error")

def foo():
    print("This error will be trapped")
    with swallower():
        print("Here comes error")
        1/0
        print("This will never be reached")
    print("Merrily on our way")
    with swallower():
        print("This error will propagate")
        nonexistentName
    print("This won't be reached")

>>> foo()
This error will be trapped
Here comes error
We stopped zero division error
Merrily on our way
This error will propagate
Traceback (most recent call last):
  File "<pyshell#4>", line 1, in <module>
    foo()
  File "<pyshell#3>", line 10, in foo
    nonexistentName
NameError: global name 'nonexistentName' is not defined
Run Code Online (Sandbox Code Playgroud)

它不能像您的示例中那样通过普通函数调用来完成。在您的示例中,该函数在执行正文startScope的其余部分之前返回myFunction,因此startScope不会对其产生任何影响。要处理异常,您需要在 ; 内部使用某种显式结构(with语句或常规try/exceptmyFunction。没有办法让一个简单的函数调用神奇地拦截其调用者引发的异常。

您应该阅读上下文管理器,因为它们似乎适合您想要做的事情。上下文管理器的 和 方法将对应于您的__enter__和。它是否会完全按照您想要的方式执行取决于您希望这些“管理器”函数执行的操作,但是使用上下文管理器执行此操作可能比尝试使用简单的函数调用执行此操作更幸运。__exit__startScopeendScope