2013-04-18 8 views
18

में फ़ाइल ऑब्जेक्ट्स (खुले और बंद संचालन का विस्तार करने के लिए) को उपclassing मान लीजिए कि मैं open और close समय पर अतिरिक्त संचालन के साथ अंतर्निहित फ़ाइल अबास्ट्रक्शन का विस्तार करना चाहता हूं। अजगर 2.7 में यह काम करता है:पाइथन 3

class ExtFile(file): 
    def __init__(self, *args): 
     file.__init__(self, *args) 
     # extra stuff here 

    def close(self): 
     file.close(self) 
     # extra stuff here 
अब

मैं अजगर 3 करने के लिए कार्यक्रम है, जिसमें open एक कारखाने समारोह है कि io मॉड्यूल पर निर्भर करता है से कई अलग अलग वर्गों में से किसी का एक उदाहरण लौट सकता है अद्यतन करने में देख रहा हूँ इसे कैसे कहा जाता है। मैं सिद्धांत रूप से उन सभी को उपclass कर सकता था, लेकिन यह थकाऊ है, और मुझे प्रेषण को फिर से लागू करना होगा कि open करता है। (पाइथन 3 में बाइनरी और टेक्स्ट फाइलों के बीच भेद 2.x में इसके मुकाबले ज्यादा मायने रखता है, और मुझे दोनों की आवश्यकता है।) इन वस्तुओं को लाइब्रेरी कोड में पास किया जा रहा है जो उनके साथ कुछ भी कर सकता है, इसलिए मुहावरे एक "फ़ाइल जैसी" बतख-टाइप की गई कक्षा बनाने के लिए जो open के वापसी मूल्य को लपेटती है और आगे के आवश्यक विधियां सबसे अधिक वर्बोज़ होगी।

क्या कोई भी 3.x दृष्टिकोण सुझा सकता है जिसमें 2.x कोड से अधिक संभव के रूप में संभव अतिरिक्त अतिरिक्त बॉयलरप्लेट शामिल है?

+0

शायद फ़ाइल ऑब्जेक्ट को विस्तारित करने के बजाय आप एक 'ऑब्जेक्ट' कथन के साथ उपयोग करने के लिए एक कस्टम ऑब्जेक्ट बना सकते हैं? –

+1

एक वर्ग जो 'ओपन' के वापसी मूल्य को लपेटता है इतना बुरा क्यों होगा? थोक विधियों को आगे बढ़ाने के लिए आप '__getattr__' को ओवरराइड कर सकते हैं। –

+0

@ बेंजामिन होडसन यह काफी लंबा रहा है कि मुझे बिल्कुल याद नहीं आया कि मैं क्या सोच रहा था, लेकिन शायद यह "लाइनों का 9 0% करना आसान होगा और हर अंतिम कोने के मामले में एक बड़ा दर्द होगा पकड़ लिया।" मैंने पायथन में ऑब्जेक्ट आत्मनिरीक्षण के साथ बहुत कुछ नहीं किया है, और जब मैंने कोशिश की है तो यह मुझे भ्रमित तरीकों से फिसल गया है। – zwol

उत्तर

14

आप इसके बजाए एक संदर्भ प्रबंधक का उपयोग कर सकते हैं। उदाहरण के इस एक के लिए:

class SpecialFileOpener: 
    def __init__ (self, fileName, someOtherParameter): 
     self.f = open(fileName) 
     # do more stuff 
     print(someOtherParameter) 
    def __enter__ (self): 
     return self.f 
    def __exit__ (self, exc_type, exc_value, traceback): 
     self.f.close() 
     # do more stuff 
     print('Everything is over.') 

तो फिर तुम इस तरह इसका इस्तेमाल कर सकते हैं:

>>> with SpecialFileOpener('C:\\test.txt', 'Hello world!') as f: 
     print(f.read()) 

Hello world! 
foo bar 
Everything is over. 

साथ with फ़ाइल वस्तुओं (और अन्य संसाधनों) वैसे भी के लिए पसंद किया जाता है एक संदर्भ ब्लॉक का उपयोग करना।

+0

