Cách sử dụng bộ lọc, bản đồ và giảm trong Python 3


321

filter, mapreducehoạt động hoàn hảo trong Python 2. Dưới đây là một ví dụ:

>>> def f(x):
        return x % 2 != 0 and x % 3 != 0
>>> filter(f, range(2, 25))
[5, 7, 11, 13, 17, 19, 23]

>>> def cube(x):
        return x*x*x
>>> map(cube, range(1, 11))
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]

>>> def add(x,y):
        return x+y
>>> reduce(add, range(1, 11))
55

Nhưng trong Python 3, tôi nhận được các kết quả đầu ra sau:

>>> filter(f, range(2, 25))
<filter object at 0x0000000002C14908>

>>> map(cube, range(1, 11))
<map object at 0x0000000002C82B70>

>>> reduce(add, range(1, 11))
Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    reduce(add, range(1, 11))
NameError: name 'reduce' is not defined

Tôi sẽ đánh giá cao nếu ai đó có thể giải thích cho tôi tại sao điều này là.

Ảnh chụp màn hình mã để rõ hơn:

Các phiên IDLE của Python 2 và 3 cạnh nhau


1
Nói tóm lại, danh sách không phải là kiểu dữ liệu duy nhất. Nếu bạn muốn có một danh sách, hãy nói rằng bạn muốn có một danh sách. Nhưng trong hầu hết các trường hợp, bạn muốn một cái gì đó khác.
Veky

Câu trả lời:


346

Bạn có thể đọc về những thay đổi trong What New Python trong Python 3.0 . Bạn nên đọc kỹ khi bạn chuyển từ 2.x sang 3.x vì rất nhiều thứ đã được thay đổi.

Toàn bộ câu trả lời ở đây là trích dẫn từ tài liệu.

Lượt xem và vòng lặp thay vì danh sách

Một số API nổi tiếng không còn trả về danh sách:

  • [...]
  • map()filter()trả lại các vòng lặp. Nếu bạn thực sự cần một danh sách, một cách khắc phục nhanh là ví dụ list(map(...)), nhưng cách khắc phục tốt hơn thường là sử dụng khả năng hiểu danh sách (đặc biệt là khi mã gốc sử dụng lambda) hoặc viết lại mã để không cần danh sách. Đặc biệt khó khăn được map()viện dẫn cho các tác dụng phụ của chức năng; chuyển đổi chính xác là sử dụng một forvòng lặp thông thường (vì việc tạo một danh sách sẽ gây lãng phí).
  • [...]

Nội địa

  • [...]
  • Đã xóa reduce(). Sử dụng functools.reduce()nếu bạn thực sự cần nó; tuy nhiên, 99 phần trăm thời gian một forvòng lặp rõ ràng là dễ đọc hơn.
  • [...]

