क्या अधिक से अधिक संख्या में कुछ करने की कोशिश करने का एक पैथोनिक तरीका है? [डुप्लिकेट]


85

मेरे पास एक पायथन स्क्रिप्ट है जो एक साझा लिनक्स होस्ट पर MySQL सर्वर को क्वेरी कर रही है। किसी कारण से, MySQL के लिए प्रश्न अक्सर "सर्वर दूर चला गया" त्रुटि लौटाते हैं:

_mysql_exceptions.OperationalError: (2006, 'MySQL server has gone away')

यदि आप क्वेरी को तुरंत बाद में आज़माते हैं, तो यह आमतौर पर सफल होता है। इसलिए, मैं जानना चाहूंगा कि क्या किसी प्रश्न को निष्पादित करने की कोशिश करने के लिए अजगर में एक समझदार तरीका है, और यदि यह विफल हो जाता है, तो निश्चित संख्या में फिर से प्रयास करने के लिए। शायद मैं इसे पूरी तरह से देने से पहले 5 बार कोशिश करना चाहता हूं।

यहाँ कोड की तरह मैं है:

conn = MySQLdb.connect(host, user, password, database)
cursor = conn.cursor()

try:
    cursor.execute(query)
    rows = cursor.fetchall()
    for row in rows:
        # do something with the data
except MySQLdb.Error, e:
    print "MySQL Error %d: %s" % (e.args[0], e.args[1])

स्पष्ट रूप से मैं इसे खंड में छोड़कर एक और प्रयास कर सकता था, लेकिन यह अविश्वसनीय रूप से बदसूरत है, और मुझे लगता है कि इसे प्राप्त करने का एक सभ्य तरीका होना चाहिए।


2
ये एक अच्छा बिंदु है। मैं शायद कुछ सेकंड के लिए नींद में डाल देता। मुझे नहीं पता कि सर्वर पर MySQL इंस्टॉलेशन में क्या गलत है, लेकिन ऐसा लगता है कि यह एक सेकंड में विफल हो जाता है, और अगला यह काम करता है।
बेन

3
@ युवल ए: यह एक सामान्य कार्य है। मुझे संदेह है कि यह एरलांग में भी बनाया गया है।
JFS

1
बस यह उल्लेख करने के लिए कि शायद कुछ भी गलत नहीं है, मैसकल के पास निष्क्रिय कनेक्शन छोड़ने के लिए mysql को कॉन्फ़िगर करने के लिए एक wa_timeout चर है।
andy

जवाबों:


97

कैसा रहेगा:

conn = MySQLdb.connect(host, user, password, database)
cursor = conn.cursor()
attempts = 0

while attempts < 3:
    try:
        cursor.execute(query)
        rows = cursor.fetchall()
        for row in rows:
            # do something with the data
        break
    except MySQLdb.Error, e:
        attempts += 1
        print "MySQL Error %d: %s" % (e.args[0], e.args[1])

19
याfor attempt_number in range(3)
cdleary

8
खैर, मैं मेरी तरह थोड़े हूं क्योंकि यह स्पष्ट करता है कि अपवाद की स्थिति में प्रयास केवल बढ़े हैं।
दना

2
हाँ, मुझे लगता है कि मैं whileज्यादातर लोगों की तुलना में अनंत लूपों के बारे में अधिक पागल हूं ।
cdleary

5
-1: ब्रेक पसंद नहीं है। जैसे "जबकि किया नहीं गया और प्रयास <3:" बेहतर है।
एस.लॉट

5
मुझे ब्रेक पसंद है, लेकिन समय नहीं। यह पाइथोनिक की तुलना में सी-ईश की तरह अधिक है। के लिए मैं सीमा में बेहतर imho है।
hasen

78

दाना के जवाब पर निर्माण, आप एक डेकोरेटर के रूप में ऐसा करना चाह सकते हैं:

def retry(howmany):
    def tryIt(func):
        def f():
            attempts = 0
            while attempts < howmany:
                try:
                    return func()
                except:
                    attempts += 1
        return f
    return tryIt