बक्षीस अनुरोध के जवाब में: आपको वास्तव में फ़ाइल ऑब्जेक्ट्स को उप-प्रकार नहीं करना चाहिए; विभिन्न फ़ाइल प्रकार सीधे कभी नहीं बनाए जाते हैं बल्कि इसके बजाय कुछ सलामी बल्लेबाज (उदा। 'ओपन', 'urlopen', ...) द्वारा बनाए जाते हैं। यदि आप कार्यक्षमता को विस्तारित करना चाहते हैं, तो आपको एक कस्टम * ओपनर * प्रदान करना चाहिए जो फ़ाइल की तरह ऑब्जेक्ट को लपेटता है और अतिरिक्त कार्यक्षमता प्रदान करता है। – poke

6

मुझे एक जैसी समस्या थी, और पायथन 2.x और 3.x दोनों का समर्थन करने की आवश्यकता थी।

class _file_obj(object): 
    """Check if `f` is a file name and open the file in `mode`. 
    A context manager.""" 
    def __init__(self, f, mode): 
     if isinstance(f, str): 
      self.file = open(f, mode) 
     else: 
      self.file = f 
     self.close_file = (self.file is not f) 
    def __enter__(self): 
     return self 
    def __exit__(self, *args, **kwargs): 
     if (not self.close_file): 
      return # do nothing 
     # clean up 
     exit = getattr(self.file, '__exit__', None) 
     if exit is not None: 
      return exit(*args, **kwargs) 
     else: 
      exit = getattr(self.file, 'close', None) 
      if exit is not None: 
       exit() 
    def __getattr__(self, attr): 
     return getattr(self.file, attr) 
    def __iter__(self): 
     return iter(self.file) 

यह अंतर्निहित फ़ाइल वस्तुओं के लिए सभी कॉल्स से गुजरता है और एक खुले फ़ाइल से या फ़ाइल नाम से प्रारंभ किया जा सकता है: क्या मैंने किया था निम्नलिखित (current full version) के समान था। एक संदर्भ प्रबंधक के रूप में भी काम करता है। this answer से प्रेरित हो गया।

+0

यह पूछे गए प्रश्न को संबोधित नहीं करता है; पाइथन 2 और पायथन 3 दोनों को संभालने वाला एक रैपर प्रदान करना विचलित हो रहा है क्योंकि पायथन 2 फ़ाइल ऑब्जेक्ट को विस्तारित करना काफी पहले से ही छोटा है और यह सवाल पूछा गया था क्योंकि पायथन 3 में ऐसा करना स्पष्ट रूप से कोई छोटा नहीं है। – ThorSummoner

+4

@ThorSummoner मुझे लगता है कि "पूछने के लिए अधिक से अधिक" जैसा नहीं है "प्रश्न को संबोधित नहीं करता"। यह देखते हुए कि पहले से ही एक जवाब था, मैंने सोचा कि यह एक विस्तारित संस्करण जोड़ने के लिए नुकसान नहीं पहुंचाएगा। जवाब बताता है कि कोड की अतिरिक्त कार्यक्षमता क्या है। हालांकि प्रतिक्रिया के लिए धन्यवाद। –

+0

मुझे यह सुझाव उपयोगी पाया गया। –

10

टीएल; डीएक संदर्भ प्रबंधक का उपयोग करें। उनके बारे में महत्वपूर्ण सावधानी के लिए इस उत्तर के नीचे देखें।


फ़ाइलें जबकि वहाँ कुछ तरीके कि सामान्य उपयोगकर्ता वर्गों पर इस्तेमाल किया जा सकता है, उन तरीकों में निर्मित वर्गों के साथ काम नहीं करते पायथन 3. में और अधिक जटिल हो गया। एक तरह से मिश्रण में करने के लिए इसे instanciating से पहले एक वांछित वर्ग है, लेकिन यह जानने के क्या मिश्रण में वर्ग पहले किया जाना चाहिए की आवश्यकता है:

class MyFileType(???): 
    def __init__(...) 
     # stuff here 
    def close(self): 
     # more stuff here 