21
Thêm list(map(...) ở khắp mọi nơi .. làm thế nào trên thế giới là giúp dễ đọc .. pythondường như không thể xử lý ứng dụng liên tục / phát trực tuyến của các tổ hợp chức năng. Các ngôn ngữ khác tôi có thể xâu chuỗi hàng tá thao tác với một bộ sưu tập liên tiếp và nó có thể đọc được. Đây? bạn muốn gì - một chục cách lồng nhau in??
javadba

11
Nếu bạn đang làm việc trong một bối cảnh bắt buộc, thì vòng lặp for có lẽ là tùy chọn dễ đọc hơn. Nhưng có những lý do chính đáng để thích một bối cảnh chức năng - và phá vỡ từ đó để quay trở lại thủ tục có thể khá xấu xí.
MatrixManAtYrService

2
@javadba Bạn có chắc chắn trong một "ứng dụng phát trực tuyến" bạn cần thêm listcuộc gọi nào không? Tôi nghĩ ý nghĩa của "phát trực tuyến" là "không có danh sách nào được tạo ra cả, xử lý đầy đủ từng yếu tố của đầu vào trước khi chuyển sang tiếp theo".
Đêm vô thường

@MatrixManAtYrService Nếu bạn chắc chắn hành vi python 2 là những gì bạn cần, bạn luôn có thể xác định lại map.
Đêm vô thường

6
Tôi vẫn không thể hiểu làm thế nào một đối số dễ đọc dẫn đến một sự thay đổi như vậy. Nếu đó là vì lý do hiệu suất, tôi có thể hiểu ...
Minato

86

Các chức năng của mapfilterđã được thay đổi một cách có chủ ý để trả về các trình vòng lặp và giảm được loại bỏ khỏi việc được tích hợp và đặt vào functools.reduce.

Vì vậy, cho filtermap, bạn có thể bọc chúng list()để xem kết quả như bạn đã làm trước đây.

>>> def f(x): return x % 2 != 0 and x % 3 != 0
...
>>> list(filter(f, range(2, 25)))
[5, 7, 11, 13, 17, 19, 23]
>>> def cube(x): return x*x*x
...
>>> list(map(cube, range(1, 11)))
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
>>> import functools
>>> def add(x,y): return x+y
...
>>> functools.reduce(add, range(1, 11))
55
>>>

Đề xuất bây giờ là bạn thay thế việc sử dụng bản đồ và bộ lọc của mình bằng các biểu thức của trình tạo hoặc danh sách hiểu. Thí dụ:

>>> def f(x): return x % 2 != 0 and x % 3 != 0
...
>>> [i for i in range(2, 25) if f(i)]
[5, 7, 11, 13, 17, 19, 23]
>>> def cube(x): return x*x*x
...
>>> [cube(i) for i in range(1, 11)]
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
>>>

Họ nói rằng đối với các vòng lặp là 99% thời gian dễ đọc hơn là giảm, nhưng tôi chỉ cần kiên trì functools.reduce.

Chỉnh sửa : Con số 99 phần trăm được lấy trực tiếp từ trang What New In Python 3.0 do Guido van Rossum sáng tác.


5
Bạn không cần phải tạo thêm chức năng trong việc hiểu danh sách. Chỉ cần sử dụng[i*i*i for i in range(1,11)]
Xiao

2
Bạn hoàn toàn chính xác. Tôi giữ chức năng trong các ví dụ hiểu danh sách để giữ cho nó trông giống với các ví dụ về bộ lọc / bản đồ.
Joshua D. Boyd

5
i ** 3 cũng tương đương với i * i * i
Breezer

5
@Breezer thực sự i**3sẽ gọi i.__pow__(3)i*i*i i.__mul__(i).__mul__(i)(hoặc đại loại như thế). Với ints, điều đó không thành vấn đề, nhưng với các số numpy / các lớp tùy chỉnh, nó thậm chí có thể tạo ra các kết quả khác nhau.
cú pháp

1
Tôi đã nhận thấy rằng bất cứ khi nào chúng ta nghe rằng "Guido đưa ra quyết định X" rằng nỗi đau là kết quả có thể xảy ra. Đây là một ví dụ tuyệt vời: list(list(list(.. )))để làm những gì đã dài dòng trong python.
javadba

12

Là một phụ lục cho các câu trả lời khác, đây có vẻ là một trường hợp sử dụng tốt cho một trình quản lý bối cảnh sẽ ánh xạ lại tên của các hàm này với các hàm trả về danh sách và giới thiệu reducetrong không gian tên toàn cục.

Một triển khai nhanh có thể trông như thế này:

from contextlib import contextmanager    

@contextmanager
def noiters(*funcs):
    if not funcs: 
        funcs = [map, filter, zip] # etc
    from functools import reduce
    globals()[reduce.__name__] = reduce
    for func in funcs:
        globals()[func.__name__] = lambda *ar, func = func, **kwar: list(func(*ar, **kwar))
    try:
        yield
    finally:
        del globals()[reduce.__name__]
        for func in funcs: globals()[func.__name__] = func

Với cách sử dụng trông như thế này:

with noiters(map):
    from operator import add
    print(reduce(add, range(1, 20)))
    print(map(int, ['1', '2']))

Bản in nào:

190
[1, 2]

Chỉ 2 xu của tôi :-)


