実行時に関数を動的に実行していて、「ローカライズされた」スコープを追跡する必要があるシナリオがあります。以下の例では、「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
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
です。単純な関数呼び出しで、呼び出し元で発生した例外を魔法のようにインターセプトする方法はありません。
コンテキストマネージャーは、あなたがやろうとしていることに合っているように見えるので、よく読んでください。コンテキストマネージャの方法は、あなたに対応するであろうと。それがあなたが望むことを正確に行うかどうかは、それらの「マネージャー」関数に何をさせたいかによって異なりますが、単純な関数呼び出しでそれをしようとするよりも、コンテキストマネージャーでそれを行うほうが幸運です。__enter__
__exit__
startScope
endScope
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加