খোলার জন্য ফাইল নামগুলিতে পাস করতে হবে, বা ফাইল খুলতে হবে?


53

ধরুন আমার একটি ফাংশন রয়েছে যা একটি পাঠ্য ফাইল দিয়ে কাজ করে - উদাহরণস্বরূপ এটি থেকে পড়ে এবং 'ক' শব্দটি সরিয়ে দেয়। আমি হয় এটির কোনও ফাইলনাম পাস করতে পারি এবং ফাংশনটিতে খোলার / সমাপ্তি পরিচালনা করতে পারতাম, বা আমি এটি খোলা ফাইলটি পাস করতে পারি এবং আশা করি যে যারাই এটিকে ডাকবে এটি বন্ধ করার সাথে মোকাবেলা করবে।

প্রথম উপায়টি কোনও ফাইল খোলা না রেখে গ্যারান্টি দেওয়ার আরও ভাল পদ্ধতির মতো বলে মনে হয় তবে স্ট্রিংআইও অবজেক্টের মতো জিনিস ব্যবহার থেকে আমাকে বাধা দেয়

দ্বিতীয় উপায়টি কিছুটা বিপজ্জনক হতে পারে - ফাইলটি বন্ধ হবে কিনা তা জানার উপায় নেই, তবে আমি ফাইলের মতো বস্তু ব্যবহার করতে সক্ষম হব

def ver_1(filename):
    with open(filename, 'r') as f:
        return do_stuff(f)

def ver_2(open_file):
    return do_stuff(open_file)

print ver_1('my_file.txt')

with open('my_file.txt', 'r') as f:
    print ver_2(f)

এর মধ্যে কোনটি সাধারণত পছন্দ হয়? সাধারণত কি প্রত্যাশা করা হয় যে কোনও ফাংশন এই দুটি উপায়ে কোনও একটিতে আচরণ করবে? অথবা এটি ঠিক এমনভাবে নথিভুক্ত করা উচিত যাতে প্রোগ্রামার যথাযথভাবে ফাংশনটি ব্যবহার করতে পারে?

উত্তর:


39

সুবিধাজনক ইন্টারফেস ভাল, এবং কখনও কখনও যাওয়ার উপায়। যাইহোক, বেশিরভাগ সময় ভাল কম্পোসিবিলিটি সুবিধার চেয়ে গুরুত্বপূর্ণ , কারণ একটি কম্পোজেবল বিমূর্ততা আমাদের উপরে এটির অন্যান্য কার্যকারিতা (সহ সুবিধাযুক্ত মোড়ক) প্রয়োগ করতে দেয়।

আপনার ফাংশন ফাইল ব্যবহারের সর্বাধিক সাধারণ উপায় হ'ল একটি খোলা ফাইল হ্যান্ডেলটিকে প্যারামিটার হিসাবে গ্রহণ করা, কারণ এটি ফাইল হ্যান্ডলগুলিও ফাইল সিস্টেমের অংশ নয় (যেমন পাইপ, সকেট,…) ব্যবহার করতে দেয়:

def your_function(open_file):
    return do_stuff(open_file)

যদি বানানটি with open(filename, 'r') as f: result = your_function(f)আপনার ব্যবহারকারীদের জিজ্ঞাসা করার জন্য খুব বেশি হয় তবে আপনি নিম্নলিখিত সমাধানগুলির মধ্যে একটি চয়ন করতে পারেন:

  • your_functionপ্যারামিটার হিসাবে একটি খোলা ফাইল বা ফাইলের নাম নেয়। যদি এটি কোনও ফাইলের নাম হয় তবে ফাইলটি খোলা এবং বন্ধ হয়ে যায় এবং ব্যতিক্রমগুলি প্রচারিত হয়। অস্পষ্টতার সাথে এখানে কিছুটা সমস্যা রয়েছে যা নাম যুক্তি ব্যবহার করে কাজ করা যেতে পারে around
  • একটি সাধারণ মোড়ক সরবরাহ করুন যা ফাইলটি খোলার ক্ষেত্রে যত্ন নেয়, যেমন

    def your_function_filename(file):
        with open(file, 'r') as f:
            return your_function(f)
    

    আমি সাধারণত এপিআই ব্লোটের মতো ফাংশনগুলি উপলব্ধি করি তবে তারা যদি সাধারণত ব্যবহৃত কার্যকারিতা সরবরাহ করে তবে প্রাপ্ত সুবিধাটি যথেষ্ট শক্তিশালী যুক্তি।

  • মোড়ানো with openআরেকটি composable ফাংশনে কার্যকারিতা:

    def with_file(filename, callback):
        with open(filename, 'r') as f:
            return callback(f)
    

    হিসাবে with_file(name, your_function)বা আরও জটিল ক্ষেত্রে ব্যবহৃতwith_file(name, lambda f: some_function(1, 2, f, named=4))


