Làm thế nào để chọn chỉ một mục từ máy phát điện (bằng python)?


213

Tôi có một chức năng tạo như sau:

def myfunct():
  ...
  yield result

Cách thông thường để gọi chức năng này sẽ là:

for r in myfunct():
  dostuff(r)

Câu hỏi của tôi, có cách nào để có được chỉ một yếu tố từ trình tạo bất cứ khi nào tôi muốn không? Ví dụ: tôi muốn làm một cái gì đó như:

while True:
  ...
  if something:
      my_element = pick_just_one_element(myfunct())
      dostuff(my_element)
  ...

Câu trả lời:


304

Tạo một trình tạo bằng cách sử dụng

g = myfunct()

Bất cứ khi nào bạn muốn một mục, sử dụng

next(g)

(hoặc g.next()trong Python 2.5 trở xuống).

Nếu máy phát điện thoát, nó sẽ tăng StopIteration. Bạn có thể bắt ngoại lệ này nếu cần hoặc sử dụng defaultđối số để next():

next(g, default_value)

4
Lưu ý, nó sẽ chỉ tăng StopIteration khi bạn cố gắng sử dụng g.next () sau khi mục cuối cùng trong g được cung cấp.
Wilduck

26
next(gen, default)cũng có thể được sử dụng để tránh StopIterationngoại lệ. Ví dụ, next(g, None)đối với một trình tạo chuỗi sẽ mang lại một chuỗi hoặc Không có sau khi việc lặp lại kết thúc.
Attila

8
trong Python 3000, next () là __next __ ()
Jonathan Baldwin

27
@JonathanBaldwin: Nhận xét của bạn có phần sai lệch. Trong Python 3, bạn sẽ sử dụng cú pháp thứ hai được đưa ra trong câu trả lời của tôi , next(g). Điều này sẽ gọi nội bộ g.__next__(), nhưng bạn không thực sự phải lo lắng về điều đó, giống như bạn thường không quan tâm đến len(a)các cuộc gọi nội bộ đó a.__len__().
Sven Marnach

14
Tôi cần phải có được rõ ràng hơn. g.next()g.__next__()trong py3k. Nội dung next(iterator)đã xuất hiện từ Python 2.6 và là thứ nên được sử dụng trong tất cả các mã Python mới và thật đơn giản để sao lưu nếu bạn cần hỗ trợ py <= 2.5.
Jonathan Baldwin

29

Để chọn chỉ một phần tử của trình tạo, sử dụng breaktrong forcâu lệnh hoặclist(itertools.islice(gen, 1))

Theo ví dụ của bạn (theo nghĩa đen), bạn có thể làm một cái gì đó như:

while True:
  ...
  if something:
      for my_element in myfunct():
          dostuff(my_element)
          break
      else:
          do_generator_empty()

Nếu bạn muốn " chỉ lấy một phần tử từ trình tạo [một lần được tạo] bất cứ khi nào tôi thích " (tôi cho rằng 50% đó là ý định ban đầu và ý định phổ biến nhất) thì:

gen = myfunct()
while True:
  ...
  if something:
      for my_element in gen:
          dostuff(my_element)
          break
      else:
          do_generator_empty()

Cách này generator.next()có thể tránh sử dụng rõ ràng và xử lý cuối đầu vào không yêu cầu StopIterationxử lý ngoại lệ (mật mã) hoặc so sánh giá trị mặc định bổ sung.

Các else:của forphần tuyên bố chỉ là cần thiết nếu bạn muốn làm điều gì đó đặc biệt trong trường hợp end-of-máy phát điện.

Lưu ý vào next()/ .next():

Trong Python3, .next()phương thức được đổi tên thành .__next__()vì lý do chính đáng: nó được coi là mức thấp (PEP 3114). Trước Python 2.6, hàm dựng sẵn next()không tồn tại. Và nó thậm chí đã được thảo luận để chuyển next()sang operatormô-đun (điều này sẽ là khôn ngoan), vì nhu cầu hiếm hoi của nó và lạm phát đáng ngờ của các tên dựng sẵn.

