2012-05-04 8 views
10

मैं एक एमपीआई-आधारित आवेदन लिख रहा हूं (लेकिन एमपीआई मेरे प्रश्न में कोई फर्क नहीं पड़ता, मैं केवल तर्क का पर्दाफाश करने के लिए इसका उल्लेख करता हूं) और कुछ मामलों में, जब प्रक्रियाओं की तुलना में कम काम करने वाले सामान होते हैं, तो मुझे एक बनाना होगा नए संचारक उन प्रक्रियाओं को छोड़कर जिनके पास कुछ भी नहीं है। अंत में, नए संवाददाता को उन प्रक्रियाओं से मुक्त किया जाना चाहिए जिनके पास काम करना है (और केवल उनके द्वारा)।पायथन संदर्भ प्रबंधक: सशर्त रूप से निष्पादन निकाय?

करने के लिए है कि लिखने के लिए होगा एक स्वच्छ रास्ता:

with filter_comm(comm, nworkitems) as newcomm: 
    ... do work with communicator newcomm... 

शरीर केवल प्रक्रियाओं करने के लिए काम किया है द्वारा निष्पादित किया जा रहा।

क्या शरीर को निष्पादित करने से बचने के लिए संदर्भ प्रबंधक में कोई तरीका है? मैं समझता हूं कि संदर्भ प्रबंधकों को छुपा नियंत्रण प्रवाह से बचने के लिए सही तरीके से डिजाइन किया गया है, लेकिन मुझे आश्चर्य है कि अगर इसे बाधित करना संभव है, क्योंकि मेरे मामले में मुझे लगता है कि यह स्पष्टता के लिए उचित होगा।

+0

यदि आप '__init __()' या '__enter __() 'में अपवाद फेंक देंगे तो यह शरीर को छोड़ सकता है ... – moooeeeep

+0

@moooeeep: हाँ, लेकिन यह ... इसके बजाय एक excepion फेंक देगा। –

+0

@ निकलासबी। हर दृष्टिकोण के अपने पेशेवरों और विपक्ष है! स्पष्ट 'if' स्थिति का उपयोग करना शायद [अधिक पायथनिक तरीका] (http://www.python.org/dev/peps/pep-0020/) ... वास्तव में। – moooeeeep

उत्तर

6

सशर्त छोड़ने के लिए संदर्भ प्रबंधक शरीर की क्षमता प्रस्तावित किया गया है और खारिज कर दिया के रूप में 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) 

उपज नहीं RuntimeExceptioncontextmanager द्वारा उठाए का परिणाम देगा। कम से कम 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 
+0

कुडोस। बहुत उपयोगी था। –

6

यह कार्यक्षमता rejected प्रतीत होती है।

if need_more_workers(): 
    newcomm = get_new_comm(comm) 
    # ... 

तुम भी उच्च क्रम कार्यों का उपयोग कर सकते हैं::

def filter_comm(comm, nworkitems, callback): 
    if foo: 
     callback(get_new_comm()) 

# ... 

some_local_var = 5 
def do_work_with_newcomm(newcomm): 
    # we can access the local scope here 

filter_comm(comm, nworkitems, do_work_with_newcomm) 
+0

संदर्भ के लिए धन्यवाद। मैंने पायथन-देव मेलिंग सूची में लिखा है। – pch

+1

मुझे एहसास है कि उत्पाद को साफ और सरल रखने के लिए "नहीं" एक महत्वपूर्ण हिस्सा है, लेकिन अक्सर मुझे लगता है कि गिडो कहते हैं कि यह थोड़ा सा बार होता है। आईई, कोई पैटर्न मिलान नहीं, कोई रेंज अक्षर नहीं है, और अब संदर्भ प्रबंधकों के लिए कोई सशर्त नहीं है। इस उत्तर में किए गए प्रयासों के लिए – ArtOfWarfare

0

कैसे बजाय कुछ इस तरह के बारे में:

@filter_comm(comm, nworkitems) 
def _(newcomm): # Name is unimportant - we'll never reference this by name. 
    ... do work with communicator newcomm... 

आप पर उन परिणामों तय समारोह लपेटा है निष्पादित करने के लिए है कि क्या जो कुछ भी काम यह comm और nworkitems साथ करना चाहिए, फिर आधारित करने के लिए filter_comm डेकोरेटर लागू newcomm में गुज़रने के आसपास या नहीं।

यह with के रूप में काफी सुरुचिपूर्ण नहीं है, लेकिन मुझे लगता है कि यह कुछ और अधिक पठनीय और अन्य प्रस्तावों की तुलना में आप जो चाहते हैं उसके करीब है। यदि आप उस नाम को पसंद नहीं करते हैं तो आप आंतरिक कार्य को _ के अलावा अन्य नाम का नाम दे सकते हैं, लेकिन मैं इसके साथ गया क्योंकि यह पाइथन में उपयोग किया जाने वाला सामान्य नाम है जब व्याकरण के लिए एक नाम की आवश्यकता होती है जिसका आप वास्तव में उपयोग नहीं करेंगे।

संबंधित मुद्दे