6
এই পদ্ধতির একমাত্র ব্যর্থতা হ'ল কখনও কখনও ফাইল-জাতীয় বস্তুর নাম প্রয়োজন হয়, যেমন ত্রুটি প্রতিবেদন করার জন্য: শেষ ব্যবহারকারীরা "স্ট্রিম @ 0x03fd2bb6> এ ত্রুটি না করে" "foo.cfg (12) এ ত্রুটি" দেখতে পছন্দ করেন (12) "। এই ক্ষেত্রে একটি alচ্ছিক "স্ট্রিম_নাম" যুক্তি your_functionব্যবহার করা যেতে পারে।

22

আসল প্রশ্ন সম্পূর্ণতা এক। আপনার ফাইল প্রসেসিং ফাইলটির সম্পূর্ণ প্রক্রিয়াজাতকরণ কাজ করে, বা এটি প্রক্রিয়াজাতকরণের ধাপগুলির একটি শৃঙ্খলে কেবল একটি টুকরা? যদি এটি সম্পূর্ণরূপে এবং এর নিজস্ব হয় তবে কোনও ফাংশনের মধ্যে সমস্ত ফাইল অ্যাক্সেসকে নির্দ্বিধায় নির্দ্বিধায় মনে করুন।

def ver(filepath):
    with open(filepath, "r") as f:
        # do processing steps on f
        return result

withবিবৃতিটির শেষে সম্পদ চূড়ান্ত করার (ফাইলটি বন্ধ করা) খুব সুন্দর সম্পত্তি এটি রয়েছে ।

তবে সেখানে সম্ভবত ইতিমধ্যেই খোলা ফাইল, তারপর আপনার এর পার্থক্য প্রক্রিয়াকরণের জন্য প্রয়োজন হয়, তাহলে ver_1ver_2আরো ইন্দ্রিয় তোলে। উদাহরণ স্বরূপ:

def _ver_file(f):
    # do processing steps on f
    return result

def ver(fileobj):
    if isinstance(fileobj, str):
        with open(fileobj, 'r') as f:
            return _ver_file(f)
    else:
        return _ver_file(fileobj)

এই ধরণের সুস্পষ্ট ধরণের পরীক্ষার জন্য প্রায়শই জোর করে বোঝানো হয় , বিশেষত জাভা, জুলিয়া এবং গোয়ের মতো ভাষায় যেখানে টাইপ- বা ইন্টারফেস-ভিত্তিক প্রেরণ সরাসরি সমর্থিত is পাইথনে, তবে টাইপ-ভিত্তিক প্রেরণের জন্য কোনও ভাষা সমর্থন নেই। আপনি পাইথনে মাঝে মাঝে সরাসরি টাইপ-টেস্টিংয়ের সমালোচনা দেখতে পাবেন, তবে বাস্তবে এটি অত্যন্ত সাধারণ এবং বেশ কার্যকর। এটি একটি ফাংশনকে উচ্চতর ডিগ্রি অর্জনের জন্য সক্ষম করে, ডেটিটাইপগুলি যেভাবে আসতে পারে তা পরিচালনা করে, যেমন "হাঁসের টাইপিং"। শীর্ষস্থানীয় আন্ডারস্কোরটি নোট করুন _ver_file; এটি একটি "ব্যক্তিগত" ফাংশন (বা পদ্ধতি) নির্ধারণ করার একটি প্রচলিত উপায়। যদিও এটি প্রযুক্তিগতভাবে সরাসরি বলা যেতে পারে, এটি প্রস্তাব দেয় যে ফাংশনটি সরাসরি বাহ্যিক ব্যবহারের জন্য নয়।


