Làm thế nào để thực hiện nhiều đối số cho hàm ánh xạ trong đó một đối số vẫn giữ nguyên trong python?


155

Hãy nói rằng chúng ta có một chức năng thêm như sau

def add(x, y):
    return x + y

chúng tôi muốn áp dụng chức năng bản đồ cho một mảng

map(add, [1, 2, 3], 2)

Các ngữ nghĩa là tôi muốn thêm 2 vào mọi phần tử của mảng. Nhưng maphàm cũng yêu cầu một danh sách trong đối số thứ ba.

Lưu ý: Tôi đang đặt ví dụ thêm cho đơn giản. Chức năng ban đầu của tôi phức tạp hơn nhiều. Và tất nhiên tùy chọn thiết lập giá trị mặc định của yhàm add là không cần thiết vì nó sẽ được thay đổi cho mỗi cuộc gọi.


20
điều này giống hệt như trong Lisp: map(add,[1,2,3],[2]*3) nói chung mapsẽ lấy một hàm làm đối số đầu tiên của nó và nếu hàm này có đối số K , bạn phải theo dõi với K iterable:addTriple(a,b,c) -> map(addTriple,[...],[...],[...])
watashiSHUN

Câu trả lời:


188

Một lựa chọn là một sự hiểu biết danh sách:

[add(x, 2) for x in [1, 2, 3]]

Lựa chọn khác:

a = [1, 2, 3]

import functools
map(functools.partial(add, y=2), a)

import itertools
map(add, a, itertools.repeat(2, len(a)))

1
yup, nhưng nó nhanh như thế nào so với chức năng bản đồ?
Shan

@Shan: Rất giống nhau, đặc biệt nếu add()là một chức năng không tầm thường
Sven Marnach

2
@Shan: Hãy xem NumPy trong trường hợp này. Nếu đây thực sự là một vấn đề đối với bạn, sự khác biệt về tốc độ giữa việc hiểu danh sách và map()sẽ không giúp được gì.
Sven Marnach

3
@Shan: Như tôi đã nói trước đây, hãy xem NumPy. Nó có thể giúp tăng tốc các vòng lặp một cách có điều kiện, miễn là chúng có thể được véc tơ.
Sven Marnach

1
@abarnert: Không rõ OP đang sử dụng Python 2 hay 3. Để đảm bảo ví dụ này hoạt động trong Python 2, tôi đã bao gồm tham số. (Lưu ý rằng map()hoạt động như zip_longest()trong Python 2, trong khi nó hoạt động như zip()trong Python 3.)
Sven Marnach

60

Các tài liệu đề nghị rõ ràng đây là cách sử dụng chính cho itertools.repeat:

Tạo một trình vòng lặp trả lại đối tượng nhiều lần. Chạy vô thời hạn trừ khi đối số lần được chỉ định. Được sử dụng làm đối số map()cho các tham số bất biến cho hàm được gọi. Cũng được sử dụng zip()để tạo một phần bất biến của bản ghi tuple.

Và không có lý do để vượt qua len([1,2,3])như là timesđối số; mapdừng ngay khi lần lặp đầu tiên được tiêu thụ, do đó, lần lặp vô hạn là hoàn toàn tốt:

>>> from operator import add
>>> from itertools import repeat
>>> list(map(add, [1,2,3], repeat(4)))
[5, 6, 7]

Trong thực tế, điều này tương đương với ví dụ cho repeattrong các tài liệu:

>>> list(map(pow, range(10), repeat(2)))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Điều này làm cho một giải pháp ngôn ngữ-chức năng-ngôn ngữ lười biếng tốt đẹp cũng hoàn toàn có thể đọc được bằng thuật ngữ Python-iterator.


2
+1 Đây phải là câu trả lời được chấp nhận. Tất nhiên, nó mở rộng đến bất kỳ số lượng tham số nào, với một số danh sách hoặc bộ tạo hoặc hằng số khác. Vd: def f(x,y,z): \\ return '.'.join([x,y,z])và sau đó: list(map(f, list('abc'), repeat('foo'), list('defgh')))trả về ['a.foo.d', 'b.foo.e', 'c.foo.f'].
Pierre D

5
Trong Python 2, cần phải cung cấp đối số độ dài repeat(), vì map()sẽ chạy cho đến khi trình lặp dài nhất bị cạn kiệt trong phiên bản Python đó, điền vào Nonetất cả các giá trị bị thiếu. Nói rằng "không có lý do" để truyền tham số là sai.
Sven Marnach

Nhận xét của @SvenMarnach không phải là nhỏ: Hành vi của Python 3Python 2 thay đổi đáng kể!
Agustín

36

Sử dụng một danh sách hiểu.

[x + 2 for x in [1, 2, 3]]

Nếu bạn thực sự , thực sự , thực sự muốn sử dụng map, hãy cung cấp cho nó một hàm ẩn danh làm đối số đầu tiên:

map(lambda x: x + 2, [1,2,3])

16

Bản đồ có thể chứa nhiều đối số, cách tiêu chuẩn là

map(add, a, b)

Trong câu hỏi của bạn, nó nên được

map(add, a, [2]*len(a))

Không chắc câu hỏi của anh ấy có ý nghĩa gì nhưng tôi nghĩ add chấp nhận 2 giá trị và 2 giá trị đó không cố định và sẽ đến từ người dùng như arg1 đang đến. Vậy trong trường hợp này chúng ta nên gọi add (x, y) dưới bản đồ như thế nào?
Bim Meat Sharma

