सशर्त छोड़ने के लिए संदर्भ प्रबंधक शरीर की क्षमता प्रस्तावित किया गया है और खारिज कर दिया के रूप में PEP 377 में दर्ज पायथन डेवलपर्स अक्सर स्पष्ट संस्करण पसंद करते हैं।
कार्यक्षमता प्राप्त करने के कुछ तरीके यहां दिए गए हैं।
पहला क्या काम नहीं करता है: एक संदर्भ प्रबंधक जनरेटर से उपज नहीं।
@contextlib.contextmanager
def drivercontext():
driver, ok = driverfactory()
try:
if ok:
yield driver
else:
print 'skip because driver not ok'
finally:
driver.quit()
with drivercontext() as driver:
dostuff(driver)
उपज नहीं RuntimeException
contextmanager
द्वारा उठाए का परिणाम देगा। कम से कम finally
विश्वसनीय रूप से निष्पादित किया गया है।
विधि 1: मैन्युअल रूप से शरीर को छोड़ दें।
@contextlib.contextmanager
def drivercontext():
driver, ok = driverfactory()
try:
yield driver, ok
finally:
driver.quit()
with drivercontext() as (driver, ok):
if ok:
dostuff(driver)
else:
print 'skip because driver not ok'
यह स्पष्ट रूप से, एक संदर्भ प्रबंधक निकाय की संक्षिप्तता को अस्वीकार करता है। संदर्भ प्रबंधक में छिपी हुई तर्क को शरीर में फैलाया जाना चाहिए और प्रत्येक आमंत्रण के लिए दोहराया जाना चाहिए।
विधि 2: जनरेटर का दुरुपयोग करें।
def drivergenerator():
driver, ok = driverfactory()
try:
if ok:
yield driver
else:
print 'skip because driver not ok'
finally:
driver.quit()
for driver in drivergenerator():
dostuff(driver)
यह बहुत ज्यादा एक संदर्भ प्रबंधक जो शरीर को छोड़ सकते हैं की तरह व्यवहार करता है। दुर्भाग्यवश यह एक लूप की तरह दिखता है।
विधि 3: मैन्युअल रूप से सबकुछ करें।
driver, ok = driverfactory()
try:
if ok:
dostuff(driver)
else:
print 'skip because driver not ok'
finally:
driver.quit()
बह। यह क्या है? शब्दशः प्रतिद्वंद्वियों जावा।
इसे सामान्य बनाना केवल कॉलबैक के साथ ही किया जा सकता है।
def withdriver(callback):
driver, ok = driverfactory()
try:
if ok:
callback(driver)
else:
print 'skip because driver not ok'
finally:
driver.quit()
withdriver(dostuff)
ओह ठीक है। संदर्भ प्रबंधक अमूर्त कई मामलों।लेकिन हमेशा गिरने के लिए दरारें हैं। यह मुझे the law of leaky abstractions की याद दिलाता है।
यहां कुछ विधियां और विधियों का प्रदर्शन करने वाला कुछ कोड है।
import contextlib
import functools
# ----------------------------------------------------------------------
# this code is a simulation of the code not under my control
# report and ok and fail are variables for use in the simulation
# they do not exist in the real code
# report is used to report certain checkpoints
# ok is used to tell the driver object whether it is ok or not
# fail is used tell dostuff whether it should fail or not
class Driver(object):
def __init__(self, report, ok):
# driver can be ok or not ok
# driver must always quit after use
# regardless if it is ok or not
print 'driver init (ok: %s)' % ok
self.report = report
def drivestuff(self):
# if driver is not ok it is not ok to do stuff with it
self.report.drivestuffrun = True
def quit(self):
# driver must always quit regardless of ok or not
print 'driver quit'
self.report.driverquit = True
def driverfactory(report, ok=True):
# driver factory always returns a driver
# but sometimes driver is not ok
# this is indicated by second return value
# not ok driver must still be quit
return Driver(report, ok), ok
class DoStuffFail(Exception):
pass
def dostuff(driver, fail=False):
# this method does a lot of stuff
# dostuff expects an ok driver
# it does not check whether the driver is ok
driver.drivestuff()
# do stuff can also fail independent of driver
if fail:
print 'dostuff fail'
raise DoStuffFail('doing stuff fail')
else:
print 'dostuff'
# ----------------------------------------------------------------------
class AbstractScenario(object):
def __init__(self, driverfactory, dostuff):
self.driverfactory = functools.partial(driverfactory, report=self)
self.dostuff = dostuff
self.driverquit = False
self.drivestuffrun = False
# ----------------------------------------------------------------------
class Scenario0(AbstractScenario):
def run(self):
print '>>>> not check driver ok and not ensure driver quit'
driver, ok = self.driverfactory()
self.dostuff(driver)
driver.quit()
# ----------------------------------------------------------------------
class Scenario1(AbstractScenario):
def run(self):
print '>>>> check driver ok but not ensure driver quit'
driver, ok = self.driverfactory()
if ok:
self.dostuff(driver)
else:
print 'skip because driver not ok'
driver.quit()
# ----------------------------------------------------------------------
class Scenario2(AbstractScenario):
def run(self):
print '>>>> check driver ok and ensure driver quit'
driver, ok = self.driverfactory()
try:
if ok:
self.dostuff(driver)
else:
print 'skip because driver not ok'
finally:
driver.quit()
# ----------------------------------------------------------------------
class Scenario3(AbstractScenario):
@contextlib.contextmanager
def drivercontext(self, driverfactory):
driver, ok = driverfactory()
try:
if ok:
yield driver
else:
print 'skip because driver not ok'
finally:
driver.quit()
def run(self):
print '>>>> skip body by not yielding (does not work)'
with self.drivercontext(self.driverfactory) as driver:
self.dostuff(driver)
# ----------------------------------------------------------------------
class Scenario4(AbstractScenario):
@contextlib.contextmanager
def drivercontext(self, driverfactory):
driver, ok = driverfactory()
try:
yield driver, ok
finally:
driver.quit()
def run(self):
print '>>>> skip body manually by returning flag with context'
with self.drivercontext(self.driverfactory) as (driver, ok):
if ok:
self.dostuff(driver)
else:
print 'skip because driver not ok'
# ----------------------------------------------------------------------
class Scenario5(AbstractScenario):
def drivergenerator(self, driverfactory):
driver, ok = driverfactory()
try:
if ok:
yield driver
else:
print 'skip because driver not ok'
finally:
driver.quit()
def run(self):
print '>>>> abuse generator as context manager'
for driver in self.drivergenerator(self.driverfactory):
self.dostuff(driver)
# ----------------------------------------------------------------------
def doscenarios(driverfactory, dostuff, drivestuffrunexpected=True):
for Scenario in AbstractScenario.__subclasses__():
print '-----------------------------------'
scenario = Scenario(driverfactory, dostuff)
try:
try:
scenario.run()
except DoStuffFail as e:
print 'dostuff fail is ok'
if not scenario.driverquit:
print '---- fail: driver did not quit'
if not scenario.drivestuffrun and drivestuffrunexpected:
print '---- fail: drivestuff did not run'
if scenario.drivestuffrun and not drivestuffrunexpected:
print '---- fail: drivestuff did run'
except Exception as e:
print '----- fail with exception'
print '--------', e
# ----------------------------------------------------------------------
notokdriverfactory = functools.partial(driverfactory, ok=False)
dostufffail = functools.partial(dostuff, fail=True)
print '============================================'
print '==== driver ok and do stuff will not fail =='
doscenarios(driverfactory, dostuff)
print '============================================'
print '==== do stuff will fail ================='
doscenarios(driverfactory, dostufffail)
print '==========================================='
print '===== driver is not ok ==================='
doscenarios(notokdriverfactory, dostuff, drivestuffrunexpected=False)
और आउटपुट।
============================================
==== driver ok and do stuff will not fail ==
-----------------------------------
>>>> not check driver ok and not ensure driver quit
driver init (ok: True)
dostuff
driver quit
-----------------------------------
>>>> check driver ok but not ensure driver quit
driver init (ok: True)
dostuff
driver quit
-----------------------------------
>>>> check driver ok and ensure driver quit
driver init (ok: True)
dostuff
driver quit
-----------------------------------
>>>> skip body by not yielding (does not work)
driver init (ok: True)
dostuff
driver quit
-----------------------------------
>>>> skip body manually by returning flag with context
driver init (ok: True)
dostuff
driver quit
-----------------------------------
>>>> abuse generator as context manager
driver init (ok: True)
dostuff
driver quit
============================================
==== do stuff will fail =================
-----------------------------------
>>>> not check driver ok and not ensure driver quit
driver init (ok: True)
dostuff fail
dostuff fail is ok
---- fail: driver did not quit
-----------------------------------
>>>> check driver ok but not ensure driver quit
driver init (ok: True)
dostuff fail
dostuff fail is ok
---- fail: driver did not quit
-----------------------------------
>>>> check driver ok and ensure driver quit
driver init (ok: True)
dostuff fail
driver quit
dostuff fail is ok
-----------------------------------
>>>> skip body by not yielding (does not work)
driver init (ok: True)
dostuff fail
driver quit
dostuff fail is ok
-----------------------------------
>>>> skip body manually by returning flag with context
driver init (ok: True)
dostuff fail
driver quit
dostuff fail is ok
-----------------------------------
>>>> abuse generator as context manager
driver init (ok: True)
dostuff fail
driver quit
dostuff fail is ok
===========================================
===== driver is not ok ===================
-----------------------------------
>>>> not check driver ok and not ensure driver quit
driver init (ok: False)
dostuff
driver quit
---- fail: drivestuff did run
-----------------------------------
>>>> check driver ok but not ensure driver quit
driver init (ok: False)
skip because driver not ok
driver quit
-----------------------------------
>>>> check driver ok and ensure driver quit
driver init (ok: False)
skip because driver not ok
driver quit
-----------------------------------
>>>> skip body by not yielding (does not work)
driver init (ok: False)
skip because driver not ok
driver quit
----- fail with exception
-------- generator didn't yield
-----------------------------------
>>>> skip body manually by returning flag with context
driver init (ok: False)
skip because driver not ok
driver quit
-----------------------------------
>>>> abuse generator as context manager
driver init (ok: False)
skip because driver not ok
driver quit
यदि आप '__init __()' या '__enter __() 'में अपवाद फेंक देंगे तो यह शरीर को छोड़ सकता है ... – moooeeeep
@moooeeep: हाँ, लेकिन यह ... इसके बजाय एक excepion फेंक देगा। –
@ निकलासबी। हर दृष्टिकोण के अपने पेशेवरों और विपक्ष है! स्पष्ट 'if' स्थिति का उपयोग करना शायद [अधिक पायथनिक तरीका] (http://www.python.org/dev/peps/pep-0020/) ... वास्तव में। – moooeeeep