2010-10-22 29 views
12

Gần đây tôi đã đi qua một số hành vi đáng ngạc nhiên trong phát Python:phát Python, không nuốt ngoại lệ trong 'coroutine'

class YieldOne: 
    def __iter__(self): 
    try: 
     yield 1 
    except: 
     print '*Excepted Successfully*' 
     # raise 

for i in YieldOne(): 
    raise Exception('test exception') 

Mà cho kết quả:

*Excepted Successfully* 
Traceback (most recent call last): 
    File "<stdin>", line 2, in <module> 
Exception: test exception 

tôi (ngạc) ngạc nhiên rằng *Excepted Successfully* đã được in, vì đây là những gì tôi muốn, nhưng cũng ngạc nhiên rằng Ngoại lệ vẫn được tuyên truyền lên đến cấp cao nhất. Tôi đã mong đợi phải sử dụng (nhận xét trong ví dụ này) raise từ khóa để có được hành vi quan sát được.

Bất cứ ai có thể giải thích tại sao chức năng này hoạt động bình thường không và tại sao máy phát điện không nuốt phải ngoại lệ?

Đây có phải là trường hợp duy nhất trong Python, trong đó except không nuốt một ngoại lệ?

Trả lời

14

Mã của bạn không làm những gì bạn nghĩ. Bạn không thể nâng cao ngoại lệ trong một coroutine như thế này. Thay vào đó, những gì bạn làm là bắt gặp ngoại lệ GeneratorExit. Hãy xem những gì sẽ xảy ra khi bạn sử dụng một ngoại lệ khác nhau:

class YieldOne: 
    def __iter__(self): 
    try: 
     yield 1 
    except RuntimeError: 
     print "you won't see this" 
    except GeneratorExit: 
     print 'this is what you saw before' 
     # raise 

for i in YieldOne(): 
    raise RuntimeError 

Vì đây vẫn được upvotes, đây là cách bạn nâng cao một ngoại lệ trong một máy phát điện:

class YieldOne: 
    def __iter__(self): 
    try: 
     yield 1 
    except Exception as e: 
     print "Got a", repr(e) 
     yield 2 
     # raise 

gen = iter(YieldOne()) 

for row in gen: 
    print row # we are at `yield 1` 
    print gen.throw(Exception) # throw there and go to `yield 2` 

Xem tài liệu cho generator.throw.

+0

Aha, bây giờ nó có ý nghĩa. Tôi đã không ban đầu mong đợi các ngoại lệ để propogate 'trên' để máy phát điện. – EoghanM

+0

+1 rất thú vị! – rubik

+0

+1 để chiếu sáng mẹo 'generator.throw'! – EoghanM

6

CHỈNH SỬA: THC4k đã nói gì.

Nếu bạn thực sự muốn nâng một ngoại lệ tùy ý bên trong một máy phát điện, sử dụng các phương pháp throw:

>>> def Gen(): 
...  try: 
...    yield 1 
...  except Exception: 
...    print "Excepted." 
... 
>>> foo = Gen() 
>>> next(foo) 
1 
>>> foo.throw(Exception()) 
Excepted. 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
StopIteration 

Bạn sẽ nhận thấy rằng bạn nhận được một StopIteration ở cấp cao nhất. Chúng được tạo ra bởi các máy phát điện đã hết các yếu tố; chúng thường được nuốt bởi vòng lặp for nhưng trong trường hợp này, chúng tôi đã tạo ra một máy phát sinh ngoại lệ để vòng lặp không nhận thấy chúng.