سؤال

جئت عبر بيثون with بيان لأول مرة اليوم. لقد كنت أستخدم Python بخفة لعدة أشهر ولم أكن أعرف حتى وجودها! بالنظر إلى حالتها الغامضة إلى حد ما ، اعتقدت أن الأمر يستحق السؤال:

  1. ما هو بيثون with بيان مصمم لاستخدامه؟
  2. ماذا كنت تستخدم ل؟
  3. هل هناك أي مسكات أحتاج إلى أن أكون على دراية بها ، أو معاداة شائعة مرتبطة باستخدامها؟ أي حالات يكون فيها استخدامها بشكل أفضل try..finally من with?
  4. لماذا لا تستخدم على نطاق أوسع؟
  5. ما هي فئات المكتبة القياسية المتوافقة معها؟
هل كانت مفيدة؟

المحلول

  1. أعتقد أن هذا قد تمت الإجابة عليه بالفعل من قبل المستخدمين الآخرين قبلي ، لذلك أنا فقط أضيفه من أجل الاكتمال: with يبسط البيان معالجة الاستثناء من خلال تغليف مهام التحضير والتنظيف المشترك في ما يسمى مديري السياق. يمكن العثور على مزيد من التفاصيل في بيب 343. على سبيل المثال ، open البيان هو مدير سياق في حد ذاته ، والذي يتيح لك فتح ملف ، وابقيه مفتوحًا طالما أن التنفيذ في سياق with البيان الذي استخدمته فيه ، وأغلقه بمجرد مغادرة السياق ، بغض النظر عما إذا كنت قد تركته بسبب استثناء أو أثناء تدفق التحكم العادي. ال with وبالتالي ، يمكن استخدام العبارة بطرق مشابهة ل نمط Raii في C ++: يتم الحصول على بعض الموارد بواسطة with بيان وأصدره عندما تغادر with سياق الكلام.

  2. بعض الأمثلة هي: فتح الملفات باستخدام with open(filename) as fp:, ، الحصول على الأقفال باستخدام with lock: (أين lock هو مثال threading.Lock). يمكنك أيضًا إنشاء مديري السياق الخاص بك باستخدام contextmanager ديكور من contextlib. على سبيل المثال ، غالبًا ما أستخدم هذا عندما يتعين علي تغيير الدليل الحالي مؤقتًا ثم أعود إلى حيث كنت:

    from contextlib import contextmanager
    import os
    
    @contextmanager
    def working_directory(path):
        current_dir = os.getcwd()
        os.chdir(path)
        try:
            yield
        finally:
            os.chdir(current_dir)
    
    with working_directory("data/stuff"):
        # do something within data/stuff
    # here I am back again in the original working directory
    

    إليك مثال آخر يعيد التوجيه مؤقتًا sys.stdin, sys.stdout و sys.stderr إلى بعض مقبض الملفات الآخر ويعيدها لاحقًا:

    from contextlib import contextmanager
    import sys
    
    @contextmanager
    def redirected(**kwds):
        stream_names = ["stdin", "stdout", "stderr"]
        old_streams = {}
        try:
            for sname in stream_names:
                stream = kwds.get(sname, None)
                if stream is not None and stream != getattr(sys, sname):
                    old_streams[sname] = getattr(sys, sname)
                    setattr(sys, sname, stream)
            yield
        finally:
            for sname, stream in old_streams.iteritems():
                setattr(sys, sname, stream)
    
    with redirected(stdout=open("/tmp/log.txt", "w")):
         # these print statements will go to /tmp/log.txt
         print "Test entry 1"
         print "Test entry 2"
    # back to the normal stdout
    print "Back to normal stdout again"
    

    وأخيراً ، مثال آخر ينشئ مجلد مؤقت ويقوم بتنظيفه عند ترك السياق:

    from tempfile import mkdtemp
    from shutil import rmtree
    
    @contextmanager
    def temporary_dir(*args, **kwds):
        name = mkdtemp(*args, **kwds)
        try:
            yield name
        finally:
            shutil.rmtree(name)
    
    with temporary_dir() as dirname:
        # do whatever you want
    

نصائح أخرى

I would suggest two interesting lectures:

  • PEP 343 The "with" Statement
  • Effbot Understanding Python's "with" statement

1. The with statement is used to wrap the execution of a block with methods defined by a context manager. This allows common try...except...finally usage patterns to be encapsulated for convenient reuse.

2. You could do something like:

with open("foo.txt") as foo_file:
    data = foo_file.read()

OR

from contextlib import nested
with nested(A(), B(), C()) as (X, Y, Z):
   do_something()

OR (Python 3.1)

with open('data') as input_file, open('result', 'w') as output_file:
   for line in input_file:
     output_file.write(parse(line))

OR

lock = threading.Lock()
with lock:
    # Critical section of code

3. I don't see any Antipattern here.
Quoting Dive into Python:

try..finally is good. with is better.

4. I guess it's related to programmers's habit to use try..catch..finally statement from other languages.

بيثون with البيان هو دعم لغة مدمج من Resource Acquisition Is Initialization المصطلح شائع الاستخدام في C ++. يهدف إلى السماح باستحواذ وإصدار موارد نظام التشغيل الآمنة.