क्योंकि वहाँ इतने सारे प्रकार के होते हैं, और अधिक संभवतः में जोड़ा जा सकता है भविष्य (असंभव, लेकिन संभव), और हम निश्चित रूप से नहीं जानते हैं कि के बाद open पर कॉल करने के बाद वापस लौटाया जाएगा, यह विधि काम नहीं करती है।

एक अन्य विधि हमारे कस्टम प्रकार के लिए लौट आए उदाहरण के __class__ विशेषता को संशोधित करने के लिए लौट आए फ़ाइल के ___bases__, और करने के लिए दोनों हमारे कस्टम प्रकार बदलने के लिए है:

class MyFileType: 
    def close(self): 
     # stuff here 

some_file = open(path_to_file, '...') # ... = desired options 
MyFileType.__bases__ = (some_file.__class__,) + MyFile.__bases__ 

लेकिन यह अभी तक पैदावार

Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: __bases__ assignment: '_io.TextIOWrapper' deallocator differs from 'object' 

एक और तरीका जो शुद्ध उपयोगकर्ता वर्गों के साथ काम कर सकता है वह फ्लाई पर कस्टम फ़ाइल प्रकार बनाना है, सीधे लौटाए गए इंस्टेंस के वर्ग से, और फिर लौटा हुआ इंस्टेंस क्लास अपडेट करें:

some_file = open(path_to_file, '...') # ... = desired options 

class MyFile(some_file.__class__): 
    def close(self): 
     super().close() 
     print("that's all, folks!") 

some_file.__class__ = MyFile 

लेकिन फिर से:

Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: __class__ assignment: only for heap types 

इसलिए, यह सबसे अच्छा तरीका है कि अजगर 3 में सब पर काम करेंगे, और सौभाग्य से भी अजगर 2 में काम करेंगे की तरह दिखता है (उपयोगी आप एक ही कोड चाहते हैं

class Open(object): 
    def __init__(self, *args, **kwds): 
     # do custom stuff here 
     self.args = args 
     self.kwds = kwds 
    def __enter__(self): 
     # or do custom stuff here :) 
     self.file_obj = open(*self.args, **self.kwds) 
     # return actual file object so we don't have to worry 
     # about proxying 
     return self.file_obj 
    def __exit__(self, *args): 
     # and still more custom stuff here 
     self.file_obj.close() 
     # or here 

और इसका इस्तेमाल करने के:

with Open('some_file') as data: 
    # custom stuff just happened 
    for line in data: 
     print(line) 
# data is now closed, and more custom stuff 
# just happened 
दोनों संस्करणों पर काम करने की) आधार एक कस्टम संदर्भ प्रबंधक है

एक महत्वपूर्ण बात को ध्यान में रखना: __init__ या __enter__ में किसी भी बिना क्रिया का अपवाद चलने से __exit__ कर पाएगा, इसलिए उन दो स्थानों में आप अभी भी यकीन है कि आप डॉन बनाने के लिए try/except और/या try/finally मुहावरों का प्रयोग करना होगा संसाधनों को रिसाव नहीं करें।

+0

मेरे साथ यह स्पष्ट करने के लिए धन्यवाद। मेरे विशेष मामले में मुझे पता है कि फाइल हमेशा 'io.TextIOWrapper' होगी। क्या TextIowrapper वर्ग का विस्तार करना काफी महत्वपूर्ण है? और यदि मैं इसे विस्तारित कर सकता हूं और उस एक्सटेंशन का उपयोग कर सकता हूं, तो मैं टेक्स्ट फ़ाइल प्राप्त करने के मामले को कहां से संभाल सकता हूं? – ThorSummoner

+0

क्या आप इन फ़ाइलों को खोले जाने के नियंत्रण में हैं, या आप अन्य मॉड्यूल द्वारा खोले गए फ़ाइलों को ट्रैक करना चाहते हैं? –

+0

मैं पूरी फ़ाइल ओपन ऑपरेशन को नियंत्रित करता हूं। एक मध्य-चरण जिसे मैं प्राप्त करना चाहता हूं वह फ़ाइल के हैशसम करने के लिए एक विधि जोड़ना है। – ThorSummoner

संबंधित मुद्दे