फिर...

@retry(5)
def the_db_func():
    # [...]

उन्नत संस्करण जो decoratorमॉड्यूल का उपयोग करता है

import decorator, time

def retry(howmany, *exception_types, **kwargs):
    timeout = kwargs.get('timeout', 0.0) # seconds
    @decorator.decorator
    def tryIt(func, *fargs, **fkwargs):
        for _ in xrange(howmany):
            try: return func(*fargs, **fkwargs)
            except exception_types or Exception:
                if timeout is not None: time.sleep(timeout)
    return tryIt

फिर...

@retry(5, MySQLdb.Error, timeout=0.5)
def the_db_func():
    # [...]

मॉड्यूल स्थापित करने के लिएdecorator :

$ easy_install decorator

2
डेकोरेटर को संभवतः एक अपवाद वर्ग लेना चाहिए, इसलिए आपको एक नंगे को छोड़कर उपयोग करने की आवश्यकता नहीं है; यानी @retry (5, MySQLdb.Error)
cdleary

निफ्टी! मैं सज्जाकारों का उपयोग करने के लिए कभी नहीं सोचता: पी
दाना

कोशिश ब्लॉक में "रिटर्न फंक () होना चाहिए, न कि सिर्फ"
फंक

बाह! सर उठाने के लिए धन्यवाद।
dwc

क्या आपने वास्तव में इसे चलाने की कोशिश की थी? यह काम नहीं करता है। समस्या यह है कि tryIt फ़ंक्शन में फ़ंक () कॉल, जैसे ही आप फ़ंक्शन को सजाते हैं , निष्पादित होता है, न कि तब जब आप वास्तव में सजाए गए फ़ंक्शन को कॉल करते हैं। आपको एक और नेस्टेड फ़ंक्शन की आवश्यकता है।
स्टीव लूश

12

अद्यतन: रिटेनिंग लाइब्रेरी के एक बेहतर बनाए रखा कांटा को तप कहा जाता है , जो अधिक सुविधाओं का समर्थन करता है और सामान्य रूप से अधिक लचीला होता है।


हां, रिट्रीइंग लाइब्रेरी है , जिसमें एक डेकोरेटर है जो कई तरह के रिट्रीइंग लॉजिक को लागू करता है जिसे आप जोड़ सकते हैं:

कुछ उदाहरण:

@retry(stop_max_attempt_number=7)
def stop_after_7_attempts():
    print "Stopping after 7 attempts"

@retry(wait_fixed=2000)
def wait_2_s():
    print "Wait 2 second between retries"

@retry(wait_exponential_multiplier=1000, wait_exponential_max=10000)
def wait_exponential_1000():
    print "Wait 2^x * 1000 milliseconds between each retry,"
    print "up to 10 seconds, then 10 seconds afterwards"

2
रिटेनिंग लाइब्रेरी को तप लाइब्रेरी द्वारा अधिगृहीत किया गया है ।
सेठ

8
conn = MySQLdb.connect(host, user, password, database)
cursor = conn.cursor()

for i in range(3):
    try:
        cursor.execute(query)
        rows = cursor.fetchall()
        for row in rows:
            # do something with the data
        break
    except MySQLdb.Error, e:
        print "MySQL Error %d: %s" % (e.args[0], e.args[1])

1
आप सबसे नीचे एक और जोड़ सकते हैं :else: raise TooManyRetriesCustomException
बॉब स्टीन

6

मैं इसे पसंद करना चाहूंगा:

def callee(cursor):
    cursor.execute(query)
    rows = cursor.fetchall()
    for row in rows:
        # do something with the data

def caller(attempt_count=3, wait_interval=20):
    """:param wait_interval: In seconds."""
    conn = MySQLdb.connect(host, user, password, database)
    cursor = conn.cursor()
    for attempt_number in range(attempt_count):
        try:
            callee(cursor)
        except MySQLdb.Error, e:
            logging.warn("MySQL Error %d: %s", e.args[0], e.args[1])
            time.sleep(wait_interval)
        else:
            break