2019 আপডেট: প্রদত্ত পাইথন 3 সাম্প্রতিক আপডেট, উদাহরণস্বরূপ পাথ এখন সম্ভাব্য সংরক্ষণ করা হয় যে হিসাবে pathlib.Pathশুধু বস্তু strবা bytes(3.4+), এবং যে টাইপ হিন্টিং হচ্ছে (প্রায় 3.6+ যদিও এখনও সক্রিয়ভাবে নব্য) মূলধারার গূঢ় থেকে গেছে, এখানে আপডেট হওয়া কোড যা এই অগ্রগতিগুলিকে আমলে নেয়:

from pathlib import Path
from typing import IO, Any, AnyStr, Union

Pathish = Union[AnyStr, Path]  # in lieu of yet-unimplemented PEP 519
FileSpec = Union[IO, Pathish]

def _ver_file(f: IO) -> Any:
    "Process file f"
    ...
    return result

def ver(fileobj: FileSpec) -> Any:
    "Process file (or file path) f"
    if isinstance(fileobj, (str, bytes, Path)):
        with open(fileobj, 'r') as f:
            return _ver_file(f)
    else:
        return _ver_file(fileobj)

1
হাঁসের টাইপিং পরীক্ষা করা হবে তার ভিত্তিতে যা বস্তুর সাথে আপনি কী করতে পারেন তার উপর নির্ভর করে test উদাহরণস্বরূপ, readফাইল-জাতীয় হতে পারে এমন কিছুতে কল করার চেষ্টা করা , বা কল করা open(fileobj, 'r')এবং TypeErrorযদি fileobjস্ট্রিং নয় তা ধরা ।
ব্যবহারকারী 2357112

আপনি হাঁস টাইপিং জন্য তর্ক করছি ব্যবহারের । উদাহরণটি হাঁসের টাইপিং কার্যকরভাবে প্রদান করে - এটি হ'ল ব্যবহারকারীরা verপ্রকারের চেয়ে আলাদা অপারেশন পান । verহাঁসের টাইপিংয়ের মাধ্যমে এটি প্রয়োগ করাও সম্ভব হতে পারে , যেমনটি আপনি বলেছেন। তবে তারপরে ব্যতিক্রমগুলি তৈরি করা সহজ ধরণের পরিদর্শনের চেয়ে ধীর এবং আইএমও কোনও বিশেষ সুবিধা দেয় না (স্পষ্টতা, সাধারণতা ইত্যাদি) আমার অভিজ্ঞতায়, হাঁসের টাইপিং "বড় আকারের," তবে "প্রতিরক্ষার পক্ষে নিরপেক্ষ" ছোট । "
জোনাথন ইউনিস

3
না, আপনি এখনও যা করছেন তা হাঁসের টাইপিং নয়। একটি hasattr(fileobj, 'read')পরীক্ষা হাঁসের টাইপিং হবে; একটি isinstance(fileobj, str)পরীক্ষা হয় না। পার্থক্যের উদাহরণ এখানে: isinstanceইউনিকোড ফাইলের সাথে পরীক্ষা ব্যর্থ হয়, যেহেতু u'adsf.txt'এটি একটি নয় str। আপনি খুব নির্দিষ্ট ধরণের জন্য পরীক্ষা করেছেন। কলিং openবা কিছু অনুমানমূলক does_this_object_represent_a_filenameফাংশনের উপর ভিত্তি করে একটি হাঁসের টাইপিং টেস্টের সমস্যা নেই।
ব্যবহারকারী 2357112

1
তাহলে কোড একটি ব্যাখ্যামূলক উদাহরণ চেয়ে উৎপাদন কোড বরং ছিল, সেজন্য আমিও যে সমস্যা হতো না, কারণ আমি ব্যবহার করা হবে না is_instance(x, str)কিছু বরং is_instance(x, string_types), সঙ্গে string_typesসঠিকভাবে PY2 এবং PY3 জুড়ে সঠিক অপারেশন জন্য সেট। এমন কিছু দেওয়া যা একটি স্ট্রিংয়ের মতো কোঁক করে, verসঠিকভাবে প্রতিক্রিয়া জানায় ; এমন কিছু দেওয়া যা কোনও ফাইলের মতো একই রকম থাকে। এর ব্যবহারকারীর কাছে verকোনও তফাত থাকবে না - ধরণের পরিদর্শন বাস্তবায়ন দ্রুত চালিত হবে। হাঁস পিউরিস্ট: দ্বিধা দ্বিধায় নির্দ্বিধায়
জোনাথন ইউনিস

