2011-10-30 5 views
17

मुझे पता है कि मैं की तरह कुछ के साथ कई फ़ाइलें खोल सकते हैं हूँ,उद्घाटन एकाधिक (एक अनिर्दिष्ट संख्या) वे सही ढंग से बंद हो जाती हैं

with open('a', 'rb') as a, open('b', 'rb') as b: 

लेकिन मैं एक स्थिति है जहाँ मैं एक सूची है है फाइलों को खोलने के लिए और सोच रहा हूं कि पसंदीदा विधि क्या करने की है जब फ़ाइलों की संख्या अग्रिम में अज्ञात है। कुछ की तरह,

with [ open(f, 'rb') for f in files ] as fs: 

(लेकिन यह एक AttributeError सूची के बाद से साथ विफल __exit__ को लागू नहीं करता है)

मैं की तरह कुछ का उपयोग कर कोई आपत्ति नहीं है,

try: 
    fs = [ open(f, 'rb') for f in files ] 

    .... 

finally: 
    for f in fs: 
     f.close() 

लेकिन यकीन नहीं है क्या अगर कुछ फाइलें खोलने की कोशिश करते समय फेंक दें तो ऐसा होगा। fsfinally ब्लॉक में, फ़ाइलों को खोलने के लिए प्रबंधित की गई फ़ाइलों के साथ ठीक से परिभाषित किया जाएगा?

+0

आप इन फ़ाइलों तक पहुँचने की जाएगी समानांतर में, या अनुक्रमिक रूप से? –

+0

@EthanFurman समानांतर में। – tjm

उत्तर

12

नहीं, आपका कोड fs शुरू नहीं करेगा जब तक कि सभी open() कॉल सफलतापूर्वक पूर्ण नहीं हो जाते। यह हालांकि काम करना चाहिए:

fs = [] 
try: 
    for f in files: 
     fs.append(open(f, 'rb')) 

    .... 

finally: 
    for f in fs: 
     f.close() 

भी ध्यान रखें कि f.close() इसलिए विफल हो सकता है तुम वहाँ पकड़ने और उपेक्षा (या अन्य प्रकार संभाल) के लिए किसी भी विफलताओं कर सकते हैं। जब एक फ़ाइल खोलने का प्रयास, जब एक फ़ाइल से पढ़ने का प्रयास है, और (बहुत मुश्किल से ही) एक फ़ाइल को बंद करने का प्रयास जब

1

त्रुटियाँ हो सकता है।

तो एक बुनियादी त्रुटि हैंडलिंग संरचना लग सकता है जैसे:

try: 
    stream = open(path) 
    try: 
     data = stream.read() 
    finally: 
     stream.close() 
except EnvironmentError as exception: 
    print 'ERROR:', str(exception) 
else: 
    print 'SUCCESS' 
    # process data 

यह सुनिश्चित करता है कि close हमेशा अगर stream चर मौजूद है बुलाया जाएगा। यदि stream मौजूद नहीं है, तो open नाकाम रहे हैं चाहिए, और इसलिए वहाँ बंद करने के लिए (इस स्थिति में, ब्लॉक को छोड़कर तुरंत निष्पादित किया जाएगा) कोई फ़ाइल है।

तुम सच में, फ़ाइलें समानांतर रूप से खोलना होगा या वे क्रमिक रूप से संसाधित किया जा सकता है? यदि उत्तरार्द्ध, तो ऊपर दिए गए फ़ाइल-प्रोसेसिंग कोड की तरह कुछ फ़ंक्शन में रखा जाना चाहिए, जिसे तब सूची में प्रत्येक पथ के लिए बुलाया जाता है।

7

ज़रूर, क्यों नहीं, यहाँ एक नुस्खा है कि यह करना चाहिए। एक संदर्भ प्रबंधक 'पूल' बनाएं जो संदर्भों की मनमानी संख्या दर्ज कर सकता है (इसे enter() विधि कहकर) और उन्हें सूट के अंत के अंत में साफ़ कर दिया जाएगा।

class ContextPool(object): 
    def __init__(self): 
     self._pool = [] 

    def __enter__(self): 
     return self 

    def __exit__(self, exc_type, exc_value, exc_tb): 
     for close in reversed(self._pool): 
      close(exc_type, exc_value, exc_tb) 

    def enter(self, context): 
     close = context.__exit__ 
     result = context.__enter__() 
     self._pool.append(close) 
     return result 

