尝试执行完整操作,尽管有错误

try executes full operation, despite error

a = []
b = [1,2,'x']


try:
    for i in b:
        a.append(i%4)
        
except:
    print('Not possible')

finally:
    print("It's over")

print(a)

结果:

Not possible
It's over
[1, 2]

我一直认为python中的try类似于交易; commit()rollback() 在 SQL 中。所以在某种程度上,操作不会像我的情况那样 return 部分结果。例如,这是一个虚拟案例,但是 python 是否提供了一种解决方案,如果在过程中强加了错误,它不会将更改提交到列表中?所以在这个例子中它会 return 一个空白列表。

请注意,我知道如何解决这个问题,我很好奇如何用 try & except 解决这个问题。

Try 是一个捕捉器,耐心地等待抛出异常,以便它可以捕获它并将代码发送到 Except 块。 只有 抛出异常的方式是如果它执行代码,所以不,它不会自动回滚。请注意:

  1. Try 中的代码不知道它存在于 Try-Except 块中。
  2. 因为 1.,如果有回滚,Python 将真正获取 Try 中的所有内容并存储其更改或副本。您肯定会嵌套 Trys。如果你导入一个包,你在里面使用的一些函数里面会有Trys。这意味着堆栈将跟踪您在块中所做的一切的多个副本。请注意,在大多数(许多?)DBMS 中,您不能在不结束第一个事务的情况下开始第二个事务。 python.
  3. 不是这种情况

如果您想回滚更改,您应该在 Except 语句的前几行中执行此操作。

在 Python 中,在 try 中发生的任何事情都将一直执行(就好像它根本不在 try/except 块中一样),直到遇到异常。在那一点上,它然后进行到 except 块。 finally 无论是否输入 except 都会被执行(也就是说,它总是会触发)。 try/except/finally 并不像 SQL 那样是原子的。

将您的操作包装在列表理解中

a = []
b = [1, 2, 'x']

try:
    a += [i % 4 for i in b]
except Exception:
    print("failed")

这样就不会向 a 添加任何内容,因为列表推导无法实例化。

您可以定义一个新的元数据class,这样每个 class 使用这个元数据class 就可以按照您的意思实现 try/except

例如,您可以定义:

from copy import deepcopy
import types


class TryExceptRollbackMeta(type):
    def __new__(cls, name, bases, attrs):
        new_attrs = {}
        for name, value in attrs.items():
            if name == "__init__" or not isinstance(value, types.FunctionType):
                new_attrs[name] = value
                continue
            # We know from now on that we're dealing with a non-static function
            # If for some reason, a non-static method is defined without being passed self as an argument
            if value.__code__.co_argcount == 0:
                new_attrs[name] = value
                continue

            new_attrs[f"updated_{name}"] = TryExceptRollbackMeta.generate_updated_method(value)
        return super().__new__(cls, name, bases, new_attrs)
    
    @staticmethod
    def generate_updated_method(func):
        def updated_method(*args, **kwargs):
            original = deepcopy(args[0])
            try:
                result = func(*args, **kwargs)
            except Exception as e:
                print(f"Exception {type(e)} has occured: {e}. Reverting state...")
                args[0].__dict__.update(original.__dict__)
                return None

            return result

        return updated_method


class Test(metaclass=TryExceptRollbackMeta):
    def __init__(self):
        self.a = []

    def correct(self):
        for i in [1, 2, 3]:
            self.a.append(i %  4)

    def incorrect(self):
        for i in [1, 2, 'x']:
            self.a.append(i % 4)

然后,这将像这样工作:

>>> test = Test()
>>> test.updated_correct()
>>> print(test.a)
[1, 2, 3]
>>> test.updated_incorrect()
Exception <class 'TypeError'> has occured: not all arguments converted during string formatting. Reverting state...
>>> print(test.a)
[1, 2, 3]

通过这样做,您可以完全控制您想要处理异常的方式,您可以根据异常类型采取行动,打印失败的行等...

问题是 deepcopy 可能会很长,具体取决于您的对象属性。尽管如此,仍然可以针对您想要具体恢复的属性,这只是您不知道哪个属性会受到方法影响的一般情况。