5

আপনি যদি ফাইল হ্যান্ডেলের পরিবর্তে ফাইলের নামটি পাস করেন তবে দ্বিতীয় ফাইলটি প্রথম ফাইলটি যখন প্রথমটি उघडবে তখন একই ফাইলের কোনও নিশ্চয়তা নেই; এটি সঠিকতা বাগ এবং সুরক্ষা গর্ত হতে পারে।


1
সত্য। তবে এটি অবশ্যই অন্য ট্রেডঅফের সাথে ভারসাম্যপূর্ণ হতে হবে: আপনি যদি কোনও ফাইল হ্যান্ডেলটি অতিক্রম করেন তবে সমস্ত পাঠককে অবশ্যই তাদের অ্যাক্সেসগুলি ফাইলের সাথে সমন্বয় করতে হবে, কারণ প্রতিটি "বর্তমান ফাইলের অবস্থান" সরাতে পারে।
জোনাথন ইউনিস

@ জোনাথনউনিস: কোন অর্থে সমন্বয়? তাদের যা করার দরকার তা হ'ল ফাইলের অবস্থান যেখানেই তারা চান সেখানে সেট করা।
মেহেরদাবাদ

1
যদি ফাইলটি পড়তে একাধিক সত্ত্বা থাকে তবে নির্ভরতা থাকতে পারে। অন্য যেখান থেকে ছেড়ে গিয়েছিল (বা পূর্ববর্তী পড়া দ্বারা পড়া ডেটা দ্বারা সংজ্ঞায়িত স্থানে) এমন একের প্রয়োজন হতে পারে। এছাড়াও, পাঠকরা বিভিন্ন থ্রেডে চলতে পারে, কৃমির অন্যান্য সমন্বয় ক্যান খোলার চেষ্টা করে। পাস করা চারপাশের ফাইল অবজেক্টগুলি সমস্ত সমস্যা (সেইসাথে সুবিধাগুলি) সহ প্রযোজ্য বিশ্বব্যাপী রাষ্ট্রে পরিণত হয়।
জোনাথন ইউনিস

1
এটি মূল পাতানো ফাইল পাথের পাশ দিয়ে যাচ্ছেনা। এটির একটি ফাংশন (বা শ্রেণি, পদ্ধতি, বা নিয়ন্ত্রণের অন্যান্য লোকস) থাকছে "ফাইলটির সম্পূর্ণ প্রক্রিয়াজাতকরণ" এর জন্য দায়বদ্ধতা গ্রহণ করে। যদি ফাইল অ্যাক্সেসগুলি কোথাও এম্পসুলেটেড হয় তবে আপনাকে খোলা ফাইল হ্যান্ডলের মতো পরিবর্তনীয় গ্লোবাল স্টেটের কাছাকাছি যাওয়ার দরকার হবে না।
জোনাথন ইউনিস

1
ঠিক আছে, আমরা তখন একমত হতে সম্মত হতে পারি। আমি বলছি এমন ডিজাইনের একটি সিদ্ধান্ত নেওয়ার দিক রয়েছে যা চূড়ান্তভাবে পরিবর্তনীয় বৈশ্বিক রাষ্ট্রের বাইরে চলে যায়। কিছু সুবিধা আছে। সুতরাং, একটি "বাণিজ্য।" ফাইল পাথগুলি অতিক্রম করে এমন নকশাগুলি প্রায়শই I / O কে এক ঝাঁকুনিতে পড়ে যায় enc আমি এটি একটি সুবিধাজনক দম্পতি হিসাবে দেখতে। YMMV।
জোনাথন ইউনিস

1

এটি মালিকানা এবং ফাইলটি বন্ধ করার দায়িত্ব সম্পর্কে। আপনি একটি স্ট্রিম বা ফাইল হ্যান্ডেল বা যাই হোক না কেন অপরিচিত যে বন্ধ করে দেওয়া উচিত / অন্য পদ্ধতি কিছু সময়ে বিন্যস্ত পাস করতে পারেন, যতদিন আপনি নিশ্চিত এটা স্পষ্ট যারা মালিক এবং নির্দিষ্ট এটা হবে মালিক দ্বারা বন্ধ হয়ে যাবে যখন আপনি কাজ হয়ে গেলে । এটি সাধারণত একটি চেষ্টা-অবশেষে নির্মাণ বা নিষ্পত্তিযোগ্য প্যাটার্ন জড়িত।