1
pythonnhư một ngôn ngữ là một mớ hỗn độn - nhưng nó có v tốt để các thư viện tuyệt vời: numpy, pandas, statsmodelsvà bạn bè .. Tôi đã Buliding thư viện tiện như bạn thấy ở đây để giảm sự đau đớn của ngôn ngữ mẹ đẻ - nhưng đã bị mất năng lượng và cố gắng không để đi lạc xa a data.frame/ datatable, hoặc xarray. Nhưng kudos đã cố gắng ..
javadba

7

reducephương thức đã bị xóa khỏi hàm dựng sẵn từ Python3, đừng quên nhập functoolsmã trong mã của bạn. Vui lòng xem đoạn mã dưới đây.

import functools
my_list = [10,15,20,25,35]
sum_numbers = functools.reduce(lambda x ,y : x+y , my_list)
print(sum_numbers)

2

Dưới đây là các ví dụ về chức năng Lọc, ánh xạ và giảm.

số = [10,11,12,22,34,43,54,34,67,87,88,98,99,87,44,66]

// Bộ lọc

lẻNumbers = danh sách (bộ lọc (lambda x: x% 2! = 0, số))

in (số lẻ)

//Bản đồ

MultiplyOf2 = list (map (lambda x: x * 2, số))

in (MultiplyOf2)

//Giảm

Hàm less, vì nó không được sử dụng phổ biến, đã bị xóa khỏi các hàm dựng sẵn trong Python 3. Nó vẫn có sẵn trong mô-đun funcools, vì vậy bạn có thể làm:

từ funcools giảm nhập

sumOfNumbers = giảm (lambda x, y: x + y, số)

in (sumOfNumbers)


0

Một trong những lợi thế của bản đồ, bộ lọc và thu nhỏ là mức độ dễ đọc của chúng khi bạn "xâu chuỗi" chúng lại với nhau để làm một việc gì đó phức tạp. Tuy nhiên, cú pháp tích hợp không dễ đọc và hoàn toàn "ngược". Vì vậy, tôi đề nghị sử dụng PyFunctionalgói ( https://pypi.org/project/PyFunctional/ ). Đây là một so sánh của hai:

flight_destinations_dict = {'NY': {'London', 'Rome'}, 'Berlin': {'NY'}}

Phiên bản PyFeftal

Cú pháp rất dễ đọc. Bạn có thể nói:

"Tôi có một chuỗi các điểm đến chuyến bay. Trong đó tôi muốn lấy khóa chính nếu thành phố nằm trong các giá trị chính tả. Cuối cùng, hãy lọc ra các danh sách trống tôi đã tạo trong quy trình."

from functional import seq  # PyFunctional package to allow easier syntax

def find_return_flights_PYFUNCTIONAL_SYNTAX(city, flight_destinations_dict):
    return seq(flight_destinations_dict.items()) \
        .map(lambda x: x[0] if city in x[1] else []) \
        .filter(lambda x: x != []) \

Phiên bản Python mặc định

Tất cả đều ngược. Bạn cần nói:

"OK, vì vậy, có một danh sách. Tôi muốn lọc các danh sách trống ra khỏi nó. Tại sao? Bởi vì lần đầu tiên tôi nhận được khóa chính tả nếu thành phố nằm trong các giá trị chính tả. "

def find_return_flights_DEFAULT_SYNTAX(city, flight_destinations_dict):
    return list(
        filter(lambda x: x != [],
               map(lambda x: x[0] if city in x[1] else [], flight_destinations_dict.items())
               )
    )
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.