उदाहरण के लिए:

>>> class StubContextManager(object): 
...  def __init__(self, name): 
...   self.__name = name 
...  def __repr__(self): 
...   return "%s(%r)" % (type(self).__name__, self.__name) 
... 
...  def __enter__(self): 
...   print "called %r.__enter__()" % (self) 
... 
...  def __exit__(self, *args): 
...   print "called %r.__exit__%r" % (self, args) 
... 
>>> with ContextPool() as pool: 
...  pool.enter(StubContextManager("foo")) 
...  pool.enter(StubContextManager("bar")) 
...  1/0 
... 
called StubContextManager('foo').__enter__() 
called StubContextManager('bar').__enter__() 
called StubContextManager('bar').__exit__(<type 'exceptions.ZeroDivisionError'>, ZeroDivisionError('integer division or modulo by zero',), <traceback object at 0x02958648>) 
called StubContextManager('foo').__exit__(<type 'exceptions.ZeroDivisionError'>, ZeroDivisionError('integer division or modulo by zero',), <traceback object at 0x02958648>) 

Traceback (most recent call last): 
    File "<pyshell#67>", line 4, in <module> 
    1/0 
ZeroDivisionError: integer division or modulo by zero 
>>> 

चेतावनियां: संदर्भ प्रबंधकों को अपने __exit__() तरीकों में अपवाद को बढ़ाने के लिए नहीं जा सकते, लेकिन अगर वे करते हैं, यह नुस्खा सभी संदर्भ के लिए सफाई नहीं करता है प्रबंधकों। इसी तरह, हर संदर्भ प्रबंधक को इंगित करता है, भले ही है कि एक अपवाद (उनके बाहर निकलने के तरीकों से True वापस लौट कर) अनदेखा किया जाना चाहिए, यह अभी भी अपवाद उठाया जा करने की अनुमति देगा।

1

अपने सभी सवालों के जवाब के लिए धन्यवाद। आप सभी से प्रेरणा लेते हुए, मैं निम्नलिखित के साथ आया हूं। मुझे लगता है (आशा) यह मेरे इरादे के रूप में काम करता है।मुझे यकीन नहीं था कि इसे एक उत्तर या प्रश्न के अतिरिक्त के रूप में पोस्ट करना है, लेकिन सोचा था कि एक उत्तर अधिक उपयुक्त था, अगर वह ऐसा करने में विफल रहता है जिसे मैंने पूछा था तो इसे उचित रूप से टिप्पणी की जा सकती है।

यह इस तरह के उदाहरण के लिए इस्तेमाल किया जा सकता ..

with contextlist([open, f, 'rb'] for f in files) as fs: 
    .... 

या इस तरह ..

f_lock = threading.Lock() 
with contextlist(f_lock, ([open, f, 'rb'] for f in files)) as (lock, *fs): 
    .... 

और यहाँ यह है,

import inspect 
import collections 
import traceback 

class contextlist: 

    def __init__(self, *contexts): 

     self._args = [] 

     for ctx in contexts: 
      if inspect.isgenerator(ctx): 
       self._args += ctx 
      else: 
       self._args.append(ctx) 


    def __enter__(self): 

     if hasattr(self, '_ctx'): 
      raise RuntimeError("cannot reenter contextlist") 

     s_ctx = self._ctx = [] 

     try: 
      for ctx in self._args: 

       if isinstance(ctx, collections.Sequence): 
        ctx = ctx[0](*ctx[1:]) 

       s_ctx.append(ctx) 

       try: 
        ctx.__enter__() 
       except Exception: 
        s_ctx.pop() 
        raise 

      return s_ctx 

     except: 
      self.__exit__() 
      raise 


    def __exit__(self, *exc_info): 

     if not hasattr(self, '_ctx'): 
      raise RuntimeError("cannot exit from unentered contextlist") 

     e = [] 

     for ctx in reversed(self._ctx): 
      try: 
       ctx.__exit__() 
      except Exception: 
       e.append(traceback.format_exc()) 

     del self._ctx 

     if not e == []: 
      raise Exception('\n> '*2+(''.join(e)).replace('\n','\n> ')) 
संबंधित मुद्दे