Python: in một biểu thức trình tạo?


103

Trong trình bao Python, nếu tôi nhập một danh sách dễ hiểu như:

>>> [x for x in string.letters if x in [y for y in "BigMan on campus"]]

Tôi nhận được một kết quả được in độc đáo:

['a', 'c', 'g', 'i', 'm', 'n', 'o', 'p', 's', 'u', 'B', 'M']

Tương tự đối với cách hiểu từ điển:

>>> {x:x*2 for x in range(1,10)}
{1: 2, 2: 4, 3: 6, 4: 8, 5: 10, 6: 12, 7: 14, 8: 16, 9: 18}

Nếu tôi nhập một biểu thức trình tạo, tôi không nhận được phản hồi thân thiện như vậy:

>>> (x for x in string.letters if x in (y for y in "BigMan on campus"))
<generator object <genexpr> at 0x1004a0be0>

Tôi biết tôi có thể làm điều này:

>>> for i in _: print i,
a c g i m n o p s u B M

Ngoài điều đó (hoặc viết một hàm trợ giúp), tôi có thể dễ dàng đánh giá và in đối tượng trình tạo đó trong shell tương tác không?


2
Vấn đề thực sự ở đây là gì? Những gì bạn đang thiếu?
Andreas Jung

3
@pynator: "Vấn đề thực sự" chỉ là tôi muốn có thể in nội dung generator objectkhi tôi tương tác xây dựng khả năng hiểu tại dấu nhắc tương tác. Gọi điện list(_)làm điều đó. Những gì tôi đã làm là sử dụng hiểu danh sách sau đó biến chúng thành genxp trong mã lớn hơn. Những điều này có thể không thành công trong thời gian chạy theo những cách mà danh sách hiểu không.
the wolf

5
Câu trả lời ngắn gọn là không thể in một biểu thức trình tạo vì các giá trị của nó không tồn tại; chúng được tạo ra theo yêu cầu. Những gì bạn có thể làm (giả sử một lúc nào đó trình tạo dừng lại) là lấy tất cả các giá trị ra khỏi nó, như với list(), và sau đó in chúng.
Kos

Câu trả lời:


161

Câu trả lời nhanh:

Làm list()xung quanh một biểu thức trình tạo (gần như) chính xác tương đương với việc có []dấu ngoặc nhọn xung quanh nó. Vì vậy, vâng, bạn có thể làm

>>> list((x for x in string.letters if x in (y for y in "BigMan on campus")))

Nhưng bạn cũng có thể làm

>>> [x for x in string.letters if x in (y for y in "BigMan on campus")]

Có, điều đó sẽ biến biểu thức trình tạo thành một danh sách dễ hiểu. Đó là điều tương tự và danh sách gọi () trên đó. Vì vậy, cách để tạo một biểu thức trình tạo thành một danh sách là đặt các dấu ngoặc nhọn xung quanh nó.

Giải thích chi tiết:

Biểu thức trình tạo là một biểu thức "trần trụi" for. Như vậy:

x*x for x in range(10)

Bây giờ, bạn không thể tự dán nó vào một dòng, bạn sẽ gặp lỗi cú pháp. Nhưng bạn có thể đặt dấu ngoặc đơn xung quanh nó.

>>> (x*x for x in range(10))
<generator object <genexpr> at 0xb7485464>

Điều này đôi khi được gọi là hiểu trình tạo, mặc dù tôi nghĩ tên chính thức vẫn là biểu thức trình tạo, thực sự không có bất kỳ sự khác biệt nào, dấu ngoặc đơn chỉ ở đó để làm cho cú pháp hợp lệ. Bạn không cần chúng nếu bạn đang chuyển nó vào làm tham số duy nhất cho một hàm, ví dụ:

>>> sorted(x*x for x in range(10))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Về cơ bản tất cả các cách hiểu khác có sẵn trong Python 3 và Python 2.7 chỉ là đường cú pháp xung quanh một biểu thức trình tạo. Đặt hiểu biết:

