Python的习惯用法';尝试直到没有引发异常';

Python的习惯用法';尝试直到没有引发异常';,python,Python,我希望我的代码自动尝试多种方法来创建数据库连接。一旦一种方法起作用,代码就需要继续前进(即,它不应该再尝试其他方法)。如果它们都失败了,那么脚本可能会崩溃 所以,在-我认为是,但很可能不是-一个天才的笔触中,我尝试了这个: import psycopg2 from getpass import getpass # ouch, global variable, ooh well, it's just a simple script eh CURSOR = None def get_cursor

我希望我的代码自动尝试多种方法来创建数据库连接。一旦一种方法起作用,代码就需要继续前进(即,它不应该再尝试其他方法)。如果它们都失败了,那么脚本可能会崩溃

所以,在-我认为是,但很可能不是-一个天才的笔触中,我尝试了这个:

import psycopg2
from getpass import getpass

# ouch, global variable, ooh well, it's just a simple script eh
CURSOR = None

def get_cursor():
    """Create database connection and return standard cursor."""

    global CURSOR

    if not CURSOR:
        # try to connect and get a cursor
        try:
            # first try the bog standard way: db postgres, user postgres and local socket
            conn = psycopg2.connect(database='postgres', user='postgres')
        except psycopg2.OperationalError:
            # maybe user pgsql?
            conn = psycopg2.connect(database='postgres', user='pgsql')
        except psycopg2.OperationalError:
            # maybe it was postgres, but on localhost? prolly need password then
            conn = psycopg2.connect(database='postgres', user='postgres', host='localhost', password=getpass())
        except psycopg2.OperationalError:
            # or maybe it was pgsql and on localhost
            conn = psycopg2.connect(database='postgres', user='pgsql', host='localhost', password=getpass())

        # allright, nothing blew up, so we have a connection
        # now make a cursor
        CURSOR = conn.cursor()

    # return existing or new cursor
    return CURSOR
但第二个和后续的except语句似乎不再捕捉操作错误。可能是因为Python在try…except语句中只捕获一次异常

是这样吗?如果不是:还有什么我做错了吗?如果是这样的话:那你是怎么做的?有标准的成语吗

(我知道有很多方法可以解决这个问题,比如让用户在命令行上指定连接参数,但这不是我的问题好:)

编辑:

我接受了retracile的优秀回答,并接受了gnibbler使用for..else结构的评论。最后的代码是(对不起,我没有真正遵循pep8中每行最多字符数的限制):

编辑2:正如您在Cursor类的注释中所看到的:我真的不知道如何调用这种类。它不是一个真正的单例(我可以有多个不同的游标实例),但是当调用get_Cursor时,每次都会得到相同的游标对象。所以它就像一个单身工厂?:)

大约:

attempts = [
    { 'database'='postgres', 'user'='pgsql', ...},
    { 'database'='postgres', 'user'='postgres', 'host'='localhost', 'password'=getpass()},
    ...
]
conn = None
for attempt in attempts:
    try:
        conn = psycopg2.connect(**attempt)
        break
    except psycopg2.OperationalError:
        pass
if conn is None:
    raise a ruckus
CURSOR = conn.cursor()
现在,如果您不想调用
getpass()
,除非有必要,那么您应该检查
在尝试中的'password'是否为:trunt['password']=getpass()
等等

现在,关于全球

class MyCursor:
    def __init__(self):
        self.CURSOR = None
    def __call__(self):
        if self.CURSOR is None:
            <insert logic here>
        return self.CURSOR

get_cursor = MyCursor()

注意:完全未经测试

您已接近成功。在这种情况下,最好的做法可能是在except块中嵌套第二次和后续尝试。因此,代码的关键部分如下所示:

if not CURSOR:
    # try to connect and get a cursor
    try:
        # first try the bog standard way: db postgres, user postgres and local socket
        conn = psycopg2.connect(database='postgres', user='postgres')
    except psycopg2.OperationalError:
        # maybe user pgsql?
        try:
            conn = psycopg2.connect(database='postgres', user='pgsql')
        except psycopg2.OperationalError:
            # maybe it was postgres, but on localhost? prolly need password then
            try:
                conn = psycopg2.connect(database='postgres', user='postgres', host='localhost', password=getpass())
            except psycopg2.OperationalError:
                # or maybe it was pgsql and on localhost
                conn = psycopg2.connect(database='postgres', user='pgsql', host='localhost', password=getpass())

不必将
conn
初始化为
None
,您只需使用
上的
else
子句,用于
+1@David:中断可确保返回第一个工作的连接。随着数据库数量的增加,此解决方案可以很好地扩展,并且连接字符串已经很好地从代码中抽象出来。@gnibbler-Woah,我忘了Python有那个很棒的功能()@retracile,你是对的,出于某种原因,我认为中断将一事无成。@David:没问题;如果你不愿意,我不会告诉任何人;)-呃,完全不是蟒蛇。使您看起来像C++ COM程序员。
class MyCursor:
    def __init__(self):
        self.CURSOR = None
    def __call__(self):
        if self.CURSOR is None:
            attempts = [
                {'database'='postgres', 'user'='postgres'},
                {'database'='postgres', 'user'='pgsql'},
                {'database'='postgres', 'user'='postgres', 'host'='localhost', 'password'=True},
                {'database'='postgres', 'user'='pgsql', 'host'='localhost', 'password'=True},
            ]
            conn = None
            for attempt in attempts:
                if 'password' in attempt:
                    attempt['password'] = getpass()
                try:
                    conn = psycopg2.connect(**attempt)
                    break # that didn't throw an exception, we're done
                except psycopg2.OperationalError:
                    pass
            if conn is None:
                raise a ruckus # nothin' worked
            self.CURSOR = conn.cursor()
        return self.CURSOR
get_cursor = MyCursor()
if not CURSOR:
    # try to connect and get a cursor
    try:
        # first try the bog standard way: db postgres, user postgres and local socket
        conn = psycopg2.connect(database='postgres', user='postgres')
    except psycopg2.OperationalError:
        # maybe user pgsql?
        try:
            conn = psycopg2.connect(database='postgres', user='pgsql')
        except psycopg2.OperationalError:
            # maybe it was postgres, but on localhost? prolly need password then
            try:
                conn = psycopg2.connect(database='postgres', user='postgres', host='localhost', password=getpass())
            except psycopg2.OperationalError:
                # or maybe it was pgsql and on localhost
                conn = psycopg2.connect(database='postgres', user='pgsql', host='localhost', password=getpass())