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

时间:2015-09-30 05:53:36

标签: 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!"

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

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

>>>

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

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

1 个答案:

答案 0 :(得分: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

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

您应该阅读context managers因为它们似乎符合您的尝试。上下文管理器的__enter____exit__方法与您的startScopeendScope相对应。它是否会完全符合您的要求取决于您想要的那些&#34;经理&#34;要做的功能,但是你可能会比使用简单的函数调用更好地使用上下文管理器。