-1

আপনি যদি খোলা ফাইলগুলি পাস করার সিদ্ধান্ত নেন তবে নীচের মতো কিছু করতে পারেন তবে ফাইলে যে ফাংশনে লেখা আছে তাতে ফাইলের কাছে আপনার কোনও অ্যাক্সেস নেই।

আমি যদি ফাইল / স্ট্রিম অপারেশন এবং অন্যান্য ক্লাস বা ফাংশনের জন্য 100% দায়বদ্ধ এমন ক্লাস করতে চাইতাম যা নিরীহ হবে এবং ফাইল বা স্ট্রিমগুলি খুলতে বা বন্ধ করার প্রত্যাশা করে না তবে আমি এটি করব।

মনে রাখবেন যে প্রসঙ্গ পরিচালকরা শেষ অবধি থাকার মতো কাজ করে। সুতরাং যদি কোনও ব্যতিক্রম লেখক ফাংশনে ফেলে দেওয়া হয় তবে ফাইলটি বন্ধ হয়ে যাচ্ছে যাই হোক না কেন।

import contextlib

class FileOpener:

    def __init__(self, path_to_file):
        self.path_to_file = path_to_file

    @contextlib.contextmanager
    def open_write(self):
        # ...
        # Here you can add code to create the directory that will accept the file.
        # ...
        # And you can add code that will check that the file does not exist 
        # already and maybe raise FileExistsError
        # ...
        try:            
            with open(self.path_to_file, "w") as file:
                print(f"open_write: has opened the file with id:{id(file)}")            
                yield file                
        except IOError:
            raise
        finally:
            # The try/catch/finally is not mandatory (except if you want to manage Exceptions in an other way, as file objects have predefined cleanup actions 
            # and when used with a 'with' ie. a context manager (not the decorator in this example) 
            # are closed even if an error occurs. Finally here is just used to demonstrate that the 
            # file was really closed.
            print(f"open_write: has closed the file with id:{id(file)} - {file.closed}")        


def writer(file_open, data, raise_exc):
    with file_open() as file:
        print("writer: started writing data.")
        file.write(data)
        if raise_exc:
            raise IOError("I am a broken data cable in your server!")
        print("writer: wrote data.")
    print("writer: finished.")

if __name__ == "__main__":
    fo = FileOpener('./my_test_file.txt')    
    data = "Hello!"  
    raise_exc = False  # change me to True and see that the file is closed even if an Exception is raised.
    writer(fo.open_write, data, raise_exc)

এটি কেবল ব্যবহারের চেয়ে আরও ভাল / আলাদা কীভাবে with open? এই ফাইলটি ফাইল-বনাম ফাইল-জাতীয় বস্তু ব্যবহারের প্রশ্নটিকে কীভাবে সম্বোধন করবে?
ড্যান্ন্নো

এটি আপনাকে ফাইল / স্ট্রিমের উন্মুক্ত / বন্ধ আচরণ লুকানোর একটি উপায় দেখায়। আপনি মন্তব্যগুলিতে পরিষ্কারভাবে দেখতে পাচ্ছেন এটি "লেখক" -এর স্বচ্ছ স্ট্রিম / ফাইল খোলার আগে যুক্তি যুক্ত করার উপায় দেয়। "লেখক" অন্য প্যাকেজের শ্রেণীর একটি পদ্ধতি হতে পারে। সংক্ষেপে এটি খোলার একটি মোড়ক। এছাড়াও, জবাব এবং ভোট দেওয়ার জন্য আপনাকে ধন্যবাদ।
Vls

এই আচরণটি ইতিমধ্যে সামলানো হয়েছে with open, তাই না? এবং আপনি কার্যকরভাবে যার পক্ষে পরামর্শ দিচ্ছেন তা হ'ল এমন একটি ফাংশন যা কেবল ফাইল-জাতীয় বস্তু ব্যবহার করে, এবং সেখান থেকে উদ্ভূত হয় না?
ডান্ন্নো
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.