سؤال

رأيت هذا في رمز شخص ما. ماذا يعني ذلك؟

    def __enter__(self):
        return self

    def __exit__(self, type, value, tb):
        self.stream.close()

from __future__ import with_statement#for python2.5 

class a(object):
    def __enter__(self):
        print 'sss'
        return 'sss111'
    def __exit__(self ,type, value, traceback):
        print 'ok'
        return False

with a() as s:
    print s


print s
هل كانت مفيدة؟

المحلول

باستخدام هذه الأساليب السحرية (__enter__, __exit__) يسمح لك بتنفيذ الكائنات التي يمكن استخدامها بسهولة مع with بيان.

والفكرة هي أنها تجعل من السهل إنشاء رمز يحتاج إلى تنفيذ بعض رمز "التنظيف" (فكر في الأمر على أنه أ try-finally منع). المزيد من التفسير هنا.

يمكن أن يكون مثال مفيد هو كائن اتصال قاعدة بيانات (والذي يغلق الاتصال تلقائيًا بمجرد خروج النطاق "مع" مع وضع "من النطاق):

class DatabaseConnection(object):

    def __enter__(self):
        # make a database connection and return it
        ...
        return self.dbconn

    def __exit__(self, exc_type, exc_val, exc_tb):
        # make sure the dbconnection gets closed
        self.dbconn.close()
        ...

كما هو موضح أعلاه ، استخدم هذا الكائن مع with بيان (قد تحتاج إلى القيام به from __future__ import with_statement في الجزء العلوي من الملف إذا كنت على Python 2.5).

with DatabaseConnection() as mydbconn:
    # do stuff

PEP343 - "مع" البيان لديه كتابة لطيفة كذلك.

نصائح أخرى

إذا كنت تعرف ماذا مديري السياق أنت بعد ذلك لا تحتاج إلى المزيد لفهمها __enter__ و __exit__ الطرق السحرية. لنرى مثالًا بسيطًا جدًا.

في هذا المثال أنا فتح myfile.txt بمساعدة افتح وظيفة. ال حاول/أخيرًا تضمن الكتلة أنه حتى إذا حدث استثناء غير متوقع myfile.txt سوف يغلق.

fp=open(r"C:\Users\SharpEl\Desktop\myfile.txt")
try:
    for line in fp:
        print(line)
finally:
    fp.close()

الآن أفتح نفس الملف مع بيان:

with open(r"C:\Users\SharpEl\Desktop\myfile.txt") as fp:
    for line in fp:
        print(line) 

إذا نظرت إلى الرمز ، لم أغلق الملف ولا يوجد حاول/أخيرًا منع. لان مع البيان يغلق تلقائيا myfile.txt . يمكنك حتى التحقق من ذلك عن طريق الاتصال print(fp.closed) السمة - التي تعود True.

هذا لأن كائنات الملفات (FP في المثال الخاص بي) تم إرجاعها بواسطة افتح الوظيفة لها طريقتان مدمجتان __enter__ و __exit__. ومن المعروف أيضًا باسم مدير السياق. __enter__ الطريقة تسمى في بداية مع كتلة و __exit__ الطريقة تسمى في النهاية. ملحوظة: مع يعمل البيان فقط مع الكائنات التي تدعم بروتوكول مامنج السياق ، أي لديهم __enter__ و __exit__ أساليب. يُعرف الفصل الذي ينفذ كلتا الطريقتين باسم فئة مدير السياق.

الآن دعنا نحدد منطقتنا مدير السياق صف دراسي.

 class Log:
    def __init__(self,filename):
        self.filename=filename
        self.fp=None    
    def logging(self,text):
        self.fp.write(text+'\n')
    def __enter__(self):
        print("__enter__")
        self.fp=open(self.filename,"a+")
        return self    
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("__exit__")
        self.fp.close()

with Log(r"C:\Users\SharpEl\Desktop\myfile.txt") as logfile:
    print("Main")
    logfile.logging("Test1")
    logfile.logging("Test2")

آمل الآن أن يكون لديك فهم أساسي لكليهما __enter__ و __exit__ الطرق السحرية.

لقد وجدت أنه من الصعب للغاية تحديد موقع مستندات Python __enter__ و __exit__ طرق googling ، وذلك لمساعدة الآخرين هنا هو الرابط:

https://docs.python.org/2/reference/datamodel.html#with-statement-context-managers
https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers
(التفاصيل هي نفسها لكلا الإصدارين)

object.__enter__(self)
أدخل سياق وقت التشغيل المتعلق بهذا الكائن. ال with سيقوم البيان بربط قيمة إرجاع هذه الطريقة بالهدف (الأهداف) المحددة في جملة AS للبيان ، إن وجدت.

object.__exit__(self, exc_type, exc_value, traceback)
الخروج من سياق وقت التشغيل المتعلق بهذا الكائن. تصف المعلمات الاستثناء الذي تسبب في خروج السياق. إذا تم الخروج من السياق دون استثناء ، فستكون جميع الحجج الثلاث None.

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

لاحظ أن __exit__() لا ينبغي أن تعيد الأساليب استثناء تم تمريره ؛ هذه هي مسؤولية المتصل.

كنت آمل في الحصول على وصف واضح ل __exit__ وسيطات طريقة. هذا ينقص ولكن يمكننا أن نستنتجهم ...

محتمل exc_type هو فئة الاستثناء.

تقول أنه يجب ألا تعيد إعادة الاستثناء الذي تم تمريره. هذا يشير لنا إلى أن إحدى الحجج قد تكون مثيل استثناء فعلي ... أو ربما من المفترض أن تقوم بتسهيله بنفسك من النوع والقيمة؟

يمكننا الإجابة من خلال النظر في هذا المقال:
http://effbot.org/zone/python-with-statement.htm

على سبيل المثال ، ما يلي __exit__ الطريقة تبتلع أي نوع ، ولكن يتيح لجميع الاستثناءات الأخرى من خلال:

def __exit__(self, type, value, traceback):
    return isinstance(value, TypeError)

...بشكل واضح value هو مثيل استثناء.

ويفترض traceback هو بيثون تتبع الأثر هدف.

بالإضافة إلى الإجابات المذكورة أعلاه لتجسيد أمر الاحتجاج ، مثال على التشغيل البسيط

class myclass:
    def __init__(self):
        print("__init__")

    def __enter__(self): 
        print("__enter__")

    def __exit__(self, type, value, traceback):
        print("__exit__")

    def __del__(self):
        print("__del__")

with myclass(): 
    print("body")

ينتج الإخراج:

__init__
__enter__
body
__exit__
__del__

تذكير: عند استخدام بناء الجملة with myclass() as mc, ، متغير MC يحصل على القيمة التي يتم إرجاعها بواسطة __enter__(), ، في الحالة أعلاه None! لمثل هذا الاستخدام ، تحتاج إلى تحديد قيمة الإرجاع ، مثل:

def __enter__(self): 
    print('__enter__')
    return self

حاول إضافة إجاباتي (فكرتي في التعلم):

__enter__ و [__exit__] كلاهما طرق تم استدعاؤها عند الدخول والخروج من جسم "" من جسم ""مع البيان" (بيب 343) وتنفيذ كلاهما يسمى مدير السياق.

يعتزم البيان إخفاء التحكم في التدفق في جملة Try أخيرًا وجعل الكود غير قابل للتوحيد.

بناء جملة البيان مع:

with EXPR as VAR:
    BLOCK

التي تترجم إلى (كما ذكر في PEP 343):

mgr = (EXPR)
exit = type(mgr).__exit__  # Not calling it yet
value = type(mgr).__enter__(mgr)
exc = True
try:
    try:
        VAR = value  # Only if "as VAR" is present
        BLOCK
    except:
        # The exceptional case is handled here
        exc = False
        if not exit(mgr, *sys.exc_info()):
            raise
        # The exception is swallowed if exit() returns true
finally:
    # The normal and non-local-goto cases are handled here
    if exc:
        exit(mgr, None, None, None)

جرب بعض التعليمات البرمجية:

>>> import logging
>>> import socket
>>> import sys

#server socket on another terminal / python interpreter
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> s.listen(5)
>>> s.bind((socket.gethostname(), 999))
>>> while True:
>>>    (clientsocket, addr) = s.accept()
>>>    print('get connection from %r' % addr[0])
>>>    msg = clientsocket.recv(1024)
>>>    print('received %r' % msg)
>>>    clientsocket.send(b'connected')
>>>    continue

#the client side
>>> class MyConnectionManager:
>>>     def __init__(self, sock, addrs):
>>>         logging.basicConfig(level=logging.DEBUG, format='%(asctime)s \
>>>         : %(levelname)s --> %(message)s')
>>>         logging.info('Initiating My connection')
>>>         self.sock = sock
>>>         self.addrs = addrs
>>>     def __enter__(self):
>>>         try:
>>>             self.sock.connect(addrs)
>>>             logging.info('connection success')
>>>             return self.sock
>>>         except:
>>>             logging.warning('Connection refused')
>>>             raise
>>>     def __exit__(self, type, value, tb):
>>>             logging.info('CM suppress exception')
>>>             return False
>>> addrs = (socket.gethostname())
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> with MyConnectionManager(s, addrs) as CM:
>>>     try:
>>>         CM.send(b'establishing connection')
>>>         msg = CM.recv(1024)
>>>         print(msg)
>>>     except:
>>>         raise
#will result (client side) :
2018-12-18 14:44:05,863         : INFO --> Initiating My connection
2018-12-18 14:44:05,863         : INFO --> connection success
b'connected'
2018-12-18 14:44:05,864         : INFO --> CM suppress exception

#result of server side
get connection from '127.0.0.1'
received b'establishing connection'

والآن حاول يدويًا (بعد ترجمة بناء الجملة):

>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #make new socket object
>>> mgr = MyConnection(s, addrs)
2018-12-18 14:53:19,331         : INFO --> Initiating My connection
>>> ext = mgr.__exit__
>>> value = mgr.__enter__()
2018-12-18 14:55:55,491         : INFO --> connection success
>>> exc = True
>>> try:
>>>     try:
>>>         VAR = value
>>>         VAR.send(b'establishing connection')
>>>         msg = VAR.recv(1024)
>>>         print(msg)
>>>     except:
>>>         exc = False
>>>         if not ext(*sys.exc_info()):
>>>             raise
>>> finally:
>>>     if exc:
>>>         ext(None, None, None)
#the result:
b'connected'
2018-12-18 15:01:54,208         : INFO --> CM suppress exception

نتيجة جانب الخادم كما كان من قبل

آسف على لغتي الإنجليزية السيئة وتوضيحاتي غير الواضحة ، شكرا لك ....

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