15

Câu trả lời đúng đơn giản hơn bạn nghĩ. Đơn giản chỉ cần làm:

map(add, [(x, 2) for x in [1,2,3]])

Và thay đổi việc thực hiện add để mất một tuple tức là

def add(t):
   x, y = t
   return x+y

Điều này có thể xử lý bất kỳ trường hợp sử dụng phức tạp nào trong đó cả hai tham số đều là động.


14

Đôi khi tôi đã giải quyết các tình huống tương tự (chẳng hạn như sử dụng phương thức pandas.apply ) bằng cách sử dụng các bao đóng

Để sử dụng chúng, bạn xác định một hàm tự động xác định và trả về một trình bao bọc cho hàm của bạn, làm cho một trong các tham số trở thành một hằng số một cách hiệu quả.

Một cái gì đó như thế này:

def add(x, y):
   return x + y

def add_constant(y):
    def f(x):
        return add(x, y)
    return f

Sau đó, add_constant(y)trả về một hàm có thể được sử dụng để thêm yvào bất kỳ giá trị nào:

>>> add_constant(2)(3)
5

Điều này cho phép bạn sử dụng nó trong mọi tình huống trong đó các tham số được đưa ra cùng một lúc:

>>> map(add_constant(2), [1,2,3])
[3, 4, 5]

biên tập

Nếu bạn không muốn phải viết hàm đóng ở một nơi khác, bạn luôn có khả năng xây dựng nó một cách nhanh chóng bằng cách sử dụng hàm lambda:

>>> map(lambda x: add(x, 2), [1, 2, 3])
[3, 4, 5]

13

Nếu bạn có sẵn nó, tôi sẽ xem xét sử dụng numpy. Nó rất nhanh cho các loại hoạt động này:

>>> import numpy
>>> numpy.array([1,2,3]) + 2
array([3, 4, 5])

Điều này là giả sử ứng dụng thực sự của bạn đang thực hiện các hoạt động toán học (có thể được vector hóa).


10

Nếu bạn thực sự cần sử dụng chức năng bản đồ (như bài tập lớp của tôi ở đây ...), bạn có thể sử dụng hàm bao bọc với 1 đối số, chuyển phần còn lại cho phần gốc trong phần thân của nó; I E :

extraArguments = value
def myFunc(arg):
    # call the target function
    return Func(arg, extraArguments)


map(myFunc, itterable)

Dơ bẩn & xấu xí, vẫn làm trò bịp


1
Một đóng cửa, tôi nghĩ rằng điều này thêm một cái gì đó, cộng với một. Tương tự như một chức năng một phần, như câu trả lời được chấp nhận có.
Aaron Hall

1
myFunccần phảireturn Func(arg, extraArguments)
PM 2Ring

Tôi đã sửa @ PM2Ring, điều đó đúng; cập nhật mã.
Todor Minakov

Func được định nghĩa ở đâu? bởi vì tôi đang nhận được một lỗi tên cho Func
Nikhil Mishra

Funclà tên của chức năng ban đầu của bạn - addví dụ như OP chặt chẽ
Todor Minakov

6

Tôi tin rằng starmap là những gì bạn cần:

from itertools import starmap


def test(x, y, z):
    return x + y + z

list(starmap(test, [(1, 2, 3), (4, 5, 6)]))

lỗi đánh máy trong ví dụ của bạn: list (starmap (test, [(1, 2, 3), (4, 5, 6)]))
Teebu

Ồ, vâng. Cảm ơn :)
Shqi.Yang

1

Để truyền nhiều đối số cho một maphàm.

def q(x,y):
    return x*y

print map (q,range(0,10),range(10,20))

Ở đây q là hàm có nhiều đối số mà map () gọi. Hãy chắc chắn rằng, độ dài của cả hai phạm vi tức là

len (range(a,a')) and len (range(b,b')) are equal.

1

Trong :nums = [1, 2, 3]

Trong :map(add, nums, [2]*len(nums))

Ngoài:[3, 4, 5]


1
Điều này không cung cấp một câu trả lời cho câu hỏi. Bạn có thể tìm kiếm các câu hỏi tương tự hoặc tham khảo các câu hỏi liên quan và được liên kết ở phía bên phải của trang để tìm câu trả lời. Nếu bạn có một câu hỏi liên quan nhưng khác nhau, hãy đặt một câu hỏi mới và bao gồm một liên kết đến câu hỏi này để giúp cung cấp ngữ cảnh. Xem: Đặt câu hỏi, nhận câu trả lời, không làm phiền
Tim Diekmann

1

Bạn có thể bao gồm lambda cùng với bản đồ:

list(map(lambda a: a+2, [1, 2, 3]))

1
def func(a, b, c, d):
 return a + b * c % d
map(lambda x: func(*x), [[1,2,3,4], [5,6,7,8]])

Bằng cách gói hàm gọi bằng lambda và sử dụng giải nén sao, bạn có thể thực hiện bản đồ với số lượng đối số tùy ý.


lambda rất chậm, thay vào đó hãy sử dụng funcools
Wang

0

Một lựa chọn khác là:

results = []
for x in [1,2,3]:
    z = add(x,2)
    ...
    results += [f(z,x,y)]

Định dạng này rất hữu ích khi gọi nhiều chức năng.

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.