>>> {x*x for x in range(10)}
{0, 1, 4, 81, 64, 9, 16, 49, 25, 36}

>>> set(x*x for x in range(10))
{0, 1, 4, 81, 64, 9, 16, 49, 25, 36}

Đọc hiểu chính tả:

>>> dict((x, x*x) for x in range(10))
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}

>>> {x: x*x for x in range(10)}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}

Và liệt kê các kiến ​​thức hiểu trong Python 3:

>>> list(x*x for x in range(10))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

>>> [x*x for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Trong Python 2, khả năng hiểu danh sách không chỉ là đường cú pháp. Nhưng sự khác biệt duy nhất là x sẽ bị rò rỉ trong Python 2 vào không gian tên.

>>> x
9

Trong khi sử dụng Python 3, bạn sẽ nhận được

>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined

Điều này có nghĩa là cách tốt nhất để có được một bản in đẹp về nội dung của biểu thức trình tạo của bạn bằng Python là tạo một danh sách dễ hiểu từ nó! Tuy nhiên, điều này rõ ràng sẽ không hoạt động nếu bạn đã có một đối tượng máy phát điện. Làm điều đó sẽ chỉ tạo danh sách một trình tạo:

>>> foo = (x*x for x in range(10))
>>> [foo]
[<generator object <genexpr> at 0xb7559504>]

Trong trường hợp đó, bạn sẽ cần gọi list():

>>> list(foo)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Mặc dù điều này hoạt động, nhưng hơi ngu ngốc:

>>> [x for x in foo]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

5
Phần còn lại hạn chính thức "biểu hiện máy phát điện" vì từ "hiểu" ngụ ý lặp, đó là một điều một genexp không làm, như câu hỏi này và câu trả lời minh họa độc đáo :)
ncoghlan

2
list( generator-expression )không in biểu thức trình tạo; nó đang tạo một danh sách (và sau đó in nó trong một trình bao tương tác). Thay vì tạo một danh sách, trong Python 3, bạn có thể tách biểu thức trình tạo thành một câu lệnh in. Tức là) print(*(generator-expression)). Thao tác này sẽ in ra các phần tử không có dấu phẩy và không có dấu ngoặc ở đầu và cuối.
AJNeufeld,

17

Không giống như một danh sách hoặc một từ điển, một trình tạo có thể là vô hạn. Làm điều này sẽ không hiệu quả:

def gen():
    x = 0
    while True:
        yield x
        x += 1
g1 = gen()
list(g1)   # never ends

Ngoài ra, việc đọc máy phát điện cũng thay đổi nó, vì vậy không có cách nào hoàn hảo để xem nó. Để xem mẫu đầu ra của trình tạo, bạn có thể làm

g1 = gen()
[g1.next() for i in range(10)]

2
Đã bỏ phiếu vì tuyên bố rằng trình tạo có thể là vô hạn, do đó gây ra vòng lặp hoặc tạm dừng hoàn toàn (tùy thuộc vào thông số kỹ thuật của bạn (lol)).
Milan Velebit

16

Bạn chỉ có thể gói biểu thức trong một cuộc gọi tới list:

>>> list(x for x in string.letters if x in (y for y in "BigMan on campus"))
['a', 'c', 'g', 'i', 'm', 'n', 'o', 'p', 's', 'u', 'B', 'M']

15

Hoặc bạn luôn có thể mapqua một trình lặp mà không cần tạo danh sách trung gian:

>>> _ = map(sys.stdout.write, (x for x in string.letters if x in (y for y in "BigMan on campus")))
acgimnopsuBM

3
đây là câu trả lời duy nhất thực sự in nội dung của máy phát điện mà không tạo đối tượng lớn.
Marek R

2
>>> list(x for x in string.letters if x in (y for y in "BigMan on campus"))
['a', 'c', 'g', 'i', 'm', 'n', 'o', 'p', 's', 'u', 'B', 'M']

Trong trường hợp bộ tạo là vô hạn, nó sẽ gây ra một vòng lặp.
Milan Velebit
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.