Sử dụng next()mà không có mặc định vẫn là một thực tiễn ở mức độ rất thấp - ném mật mã StopIterationnhư một tia sáng ra khỏi màu xanh trong mã ứng dụng thông thường một cách công khai. Và việc sử dụng next()với sentinel mặc định - tốt nhất nên là tùy chọn duy nhất cho một next()trực tiếp trong builtins- bị hạn chế và thường đưa ra lý do cho logic / khả năng đọc không phải là pythonic.

Dòng dưới cùng: Sử dụng next () sẽ rất hiếm - như sử dụng các chức năng của operatormô-đun. Sử dụng for x in iterator, islice, list(iterator)và các chức năng khác chấp nhận một iterator liên tục là cách tự nhiên của việc sử dụng vòng lặp trên mức ứng dụng - và khá luôn càng tốt. next()là cấp độ thấp, một khái niệm bổ sung, không khó hiểu - như câu hỏi của chủ đề này cho thấy. Trong khi ví dụ sử dụng breaktrong forlà thông thường.


8
Đây là cách làm việc quá nhiều cho việc chỉ nhận phần tử đầu tiên của kết quả danh sách. Thường thì tôi không cần phải lười biếng nhưng không có lựa chọn nào trong py3. Không có cái gì đó giống như mySeq.head?
javadba

2

Tôi không tin có một cách thuận tiện để lấy giá trị tùy ý từ trình tạo. Trình tạo sẽ cung cấp một phương thức () tiếp theo để tự đi qua, nhưng toàn bộ chuỗi không được tạo ngay lập tức để tiết kiệm bộ nhớ. Đó là sự khác biệt về chức năng giữa máy phát điện và danh sách.


1

Đối với những người bạn đang quét qua các câu trả lời này để biết ví dụ hoạt động hoàn chỉnh cho Python3 ... đây là ya go:

def numgen():
    x = 1000
    while True:
        x += 1
        yield x

nums = numgen() # because it must be the _same_ generator

for n in range(3):
    numnext = next(nums)
    print(numnext)

Kết quả này:

1001
1002
1003

1

Trình tạo là một hàm tạo ra một trình vòng lặp. Do đó, khi bạn có phiên bản iterator, hãy sử dụng next () để tìm nạp mục tiếp theo từ iterator. Ví dụ: sử dụng hàm next () để tìm nạp mục đầu tiên và sau đó sử dụng for inđể xử lý các mục còn lại:

# create new instance of iterator by calling a generator function
items = generator_function()

# fetch and print first item
first = next(items)
print('first item:', first)

# process remaining items:
for item in items:
    print('next item:', item)

0
generator = myfunct()
while True:
   my_element = generator.next()

đảm bảo bắt ngoại lệ được ném sau khi phần tử cuối cùng được lấy


Không hợp lệ cho Python 3, xem câu trả lời xuất sắc của kxr .
clacke

2
Chỉ cần thay thế "Generator.next ()" bằng "next (trình tạo)" cho Python 3.
iyop45

-3

Tôi tin rằng cách duy nhất là lấy một danh sách từ iterator sau đó lấy phần tử bạn muốn từ danh sách đó.

l = list(myfunct())
l[4]

Câu trả lời của Sven có lẽ tốt hơn, nhưng tôi sẽ để nó ở đây trong trường hợp nó phù hợp hơn với nhu cầu của bạn.
keegan3d

26
Hãy chắc chắn rằng bạn có một máy phát hữu hạn trước khi làm điều này.
Seth

6
Xin lỗi, điều này có độ phức tạp của độ dài của vòng lặp, trong khi vấn đề rõ ràng là O (1).
yo '

1
Lãng phí quá nhiều bộ nhớ và quá trình để hút từ máy phát điện! Ngoài ra, như @Seth đã đề cập trước đó, máy phát điện không được đảm bảo khi nào ngừng phát điện.
pylover

Đây rõ ràng không phải là cách duy nhất (hoặc cách tốt nhất nếu myfunct()tạo ra một số lượng lớn các giá trị) vì bạn có thể sử dụng hàm dựng sẵn nextđể lấy giá trị được tạo tiếp theo.
HelloGoodbye
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.