calleeफ़ंक्शन को बाहर करने से कार्यक्षमता टूटने लगती है, जिससे कि रिट्रीट कोड में फंसने के बिना व्यापार तर्क को देखना आसान है।


-1: और तोड़ो ... icky। एक स्पष्ट "और नहीं किया है! जबकि ब्रेक की तुलना में गिनती! Try_count" को प्राथमिकता दें।
S.Lott

1
वास्तव में? मैंने सोचा कि यह इस तरह से अधिक समझ में आता है - यदि अपवाद नहीं होता है, तो लूप से बाहर निकलें। मैं छोरों से अनंत रूप से भयभीत हो सकता हूं।
cdleary

4
+1: मुझे ध्वज चर से नफरत है जब भाषा में आपके लिए यह करने के लिए कोड संरचनाएं शामिल हैं। बोनस अंक के लिए, सभी प्रयासों को विफल करने से निपटने के लिए आगे के लिए एक और डालें।
xorsyst 15

6

एस.लॉट की तरह, मुझे यह जांचने के लिए एक ध्वज पसंद है कि क्या हम कर रहे हैं:

conn = MySQLdb.connect(host, user, password, database)
cursor = conn.cursor()

success = False
attempts = 0

while attempts < 3 and not success:
    try:
        cursor.execute(query)
        rows = cursor.fetchall()
        for row in rows:
            # do something with the data
        success = True 
    except MySQLdb.Error, e:
        print "MySQL Error %d: %s" % (e.args[0], e.args[1])
        attempts += 1

1
def successful_transaction(transaction):
    try:
        transaction()
        return True
    except SQL...:
        return False

succeeded = any(successful_transaction(transaction)
                for transaction in repeat(transaction, 3))

1

1.Definition:

def try_three_times(express):
    att = 0
    while att < 3:
        try: return express()
        except: att += 1
    else: return u"FAILED"

2.Usage:

try_three_times(lambda: do_some_function_or_express())

मैं इसे पार्स html संदर्भ के लिए उपयोग करता हूं।


0

यह मेरा सामान्य समाधान है:

class TryTimes(object):
    ''' A context-managed coroutine that returns True until a number of tries have been reached. '''

    def __init__(self, times):
        ''' times: Number of retries before failing. '''
        self.times = times
        self.count = 0

    def __next__(self):
        ''' A generator expression that counts up to times. '''
        while self.count < self.times:
            self.count += 1
        yield False

    def __call__(self, *args, **kwargs):
        ''' This allows "o() calls for "o = TryTimes(3)". '''
        return self.__next__().next()

    def __enter__(self):
        ''' Context manager entry, bound to t in "with TryTimes(3) as t" '''
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        ''' Context manager exit. '''
        return False # don't suppress exception

यह निम्नलिखित की तरह कोड की अनुमति देता है:

with TryTimes(3) as t:
    while t():
        print "Your code to try several times"

यह भी संभव है:

t = TryTimes(3)
while t():
    print "Your code to try several times"

मुझे आशा है कि अपवादों को अधिक सहज तरीके से संभालकर इसे बेहतर बनाया जा सकता है। सुझाव के लिए खोलें।


0

आप अधिकतम प्रभाव के लिए forएक elseखंड के साथ एक लूप का उपयोग कर सकते हैं :

conn = MySQLdb.connect(host, user, password, database)
cursor = conn.cursor()

for n in range(3):
    try:
        cursor.execute(query)
    except MySQLdb.Error, e:
        print "MySQL Error %d: %s" % (e.args[0], e.args[1])
    else:
        rows = cursor.fetchall()
        for row in rows:
            # do something with the data
        break
else:
    # All attempts failed, raise a real error or whatever

जैसे ही क्वेरी सफल होती है, कुंजी लूप से बाहर हो जाती है। elseपाश एक के बिना पूरा करता है, तो खंड केवल तभी ट्रिगर होंगे break

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.