2015-09-30 16 views
5

Çalışma zamanında dinamik olarak işlevleri çalıştırdığım ve "yerelleştirilmiş" kapsamı takip etmem gereken bir senaryo var. Aşağıdaki örnekte, "startScope" ve "endScope" aslında "yuvalama" seviyelerini oluşturacaktı (gerçekte, bu yerelleştirilmiş kapsamda yer alan öğeler baskı ifadeleri değil ... başka yerlerde ve yuvada veri gönderen işlev çağrılarıdır) orada izlenir startScope/endScope sadece geçerli yuvalama derinliğini başlatmak/sonlandırmak için kullanılan kontrol bayraklarını ayarlar.Python'da dinamik yerelleştirilmiş bir alan oluşturmak mümkün mü?

Bu, iç içe geçmiş verileri izlemek için iyi çalışır, ancak istisnalar başka bir konudur. İdeal olarak, bir istisna mevcut lokalize kapsamın "düşmesi" ile sonuçlanacak ve tüm fonksiyonu sona ermeyecektir (aşağıdaki örnekte benim fonksiyonum).

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!" 

sonrasında bu olur (teorik olarak) çıkışı çalıştırma:

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

>>> 

ben yukarıda yazdım bu mümkün değildir oldukça eminim - Sadece bir resmini boyamak istedi sonuç elde etmeye çalışıyorum tür sonuç.

Python'da böyle bir şey mümkün mü?

Düzenleme: bu aslında nasıl kullanıldığını ait A (uzun olsa) daha alakalı örnek:

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 
+0

Bunun hangi yönlerinin zorunlu olduğunu ve hangisinin esnek olduğunu açıklayabilir misiniz? Ayrıca, bu "kapsamlar" ı istisna işleminden başka bir şey üzerinde herhangi bir etkiye sahip olmak için istersiniz/ister misiniz? Eğer istisna “ana” olarak yakalanırsa, “yazdır” “E” yi çalıştırabilmenin hiçbir yolu yoktur; istisna ilerledikçe, yürütmeyi daha düşük bir seviyede "devam ettiremezsiniz". – BrenBarn

+0

Bu örnek için, sadece sonuçla ilgileniyorum. Örnek (yukarıda yazıldığı gibi), daha önce bahsettiğiniz sebepten dolayı işe yaramayacaktır. Esnekliğe gelince - "myFunction" un içeriği üzerinde doğrudan kontrol sahibi değilim ... tek bildiğim "startScope" ve "endScope" olarak adlandırılacak ve bu aramaları uygun gördüğüm şekilde halledebiliyorum. Şu anda, bu, ayrı bir kontrol sınıfında bir yığından ittirmek/atmak anlamına gelir. Bu veriler için çalışır, ancak istisna işleme başka bir hikaye. Umudum, bu tür şeylerle uğraşan bir çeşit ortak tasarım deseni var ... – Novark

cevap

3

Bir with ifadesini kullanarak bir bağlam yöneticisi ile benzer bir etkiyi elde edebilirsiniz.

@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 

Bu sizin örnekte olduğu gibi sıradan bir işlev çağrısı ile yapılamaz: İşte contextlib.contextmanager dekoratör kullanın. Örneğinizde, myFunction'un çalıştırılmasının geri kalanı önce startScope işlevi döndürülür, bu nedenle startScope üzerinde herhangi bir etkisi olamaz. İstisnaları işlemek için, myFunction içinde bir tür açık yapıya (bir with bildirimi veya düzenli bir try/except) gereksinim duyarsınız; basit bir işlev çağrısı yapmanın, çağrısında yükselen istisnaları gizlemenin bir yolu yoktur.

context managers numaralı telefonu, yapmaya çalıştığınız şeye benzedikleri için okumanız gerekir. Bağlam yöneticisinin __enter__ ve __exit__ yöntemleri, startScope ve endScope'a karşılık gelir. Tam olarak ne istediğinizi yapacağınız, bu "yönetici" işlevlerinin tam olarak ne yapmak istediğinize bağlı olarak değişir, ancak basit işlev çağrılarıyla yapmaya çalışmaktan ziyade bir bağlam yöneticisi ile daha fazla şansınız olacaktır.

+0

Muhtemelen istediğim sonuca ulaşacağım kadar yakınım sanırım ... Ne yazık ki yukarıdaki örnekte olduğu gibi foo() üzerinde doğrudan kontrolüm yok. "foo" bir başkası tarafından yazılacak ve amaç onlara kendileri için bağlamı ele alacak bazı sihirli yöntemlere erişebilmektir. Böylece "startMagic()", "errorFunc()" ve sonra "endMagic()" diye adlandırırlar. Amacım, malzeme kırıldığında, "endMagic" değerine düştüğümüzden ve istisna kaldırıldıktan sonra "errorFunc" dosyasında yer alan diğer öğeleri atladığımızdan emin olmaktır. – Novark

+0

@Novark: "Foo" üzerinde doğrudan kontrollere ihtiyacınız yok. Kullanmak için 'foo' yazarları için bir API yazıyorsanız, muhtemelen işe yaratabileceğinizi düşünüyorum. Yapmanız gereken şey onlara içerik yöneticisine erişim sağlamaktır, böylece 'startMagic() ... endMagic()' yerine, 'magicManager ile:' 'yaparlar. Bir içerik yöneticisi çözümü ile denemenizi öneririz. Ne tür bir dava ele almayacak? – BrenBarn

+0

Orijinal yazımda, üzerinde çalıştığım şeyle biraz daha ilgili olan genişletilmiş bir örnek ekledim ... Bu örnek, işlerin nasıl çalıştığını gösterir, ancak düşme problemine * neden olur * test_alpha() işlevi, yerel kapsamın sonuna değil, bir hatayla karşılaşıldığında işlev görür (umarım bu mantıklıdır - herhangi bir şeyi açıklığa kavuşturursam bana bildirin) – Novark

İlgili konular