2013-07-16 23 views
6

Tôi cố gắng xác định chức năng của máy phát điện mycount() có thể được đặt lại với chức năng của máy phát send(0) như trong ví dụ bên dưới. Mọi thứ hoạt động tốt, trừ khi tôi sử dụng send(0) trên một đối tượng máy phát mới chưa bắt đầu. Trong trường hợp này, nó cho một số TypeError. Có chức năng nào kiểm tra xem máy phát điện đã khởi động hay tôi phải bắt giữ TypeError và tạo một đối tượng máy phát mới với mycount(0) trong trường hợp này?Có chức năng Python nào kiểm tra xem máy phát điện có được khởi động không?

def mycount(value): 
    while True: 
     v = yield value 
     if v == None: 
      value = value + 1 
     else: 
      value = v 

g = mycount(3) 
print(next(g)) # prints 3 
print(next(g)) # prints 4 
print(g.send(0)) # prints 0 
print(next(g)) # prints 1 
print(next(g)) # prints 2 

g2 = mycount(3) 
g2.send(0) 
# TypeError: can't send non-None value to a just-started generator 

Trả lời

7

Để tránh gửi giá trị không None cho máy phát điện chỉ mới bắt đầu, trước tiên bạn cần gọi số next hoặc send(None). Tôi đồng ý với những người khác rằng số điện thoại coroutine decorator của David Beazley (trong python 3.x bạn cần gọi tới __next__() chức năng thay vì next()) là một lựa chọn tuyệt vời. Mặc dù trang trí cụ thể đó rất đơn giản, tôi cũng đã sử dụng thành công thư viện copipes, một triển khai tốt đẹp của nhiều tiện ích từ các bản trình bày của Beazley, bao gồm cả coroutine.

Về việc liệu người ta có thể kiểm tra xem máy phát điện có được khởi động hay không - trong Python 3, bạn có thể sử dụng inspect.getgeneratorstate. Đây không phải là có sẵn trong Python 2, nhưng CPython implementation là trăn tinh khiết và không dựa vào bất cứ điều gì mới để Python 3, vì vậy bạn có thể kiểm tra bản thân trong cùng một cách:

if generator.gi_running: 
    return GEN_RUNNING 
if generator.gi_frame is None: 
    return GEN_CLOSED 
if generator.gi_frame.f_lasti == -1: 
    return GEN_CREATED 
return GEN_SUSPENDED 

Cụ thể, g2 được bắt đầu nếu inspect.getgeneratorstate(g2) != inspect.GEN_CREATED.

1

Là lỗi của bạn ngụ ý send hàm phải được gọi với None trên một máy phát điện vừa bắt đầu (docs-link).

Bạn thể bắt các TypeError and roll từ đó:

#... 
    try: 
     g2.send(0) 
    except TypeError: 
     #Now you know it hasn't started, etc. 
     g2.send(None) 

Dù bằng cách nào nó không thể được sử dụng để 'reset' máy phát điện, nó chỉ có được làm lại.

Tổng quan về khái niệm máy phát điện và cú pháp here, bao gồm chuỗi máy phát và các chủ đề nâng cao khác.

1

Cụ thể, bạn có thể tìm cách sử dụng trang trí consumer được mô tả trên trang. I-131 của David Beazley "Generator Tricks," mà J. Gwyn cung cấp một liên kết:

def consumer(func): 
    def start(*args,**kwargs): 
     c = func(*args,**kwargs) 
     c.next() 
     return c 
    return start 

tôi sử dụng một cái gì đó tương tự trong mã của tôi.

Lưu ý rằng if v is None được ưu tiên hơn if v == None.

+0

Đây thực sự là một giải pháp tốt, nhưng nó chỉ hoạt động cho các chức năng của máy phát điện sử dụng trang trí này. Tôi thích cái gì đó hoạt động trên mọi máy phát điện. Vì vậy, tôi sử dụng giải pháp Rich Franks. – Holger

+0

Đúng, giải pháp này bị hạn chế. IMO quan trọng hơn là nó thay đổi hành vi của máy phát điện. –

0

Dưới đây là triển khai hoàn chỉnh quy trình tương thích Python2, getgeneratorstate (gtor), với mã kiểm tra.

import unittest 
import enum 

class GtorState(enum.Enum): 
    GEN_RUNNING ='GEN_RUNNING' 
    GEN_CLOSED ='GEN_CLOSED' 
    GEN_CREATED ='GEN_CREATED' 
    GEN_SUSPENDED ='GEN_SUSPENDED' 

    @staticmethod 
    def getgeneratorstate(gtor): 
     if gtor.gi_running: 
      return GtorState.GEN_RUNNING 

     if gtor.gi_frame is None: 
      return GtorState.GEN_CLOSED 

     if gtor.gi_frame.f_lasti == -1: 
      return GtorState.GEN_CREATED 

     return GtorState.GEN_SUSPENDED 
    #end-def 

def coro000(): 
    """ a coroutine that does little 

    """ 
    print('-> coroutine started') 
    x =yield 
    print('-> coroutine received ', x) 


class Test_Coro(unittest.TestCase): 
    def test_coro000(self): 

     my_coro000 =coro000() 
     self.assertEqual(GtorState.getgeneratorstate(my_coro000), GtorState.GEN_CREATED) 

     next(my_coro000) # prints '-> coroutine started' 
     self.assertEqual(GtorState.getgeneratorstate(my_coro000), GtorState.GEN_SUSPENDED) 

     try: 
      my_coro000.send(42) # prints '-> coroutine received 42 
      self.assertEqual(GtorState.getgeneratorstate(my_coro000), GtorState.GEN_SUSPENDED) 

      self.fail('should have raised StopIteration ') 

     except StopIteration: 
      self.assertTrue(True, 'On exit a coroutine will throw StopIteration') 
      self.assertEqual(GtorState.getgeneratorstate(my_coro000), GtorState.GEN_CLOSED)