شرح بيثون '__enter__' و '__exit__'
-
22-09-2019 - |
سؤال
رأيت هذا في رمز شخص ما. ماذا يعني ذلك؟
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
نتيجة جانب الخادم كما كان من قبل
آسف على لغتي الإنجليزية السيئة وتوضيحاتي غير الواضحة ، شكرا لك ....