ال with البيان يخلق الموارد ضمن نطاق/كتلة. تكتب التعليمات البرمجية الخاصة بك باستخدام الموارد داخل الكتلة. عندما تخرج الكتلة ، يتم إطلاق الموارد بشكل نظيف بغض النظر عن نتيجة الكود في الكتلة (أي ما إذا كانت الكتلة تخرج بشكل طبيعي أو بسبب استثناء).

العديد من الموارد في مكتبة بيثون التي تطيع البروتوكول المطلوب من قبل with بيان وهكذا يمكن استخدامه معها خارج الصندوق. ومع ذلك ، يمكن لأي شخص إنشاء موارد يمكن استخدامها في بيان من خلال تنفيذ البروتوكول الموثق جيدًا: PEP 0343

استخدمه كلما حصلت على موارد في التطبيق الخاص بك والتي يجب التخلص منها بشكل صريح مثل الملفات واتصالات الشبكة والأقفال وما شابه.

قد يكون مثال على antepattern هو استخدام with داخل حلقة عندما يكون من المفيد الحصول على with خارج الحلقة

فمثلا

for row in lines:
    with open("outfile","a") as f:
        f.write(row)

ضد

with open("outfile","a") as f:
    for row in lines:
        f.write(row)

الطريقة الأولى هي فتح وإغلاق الملف لكل منهما row مما قد يسبب مشاكل في الأداء مقارنة بالطريقة الثانية مع فتح وإغلاق الملف مرة واحدة فقط.

مرة أخرى للاكتمال سأضيف أكثر حالة استخدام مفيدة ل with صياغات.

أقوم بالكثير من الحوسبة العلمية وللأشخاص الذين أحتاج إلى بعض الأنشطة Decimal مكتبة لحسابات الدقة التعسفية. جزء من الكود الخاص بي أحتاج إلى دقة عالية وبالنسبة لمعظم الأجزاء الأخرى ، أحتاج إلى أقل دقة.

قمت بتعيين دقة الافتراضية على رقم منخفض ثم استخدم with للحصول على إجابة أكثر دقة لبعض الأقسام:

from decimal import localcontext

with localcontext() as ctx:
    ctx.prec = 42   # Perform a high precision calculation
    s = calculate_something()
s = +s  # Round the final result back to the default precision

أستخدم هذا كثيرًا مع الاختبار hypergeometric الذي يتطلب تقسيم الأعداد الكبيرة الناتجة عن الحالات. عندما تقوم بحسابات المقياس الجيني ، يجب أن تكون حذراً من الأخطاء حول التقريب والتدفق.

نرى بيب 343 - بيان "مع", ، هناك قسم مثال في النهاية.

... بيان جديد "مع" إلى لغة بيثون لجعل من الممكن وضع الاستخدامات القياسية لبيانات المحاولة/أخيرًا.

النقاط 1 و 2 و 3 يتم تغطيتها بشكل جيد:

4: إنه جديد نسبيًا ، متوفر فقط في Python2.6+ (أو Python2.5 باستخدام from __future__ import with_statement)

يعمل البيان مع ما يسمى مديري السياق:

http://docs.python.org/release/2.5.2/lib/typecontextmanager.html

تتمثل الفكرة في تبسيط معالجة الاستثناءات عن طريق القيام بالتنظيف اللازم بعد مغادرة الكتلة "مع". بعض من Python مدمجة تعمل بالفعل كمديرين للسياق.

مثال آخر لدعم خارج الصندوق ، ومثال قد يكون محيرًا بعض الشيء في البداية عندما يتم استخدامك للطريقة المدمجة في open() يتصرف ، هي connection كائنات من وحدات قاعدة البيانات الشائعة مثل:

ال connection الكائنات هي مديري السياق وبالتالي يمكن استخدامها خارج الصندوق في أ with-statement, ولكن عند استخدام ما أعلاه ، لاحظ أن:

عندما with-block انتهى ، إما باستثناء أو بدون ، الاتصال غير مغلق. في حالة with-block التشطيبات باستثناء ، يتم التراجع عن المعاملة ، وإلا يتم الالتزام بالمعاملة.

هذا يعني أن المبرمج يجب أن يحرص على إغلاق الاتصال بأنفسهم ، ولكنه يسمح باكتساب اتصال ، واستخدامه في متعددة with-statements, ، كما هو موضح في مستندات PSYCOPG2:

conn = psycopg2.connect(DSN)

with conn:
    with conn.cursor() as curs:
        curs.execute(SQL1)

with conn:
    with conn.cursor() as curs:
        curs.execute(SQL2)

conn.close()

في المثال أعلاه ، ستلاحظ أن cursor كائنات psycopg2 أيضا مديري السياق. من الوثائق ذات الصلة حول السلوك:

عندما cursor يخرج with-block إنه مغلق ، وإطلاق أي مورد مرتبط به في النهاية. لا تتأثر حالة المعاملة.

في بيثون بشكل عام "مع"يتم استخدام العبارة لفتح ملف ، ومعالجة البيانات الموجودة في الملف ، وأيضًا لإغلاق الملف دون استدعاء طريقة إغلاق (). يجعل بيان "مع" الاستثناء معالجة أكثر بساطة من خلال توفير أنشطة التنظيف.

الشكل العام لـ:

with open(“file name”, “mode”) as file-var:
    processing statements

ملاحظة: لا حاجة لإغلاق الملف عن طريق الاتصال بإغلاق () على ملف file-var.close ()

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top