কীভাবে open ওপেন (…) `এবং` sys.stdout` দিয়ে দুর্দান্তভাবে পরিচালনা করবেন?


96

প্রায়শই আমাকে হয় ফাইল করতে ডেটা আউটপুট করতে হয়, অথবা যদি ফাইল নির্দিষ্ট না করা হয়, স্টডআউট করার জন্য। আমি নিম্নলিখিত স্নিপেট ব্যবহার:

if target:
    with open(target, 'w') as h:
        h.write(content)
else:
    sys.stdout.write(content)

আমি এটিকে আবার লিখতে এবং উভয় লক্ষ্যকে অভিন্নভাবে পরিচালনা করতে চাই।

আদর্শ ক্ষেত্রে এটি হবে:

with open(target, 'w') as h:
    h.write(content)

তবে এটি ভালভাবে কাজ করবে না কারণ withব্লক ছাড়ার সময় sys.stdout বন্ধ হয়ে যায় এবং আমি এটি চাই না। আমিও চাই না

stdout = open(target, 'w')
...

কারণ মূল স্টডআউটটি পুনরুদ্ধার করতে আমার মনে রাখা দরকার।

সম্পর্কিত:

সম্পাদনা করুন

আমি জানি যে আমি মোড়তে targetপারি, পৃথক ফাংশন সংজ্ঞায়িত করতে পারি বা প্রসঙ্গ পরিচালককে ব্যবহার করতে পারি । আমি একটি সরল, মার্জিত, আইডোম্যাটিক সলিউশন ফিটিংয়ের সন্ধান করছি যার জন্য 5 টির বেশি লাইনের প্রয়োজন হবে না


খুব খারাপ আপনি পূর্বে সম্পাদনাটি যোগ করেননি;) যাইহোক ... বিকল্পভাবে আপনি কেবল নিজের খোলা ফাইলটি পরিষ্কার করার জন্য বিরক্ত করতে পারবেন না: পি
ওল্ফ

উত্তর:


94

এখানে বাক্সের বাইরে কেবল ভাবছি, একটি কাস্টম open()পদ্ধতি সম্পর্কে কীভাবে ?

import sys
import contextlib

@contextlib.contextmanager
def smart_open(filename=None):
    if filename and filename != '-':
        fh = open(filename, 'w')
    else:
        fh = sys.stdout

    try:
        yield fh
    finally:
        if fh is not sys.stdout:
            fh.close()

এটি এর মতো ব্যবহার করুন:

# For Python 2 you need this line
from __future__ import print_function

# writes to some_file
with smart_open('some_file') as fh:
    print('some output', file=fh)

# writes to stdout
with smart_open() as fh:
    print('some output', file=fh)

# writes to stdout
with smart_open('-') as fh:
    print('some output', file=fh)

31

আপনার বর্তমান কোডটি আটকে দিন। এটি সহজ এবং এটি কেবল তাকিয়ে আপনি ঠিক বলতে পারেন ।

আর একটি উপায় একটি ইনলাইন সহ হবে if:

handle = open(target, 'w') if target else sys.stdout
handle.write(content)

if handle is not sys.stdout:
    handle.close()

তবে এটি আপনার কাছে যা আছে তার চেয়ে খুব কম নয় এবং এটি তাত্ক্ষণিকভাবে খারাপ দেখায়।

আপনি sys.stdoutঅযৌক্তিকও করতে পারেন, তবে এটি পাইথোনিকের খুব বেশি লাগে না:

sys.stdout.close = lambda: None

with (open(target, 'w') if target else sys.stdout) as handle:
    handle.write(content)

4
: আপনি দীর্ঘ হিসাবে ভাল এটির জন্য একটি প্রসঙ্গ ব্যবস্থাপক করে এটি প্রয়োজন হিসাবে হিসাবে জন্য unclosability রাখতে পারবেন না with unclosable(sys.stdout): ...সেটিং দ্বারা sys.stdout.close = lambda: Noneএই প্রেক্ষাপটে ম্যানেজার ভিতরে এবং পুরানো মান এটা পরে রিসেট। তবে এটি মনে হয় খানিকটা দূরে নিয়ে
এসেছিল

4
আমি "এটি ছেড়ে দিন, আপনি ঠিক এটি কী করছেন" এবং ভয়াবহ অযৌক্তিক পরামর্শের পক্ষে ভোট দেওয়ার মধ্যে ভোট দেওয়ার মধ্যে আমার মধ্যে ছিঁড়ে গেছে!
গ্রিনএজজেড

@GreenAsJade আমি মনে করি না যে তিনি কি পরামর্শ উপার্জন sys.stdoutunclosable শুধু লক্ষ tht এটি সম্পন্ন করা যেতে পারে। খারাপ ধারণা প্রদর্শন করা এবং সেগুলি খারাপ না হওয়ার কারণ তাদের উল্লেখ না করার চেয়ে ব্যাখ্যা করা ভাল এবং আশা করা যায় যে তারা অন্যের দ্বারা হোঁচট খাচ্ছে না।
সিজেএস

8

আপনি যখন ইএএফপি করতে পারবেন তখন এলবিওয়াইএল কেন?

try:
    with open(target, 'w') as h:
        h.write(content)
except TypeError:
    sys.stdout.write(content)

কেন আপনি যখন এটি একটি বিভ্রান্ত উপায়ে কাজ করতে হয় তখন with/ asব্লকটি অভিন্নভাবে ব্যবহার করতে পুনরায় লেখেন? আপনি আরও লাইন যুক্ত করবেন এবং কার্যকারিতা হ্রাস করবেন।


4
রুটিনের "স্বাভাবিক" প্রবাহ নিয়ন্ত্রণ করতে ব্যতিক্রমগুলি ব্যবহার করা উচিত নয় । কর্মক্ষমতা? বুবলিং-আপ একটি ত্রুটি দ্রুত হবে যে যদি / অন্যথায়?
জাকুব এম

4
আপনি এক বা অন্যটি ব্যবহার করবেন এমন সম্ভাবনার উপর নির্ভর করে।
2rs2ts

32
@ জাকুবএম ব্যতিক্রম পাইথন-এ হতে পারে এবং ব্যবহার করা যেতে পারে।
গ্যারেথ লেটি

13
পাইথনের forলুপটি আউটরেটারের দ্বারা থামানো স্টপআইটারেশন ত্রুটিটি ধরা পড়ে এটি শেষ হয়ে চলেছে, আমি বলব যে প্রবাহ নিয়ন্ত্রণের ক্ষেত্রে ব্যতিক্রমগুলি সম্পূর্ণ পিত্তোনিক।
কর্ক স্ট্রুয়েজার

4
ধরে targetনেওয়া Noneযখন sys.stdout উদ্দেশ্যে করা হয়, আপনার TypeErrorপরিবর্তে এটি ধরতে হবে IOError
টোরিক

5

আর একটি সম্ভাব্য সমাধান: প্রসঙ্গ পরিচালকের প্রস্থান পদ্ধতি এড়ানোর চেষ্টা করবেন না, কেবল স্টাডআউটকে নকল করুন।

with (os.fdopen(os.dup(sys.stdout.fileno()), 'w')
      if target == '-'
      else open(target, 'w')) as f:
      f.write("Foo")

5

ওল্ফের উত্তরের একটি উন্নতি

import sys
import contextlib

@contextlib.contextmanager
def smart_open(filename: str, mode: str = 'r', *args, **kwargs):
    '''Open files and i/o streams transparently.'''
    if filename == '-':
        if 'r' in mode:
            stream = sys.stdin
        else:
            stream = sys.stdout
        if 'b' in mode:
            fh = stream.buffer  # type: IO
        else:
            fh = stream
        close = False
    else:
        fh = open(filename, mode, *args, **kwargs)
        close = True

    try:
        yield fh
    finally:
        if close:
            try:
                fh.close()
            except AttributeError:
                pass

এটি বাইনারি আইওকে অনুমতি দেয় এবং প্রকৃতপক্ষে কোনও ফাইলের নাম হয় openকিনা filenameতা অবশেষে বহিরাগত যুক্তিগুলি পাস করতে দেয় pass


1

আমি একটি সাধারণ র‍্যাপার ফাংশনও যাব, যা আপনি যদি মোডটিকে উপেক্ষা করতে পারেন তবে এটি বেশ সহজ হতে পারে (এবং ফলস্বরূপ স্টিডিন বনাম স্টডআউট), উদাহরণস্বরূপ:

from contextlib import contextmanager
import sys

@contextmanager
def open_or_stdout(filename):
    if filename != '-':
        with open(filename, 'w') as f:
            yield f
    else:
        yield sys.stdout

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

4
আমি ValueError: I/O operation on closed fileযদি with open_or_stdout(..)ব্লকের বাইরে ফাইলটিতে লেখার চেষ্টা করি তবে আমি পেয়েছি । আমি কী মিস করছি? sys.stdout বন্ধ করা বোঝানো হয় না।
টম্মি কমুলাইনেন

1

ঠিক আছে, যদি আমরা ওয়ান-লাইন যুদ্ধে লিপ্ত হই, তবে এখানে দেখুন:

(target and open(target, 'w') or sys.stdout).write(content)

আমি যাকোবের মূল উদাহরণটি পছন্দ করি যতক্ষণ প্রসঙ্গে কেবল এক জায়গায় লেখা থাকে। আপনি অনেক লেখকের জন্য ফাইলটি পুনরায় খোলার সমাপ্ত হলে সমস্যা হবে problem আমি মনে করি আমি কেবল একবার স্ক্রিপ্টের শীর্ষে সিদ্ধান্ত নেব এবং সিস্টেমটি প্রস্থান করার সময় ফাইলটি বন্ধ করতে দেব:

output = target and open(target, 'w') or sys.stdout
...
output.write('thing one\n')
...
output.write('thing two\n')

আপনি যদি এর আরও পরিপাটি মনে করেন তবে আপনি নিজের বহির্গমন হ্যান্ডলারটি অন্তর্ভুক্ত করতে পারেন

import atexit

def cleanup_output():
    global output
    if output is not sys.stdout:
        output.close()

atexit(cleanup_output)

আমি মনে করি না যে আপনার ওয়ান-লাইনার ফাইল অবজেক্টটি বন্ধ করে দেয়। আমি কি ভূল?
2rs2ts

4
@ 2rs2ts - এটি ... শর্তযুক্ত করে। ফাইলের অবজেক্টের পুনঃতফসিল শূন্যে চলে যায় কারণ এতে কোনও ভেরিয়েবল ইঙ্গিত করে না, সুতরাং এর __del__ পদ্ধতিটি তাত্ক্ষণিকভাবে (সিপিথনে) বলা যেতে পারে বা পরে আবর্জনা সংগ্রহের সময় পাওয়া যায়। ডকটিতে সতর্কতা রয়েছে যে এটি সর্বদা কার্যকর হবে তবে আমি এটি সর্বদা সংক্ষিপ্ত স্ক্রিপ্টগুলিতে ব্যবহার করি না trust এমন একটি বড় কিছু যা দীর্ঘ সময় চলে এবং প্রচুর ফাইল খোলে ... ভাল আমি অনুমান করি যে আমি 'সহ' বা 'চেষ্টা / অবশেষে' ব্যবহার করব।
tdelaney

তিল আমি জানতাম না যে ফাইলের জিনিসগুলি 'এটি __del__করবে।
2rs2ts

@ 2rs2ts: সিপিথন একটি রেফারেন্স গণনা আবর্জনা সংগ্রহকারী ব্যবহার করে (প্রয়োজন অনুসারে নীচে "প্রকৃত" জিসি সহ) এটি স্ট্রিম-হ্যান্ডেলের সমস্ত রেফারেন্স বাদ দেওয়ার সাথে সাথে ফাইলটি বন্ধ করে দিতে পারে। জাইথন ​​এবং স্পষ্টতই আয়রন পাইথনের কেবলমাত্র "আসল" জিসি রয়েছে যাতে তারা কোনও শেষ জিসি না হওয়া পর্যন্ত ফাইলটি বন্ধ না করে।
টোরিক

0

আপনার যদি সত্যিই আরও কিছু "মার্জিত", অর্থাত্ ওয়ান-লাইনারের উপর জোর দিতে হবে:

>>> import sys
>>> target = "foo.txt"
>>> content = "foo"
>>> (lambda target, content: (lambda target, content: filter(lambda h: not h.write(content), (target,))[0].close())(open(target, 'w'), content) if target else sys.stdout.write(content))(target, content)

foo.txtপ্রদর্শিত হয় এবং পাঠ্য ধারণ করে foo


এটি কোডগল্ফ স্ট্যাকএক্সচেঞ্জে স্থানান্তরিত হওয়া উচিত: ডি
কায়সার

0

কিভাবে sys.stdout জন্য একটি নতুন এফডি খোলার? এইভাবে এটি বন্ধ করতে আপনার কোনও সমস্যা হবে না:

if not target:
    target = "/dev/stdout"
with open(target, 'w') as f:
    f.write(content)

4
দুঃখের বিষয়, এই অজগর স্ক্রিপ্টটি চালানোর জন্য আমার ইনস্টলে একটি সুডো দরকার। / dev / stdout রুটের মালিকানাধীন।
মনুর

অনেক পরিস্থিতিতে স্টাডাউটে একটি এফডি পুনরায় খোলার বিষয়টি প্রত্যাশিত নয়। উদাহরণস্বরূপ, এই কোডটি stdout কেটে দেবে, সুতরাং শেল জিনিসগুলি ফাইলটিতে যুক্ত করার পরিবর্তে ./script.py >> file ওভাররাইটের মতো তৈরি করে।
সালাইস্লাইডব্লক

এটি উইন্ডোতে কাজ করবে না যার কোনও / dev / stdout নেই।
ব্রায়ান ওকলে

0
if (out != sys.stdout):
    with open(out, 'wb') as f:
        f.write(data)
else:
    out.write(data)

কিছু ক্ষেত্রে সামান্য উন্নতি।


0
import contextlib
import sys

with contextlib.ExitStack() as stack:
    h = stack.enter_context(open(target, 'w')) if target else sys.stdout
    h.write(content)

আপনি পাইথন ৩.৩ বা ততোধিক উচ্চতর ব্যবহার করছেন মাত্র দুটি অতিরিক্ত লাইন: অতিরিক্তের importজন্য একটি লাইন এবং এর জন্য একটি লাইন stack.enter_context


0

যদি এটি সূক্ষ্ম হয় যা শরীরের sys.stdoutপরে বন্ধ withথাকে তবে আপনি এই জাতীয় নিদর্শনগুলিও ব্যবহার করতে পারেন:

# Use stdout when target is "-"
with open(target, "w") if target != "-" else sys.stdout as f:
    f.write("hello world")

# Use stdout when target is falsy (None, empty string, ...)
with open(target, "w") if target else sys.stdout as f:
    f.write("hello world")

বা আরও সাধারণভাবে:

with target if isinstance(target, io.IOBase) else open(target, "w") as f:
    f.write("hello world")

0

নিম্নলিখিত সমাধানটি কোনও সৌন্দর্য নয়, বরং বহু কাল আগে থেকে; এর ঠিক আগে ...

handler = open(path, mode = 'a') if path else sys.stdout
try:
    print('stuff', file = handler)
    ... # other stuff or more writes/prints, etc.
except Exception as e:
    if not (path is None): handler.close()
    raise e
handler.close()
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.