Có một lý do để thích sử dụng map()
hơn hiểu danh sách hoặc ngược lại? Là một trong số họ nói chung hiệu quả hơn hoặc được coi là nói chung nhiều pythonic hơn những người khác?
Có một lý do để thích sử dụng map()
hơn hiểu danh sách hoặc ngược lại? Là một trong số họ nói chung hiệu quả hơn hoặc được coi là nói chung nhiều pythonic hơn những người khác?
Câu trả lời:
map
có thể nhanh hơn kính hiển vi trong một số trường hợp (khi bạn KHÔNG tạo lambda cho mục đích, nhưng sử dụng cùng chức năng trong bản đồ và listcomp). Việc hiểu danh sách có thể nhanh hơn trong các trường hợp khác và hầu hết (không phải tất cả) pythonistas coi chúng trực tiếp và rõ ràng hơn.
Một ví dụ về lợi thế tốc độ nhỏ của bản đồ khi sử dụng chính xác cùng một chức năng:
$ python -mtimeit -s'xs=range(10)' 'map(hex, xs)'
100000 loops, best of 3: 4.86 usec per loop
$ python -mtimeit -s'xs=range(10)' '[hex(x) for x in xs]'
100000 loops, best of 3: 5.58 usec per loop
Một ví dụ về cách so sánh hiệu suất bị đảo ngược hoàn toàn khi bản đồ cần lambda:
$ python -mtimeit -s'xs=range(10)' 'map(lambda x: x+2, xs)'
100000 loops, best of 3: 4.24 usec per loop
$ python -mtimeit -s'xs=range(10)' '[x+2 for x in xs]'
100000 loops, best of 3: 2.32 usec per loop
map(operator.attrgetter('foo'), objs)
dễ đọc hơn [o.foo for o in objs]
?!
o
ở đây, và ví dụ của bạn cho thấy lý do tại sao.
str()
ví dụ của anh ấy .
Các trường hợp
map
, mặc dù nó được coi là 'unpythonic'. Ví dụ, map(sum, myLists)
là thanh lịch / ngắn gọn hơn [sum(x) for x in myLists]
. Bạn có được sự tao nhã khi không phải tạo ra một biến giả (ví dụ sum(x) for x...
hoặc sum(_) for _...
hoặc sum(readableName) for readableName...
) mà bạn phải gõ hai lần, chỉ để lặp lại. Lập luận tương tự giữ filter
và reduce
và bất cứ điều gì từ itertools
mô-đun: nếu bạn đã có sẵn một chức năng, bạn có thể tiếp tục và thực hiện một số lập trình chức năng. Điều này đạt được khả năng đọc trong một số tình huống và mất nó trong các tình huống khác (ví dụ: lập trình viên mới làm quen, nhiều đối số) ... nhưng khả năng đọc mã của bạn phụ thuộc rất nhiều vào nhận xét của bạn.map
chức năng như một chức năng trừu tượng thuần túy trong khi thực hiện lập trình chức năng, trong đó bạn đang lập bản đồ map
, hoặc curry map
, hoặc nói cách khác là có lợi khi nói về map
chức năng. Ví dụ, trong Haskell, giao diện functor được gọi là fmap
tổng quát hóa ánh xạ trên bất kỳ cấu trúc dữ liệu nào. Điều này rất không phổ biến trong python vì ngữ pháp python buộc bạn phải sử dụng kiểu trình tạo để nói về phép lặp; bạn không thể khái quát nó dễ dàng. (Điều này đôi khi tốt và đôi khi xấu.) Bạn có thể đến với các ví dụ trăn quý hiếm, nơi đó map(f, *lists)
là một điều hợp lý để làm. Ví dụ gần nhất tôi có thể đưa ra là sumEach = partial(map,sum)
, đó là một lớp lót tương đương với:def sumEach(myLists):
return [sum(_) for _ in myLists]
for
-loop : Tất nhiên bạn cũng có thể chỉ sử dụng một vòng lặp for. Mặc dù không thanh lịch theo quan điểm lập trình chức năng, đôi khi các biến không cục bộ làm cho mã rõ ràng hơn trong các ngôn ngữ lập trình bắt buộc như python, bởi vì mọi người rất quen đọc mã theo cách đó. Nhìn chung, các vòng lặp cũng hiệu quả nhất khi bạn chỉ thực hiện bất kỳ thao tác phức tạp nào không xây dựng danh sách như hiểu danh sách và bản đồ được tối ưu hóa cho (ví dụ: tổng hợp hoặc tạo cây, v.v.) - ít nhất hiệu quả về mặt bộ nhớ (không nhất thiết là về thời gian, trong đó tôi mong đợi điều tồi tệ nhất là yếu tố bất biến, loại bỏ một số trục trặc thu gom rác bệnh lý hiếm gặp)."Chủ nghĩa trăn"
Tôi không thích từ "pythonic" bởi vì tôi không thấy rằng pythonic luôn thanh lịch trong mắt tôi. Tuy nhiên, map
và filter
các chức năng tương tự (như itertools
mô-đun rất hữu ích ) có thể được coi là unpythonic về kiểu dáng.
Lười biếng
Về hiệu quả, giống như hầu hết các cấu trúc lập trình chức năng, MAP CAN BE LAZY , và trên thực tế là lười biếng trong python. Điều đó có nghĩa là bạn có thể làm điều này (trong python3 ) và máy tính của bạn sẽ không hết bộ nhớ và mất tất cả dữ liệu chưa được lưu của bạn:
>>> map(str, range(10**100))
<map object at 0x2201d50>
Hãy thử làm điều đó với một sự hiểu biết danh sách:
>>> [str(n) for n in range(10**100)]
# DO NOT TRY THIS AT HOME OR YOU WILL BE SAD #
Xin lưu ý rằng việc hiểu danh sách vốn dĩ rất lười biếng, nhưng python đã chọn thực hiện chúng là không lười biếng . Tuy nhiên, python không hỗ trợ việc hiểu danh sách lười biếng dưới dạng biểu thức trình tạo, như sau:
>>> (str(n) for n in range(10**100))
<generator object <genexpr> at 0xacbdef>
Về cơ bản, bạn có thể nghĩ về [...]
cú pháp như chuyển một biểu thức trình tạo đến hàm tạo danh sách, như thế nào list(x for x in range(5))
.
Ví dụ ngắn gọn
from operator import neg
print({x:x**2 for x in map(neg,range(5))})
print({x:x**2 for x in [-y for y in range(5)]})
print({x:x**2 for x in (-y for y in range(5))})
Khả năng hiểu danh sách là không lười biếng, vì vậy có thể cần nhiều bộ nhớ hơn (trừ khi bạn sử dụng khả năng hiểu trình tạo). Dấu ngoặc vuông [...]
thường làm cho mọi thứ rõ ràng, đặc biệt là khi trong một mớ dấu ngoặc đơn. Mặt khác, đôi khi bạn trở nên dài dòng như gõ [x for x in...
. Miễn là bạn giữ các biến lặp của bạn ngắn, việc hiểu danh sách thường rõ ràng hơn nếu bạn không thụt mã. Nhưng bạn luôn có thể thụt lề mã của bạn.
print(
{x:x**2 for x in (-y for y in range(5))}
)
hoặc phá vỡ mọi thứ:
rangeNeg5 = (-y for y in range(5))
print(
{x:x**2 for x in rangeNeg5}
)
So sánh hiệu quả cho python3
map
bây giờ lười biếng:
% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=map(f,xs)'
1000000 loops, best of 3: 0.336 usec per loop ^^^^^^^^^
Do đó, nếu bạn sẽ không sử dụng tất cả dữ liệu của mình hoặc không biết trước mình cần bao nhiêu dữ liệu, map
trong python3 (và biểu thức trình tạo trong python2 hoặc python3) sẽ tránh tính toán giá trị của chúng cho đến giây phút cuối cùng cần thiết. Thông thường, điều này thường sẽ vượt trội hơn bất kỳ chi phí sử dụng map
. Nhược điểm là điều này rất hạn chế đối với python so với hầu hết các ngôn ngữ chức năng: bạn chỉ nhận được lợi ích này nếu bạn truy cập dữ liệu từ trái sang phải "theo thứ tự", bởi vì các biểu thức của trình tạo python chỉ có thể được đánh giá theo thứ tự x[0], x[1], x[2], ...
.
Tuy nhiên, hãy nói rằng chúng tôi có một chức năng được tạo sẵn f
mà chúng tôi muốn map
và chúng tôi bỏ qua sự lười biếng map
bằng cách ngay lập tức buộc phải đánh giá list(...)
. Chúng tôi nhận được một số kết quả rất thú vị:
% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=list(map(f,xs))'
10000 loops, best of 3: 165/124/135 usec per loop ^^^^^^^^^^^^^^^
for list(<map object>)
% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=[f(x) for x in xs]'
10000 loops, best of 3: 181/118/123 usec per loop ^^^^^^^^^^^^^^^^^^
for list(<generator>), probably optimized
% python3 -mtimeit -s 'xs=range(1000)' 'f=lambda x:x' 'z=list(f(x) for x in xs)'
1000 loops, best of 3: 215/150/150 usec per loop ^^^^^^^^^^^^^^^^^^^^^^
for list(<generator>)
Kết quả là ở dạng AAA / BBB / CCC, trong đó A được thực hiện trên máy trạm Intel vào khoảng năm 2010 với python 3.?., Và B và C đã được thực hiện với máy trạm AMD vào khoảng năm 2013 với python 3.2.1, với phần cứng cực kỳ khác nhau. Kết quả có vẻ là sự hiểu biết về bản đồ và danh sách có thể so sánh về hiệu suất, bị ảnh hưởng mạnh mẽ nhất bởi các yếu tố ngẫu nhiên khác. Điều duy nhất chúng ta có thể nói dường như là, thật kỳ lạ, trong khi chúng ta mong đợi việc hiểu danh sách [...]
sẽ hoạt động tốt hơn các biểu thức của trình tạo (...)
, map
C ALNG hiệu quả hơn các biểu thức của trình tạo (một lần nữa giả định rằng tất cả các giá trị được ước tính / sử dụng).
Điều quan trọng là phải nhận ra rằng các thử nghiệm này đảm nhận một chức năng rất đơn giản (chức năng nhận dạng); tuy nhiên điều này là tốt vì nếu chức năng phức tạp, thì chi phí hoạt động sẽ không đáng kể so với các yếu tố khác trong chương trình. (Có thể vẫn thú vị khi thử nghiệm với những thứ đơn giản khác như f=lambda x:x+x
)
Nếu bạn thành thạo đọc lắp ráp python, bạn có thể sử dụng dis
mô-đun để xem đó có thực sự là những gì đang diễn ra sau hậu trường không:
>>> listComp = compile('[f(x) for x in xs]', 'listComp', 'eval')
>>> dis.dis(listComp)
1 0 LOAD_CONST 0 (<code object <listcomp> at 0x2511a48, file "listComp", line 1>)
3 MAKE_FUNCTION 0
6 LOAD_NAME 0 (xs)
9 GET_ITER
10 CALL_FUNCTION 1
13 RETURN_VALUE
>>> listComp.co_consts
(<code object <listcomp> at 0x2511a48, file "listComp", line 1>,)
>>> dis.dis(listComp.co_consts[0])
1 0 BUILD_LIST 0
3 LOAD_FAST 0 (.0)
>> 6 FOR_ITER 18 (to 27)
9 STORE_FAST 1 (x)
12 LOAD_GLOBAL 0 (f)
15 LOAD_FAST 1 (x)
18 CALL_FUNCTION 1
21 LIST_APPEND 2
24 JUMP_ABSOLUTE 6
>> 27 RETURN_VALUE
>>> listComp2 = compile('list(f(x) for x in xs)', 'listComp2', 'eval')
>>> dis.dis(listComp2)
1 0 LOAD_NAME 0 (list)
3 LOAD_CONST 0 (<code object <genexpr> at 0x255bc68, file "listComp2", line 1>)
6 MAKE_FUNCTION 0
9 LOAD_NAME 1 (xs)
12 GET_ITER
13 CALL_FUNCTION 1
16 CALL_FUNCTION 1
19 RETURN_VALUE
>>> listComp2.co_consts
(<code object <genexpr> at 0x255bc68, file "listComp2", line 1>,)
>>> dis.dis(listComp2.co_consts[0])
1 0 LOAD_FAST 0 (.0)
>> 3 FOR_ITER 17 (to 23)
6 STORE_FAST 1 (x)
9 LOAD_GLOBAL 0 (f)
12 LOAD_FAST 1 (x)
15 CALL_FUNCTION 1
18 YIELD_VALUE
19 POP_TOP
20 JUMP_ABSOLUTE 3
>> 23 LOAD_CONST 0 (None)
26 RETURN_VALUE
>>> evalledMap = compile('list(map(f,xs))', 'evalledMap', 'eval')
>>> dis.dis(evalledMap)
1 0 LOAD_NAME 0 (list)
3 LOAD_NAME 1 (map)
6 LOAD_NAME 2 (f)
9 LOAD_NAME 3 (xs)
12 CALL_FUNCTION 2
15 CALL_FUNCTION 1
18 RETURN_VALUE
Có vẻ như tốt hơn để sử dụng [...]
cú pháp hơn list(...)
. Đáng buồn là map
lớp học hơi mờ để tháo gỡ, nhưng chúng ta có thể thực hiện nhờ kiểm tra tốc độ của chúng tôi.
map
và filter
cùng với thư viện tiêu chuẩn itertools
vốn đã có phong cách xấu. Trừ khi GvR thực sự nói rằng họ là một sai lầm khủng khiếp hoặc chỉ vì hiệu suất, kết luận tự nhiên duy nhất nếu đó là điều "Pythonicness" nói là hãy quên nó đi là ngu ngốc ;-)
map
/ filter
là một ý tưởng tuyệt vời cho Python 3 và chỉ có một cuộc nổi loạn của các Pythonistas khác giữ chúng trong không gian tên tích hợp (trong khi reduce
được chuyển sang functools
). Cá nhân tôi không đồng ý ( map
và filter
vẫn ổn với các chức năng được xác định trước, đặc biệt là tích hợp sẵn, chỉ không bao giờ sử dụng chúng nếu lambda
cần thiết), nhưng về cơ bản GvR đã gọi chúng không phải là Pythonic trong nhiều năm.
itertools
chưa? Phần tôi trích dẫn từ câu trả lời này là yêu cầu chính làm tôi bối rối. Tôi không biết liệu trong thế giới lý tưởng của anh ấy, map
và filter
sẽ chuyển đến itertools
(hoặc functools
) hoặc đi hoàn toàn, nhưng bất kể là trường hợp nào, một khi người ta nói rằng đó itertools
là unPythonic hoàn toàn, thì tôi không thực sự biết "Pythonic" là gì có nghĩa là nhưng tôi không nghĩ nó có thể giống với "những gì GvR khuyên mọi người nên sử dụng".
map
/ filter
, không phải itertools
. Lập trình chức năng là hoàn toàn Pythonic ( itertools
, functools
và operator
tất cả đều được thiết kế đặc biệt với chức năng lập trình trong tâm trí, và tôi sử dụng thành ngữ chức năng trong Python tất cả các thời gian), và itertools
cung cấp các tính năng đó sẽ là một nỗi đau để thực hiện chính mình, Nó đặc biệt map
và filter
là dư thừa với các biểu thức máy phát điện Điều đó khiến Guido ghét họ. itertools
luôn luôn tốt
map
và filter
thay vì hiểu danh sách.Một lý do khách quan tại sao bạn nên thích chúng hơn mặc dù chúng không phải là "Pythonic" là:
Chúng yêu cầu các hàm / lambdas làm đối số, giới thiệu một phạm vi mới .
Tôi đã bị cắn bởi điều này hơn một lần:
for x, y in somePoints:
# (several lines of code here)
squared = [x ** 2 for x in numbers]
# Oops, x was silently overwritten!
nhưng nếu thay vào đó tôi đã nói:
for x, y in somePoints:
# (several lines of code here)
squared = map(lambda x: x ** 2, numbers)
sau đó mọi thứ sẽ ổn
Bạn có thể nói rằng tôi đã ngớ ngẩn vì sử dụng cùng một tên biến trong cùng một phạm vi.
Tôi đã không. Mã ban đầu vẫn ổn - hai x
s không có cùng phạm vi.
Chỉ sau khi tôi chuyển khối bên trong sang một phần khác của mã thì vấn đề mới xuất hiện (đọc: vấn đề trong quá trình bảo trì, không phát triển) và tôi không mong đợi điều đó.
Có, nếu bạn không bao giờ phạm sai lầm này thì danh sách hiểu được thanh lịch hơn.
Nhưng từ kinh nghiệm cá nhân (và từ việc nhìn thấy người khác mắc lỗi tương tự) Tôi đã thấy điều đó xảy ra đủ lần mà tôi nghĩ rằng nó không đáng để bạn phải trải qua khi những con bọ này chui vào mã của bạn.
Sử dụng map
và filter
. Chúng ngăn ngừa các lỗi liên quan đến phạm vi khó chẩn đoán.
Đừng quên cân nhắc sử dụng imap
và ifilter
(trong itertools
) nếu chúng phù hợp với hoàn cảnh của bạn!
map
và / hoặc filter
. Nếu bất cứ điều gì, bản dịch trực tiếp và hợp lý nhất để tránh vấn đề của bạn không phải là map(lambda x: x ** 2, numbers)
mà là một biểu thức trình list(x ** 2 for x in numbers)
tạo không bị rò rỉ, như JeromeJ đã chỉ ra. Hãy nhìn Mehrdad, đừng coi thường cá nhân, tôi hoàn toàn không đồng ý với lý luận của bạn ở đây.
Trên thực tế, map
và việc hiểu danh sách hoạt động hoàn toàn khác nhau trong ngôn ngữ Python 3. Hãy xem chương trình Python 3 sau:
def square(x):
return x*x
squares = map(square, [1, 2, 3])
print(list(squares))
print(list(squares))
Bạn có thể mong đợi nó in dòng "[1, 4, 9]" hai lần, nhưng thay vào đó, nó sẽ in "[1, 4, 9]" theo sau là "[]". Lần đầu tiên bạn nhìn vào squares
nó dường như hành xử như một chuỗi ba yếu tố, nhưng lần thứ hai là một yếu tố trống rỗng.
Trong ngôn ngữ Python 2 map
trả về một danh sách cũ đơn giản, giống như việc hiểu danh sách thực hiện ở cả hai ngôn ngữ. Điểm mấu chốt là giá trị trả về của map
Python 3 (và imap
trong Python 2) không phải là một danh sách - đó là một trình vòng lặp!
Các phần tử được tiêu thụ khi bạn lặp qua một trình vòng lặp không giống như khi bạn lặp qua một danh sách. Đây là lý do tại sao squares
trông trống rỗng trong print(list(squares))
dòng cuối cùng .
Để tóm tắt:
map
để tạo ra một cấu trúc dữ liệu, không phải là một trình vòng lặp. Nhưng có lẽ các trình vòng lặp lười biếng dễ hơn các cấu trúc dữ liệu lười biếng. Thức ăn cho suy nghĩ. Cảm ơn @MnZrK
Tôi thấy việc hiểu danh sách nói chung thể hiện rõ hơn những gì tôi đang cố gắng thực hiện hơn map
- cả hai đều hoàn thành nó, nhưng trước đây giúp tiết kiệm tinh thần của việc cố gắng hiểu những gì có thể là một lambda
biểu thức phức tạp .
Ngoài ra còn có một cuộc phỏng vấn ở đâu đó (tôi không thể tìm thấy nó một cách trực tiếp) nơi Guido liệt kê lambda
các chức năng và chức năng là điều anh ấy hối hận nhất khi chấp nhận vào Python, vì vậy bạn có thể đưa ra lập luận rằng họ không phải là Pythonic bởi đức hạnh điều đó
const
từ khóa trong C ++ là một chiến thắng tuyệt vời dọc theo những dòng này.
lambda
đã được thực hiện rất khập khiễng (không có tuyên bố ..) rằng chúng khó sử dụng và hạn chế dù sao đi nữa.
Đây là một trường hợp có thể:
map(lambda op1,op2: op1*op2, list1, list2)
đấu với:
[op1*op2 for op1,op2 in zip(list1,list2)]
Tôi đoán rằng zip () là một chi phí không may và không cần thiết mà bạn cần phải thưởng thức nếu bạn khăng khăng sử dụng danh sách hiểu thay vì bản đồ. Sẽ là tuyệt vời nếu ai đó làm rõ điều này cho dù khẳng định hay phủ định.
zip
lười biếng bằng cách sử dụngitertools.izip
map(operator.mul, list1, list2)
. Chính ở những biểu hiện bên trái rất đơn giản này mà sự hiểu biết trở nên vụng về.
Nếu bạn có kế hoạch viết bất kỳ mã không đồng bộ, song song hoặc phân tán nào, có lẽ bạn sẽ thích map
hơn việc hiểu danh sách - vì hầu hết các gói không đồng bộ, song song hoặc phân tán đều cung cấp một map
hàm để quá tải python map
. Sau đó, bằng cách chuyển map
hàm thích hợp cho phần còn lại của mã của bạn, bạn có thể không phải sửa đổi mã nối tiếp ban đầu của mình để nó chạy song song (v.v.).
Vì vậy, vì Python 3, map()
là một trình vòng lặp, bạn cần ghi nhớ những gì bạn cần: một trình vòng lặp hoặc list
đối tượng.
Như @AlexMartelli đã đề cập , map()
chỉ nhanh hơn việc hiểu danh sách nếu bạn không sử dụng lambda
chức năng.
Tôi sẽ trình bày cho bạn một số so sánh thời gian.
Python 3.5.2 và CPython
Tôi đã sử dụng máy tính xách tay Jupiter và đặc biệt là %timeit
lệnh ma thuật tích hợp Các phép
đo : s == 1000 ms == 1000 * 1000 trận đấu = 1000 * 1000 * 1000 ns
Thiết lập:
x_list = [(i, i+1, i+2, i*2, i-9) for i in range(1000)]
i_list = list(range(1000))
Chức năng tích hợp:
%timeit map(sum, x_list) # creating iterator object
# Output: The slowest run took 9.91 times longer than the fastest.
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 277 ns per loop
%timeit list(map(sum, x_list)) # creating list with map
# Output: 1000 loops, best of 3: 214 µs per loop
%timeit [sum(x) for x in x_list] # creating list with list comprehension
# Output: 1000 loops, best of 3: 290 µs per loop
lambda
chức năng:
%timeit map(lambda i: i+1, i_list)
# Output: The slowest run took 8.64 times longer than the fastest.
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 325 ns per loop
%timeit list(map(lambda i: i+1, i_list))
# Output: 1000 loops, best of 3: 183 µs per loop
%timeit [i+1 for i in i_list]
# Output: 10000 loops, best of 3: 84.2 µs per loop
Ngoài ra còn có biểu thức như trình tạo, xem PEP-0289 . Vì vậy, tôi nghĩ rằng nó sẽ hữu ích để thêm nó để so sánh
%timeit (sum(i) for i in x_list)
# Output: The slowest run took 6.66 times longer than the fastest.
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 495 ns per loop
%timeit list((sum(x) for x in x_list))
# Output: 1000 loops, best of 3: 319 µs per loop
%timeit (i+1 for i in i_list)
# Output: The slowest run took 6.83 times longer than the fastest.
# This could mean that an intermediate result is being cached.
# 1000000 loops, best of 3: 506 ns per loop
%timeit list((i+1 for i in i_list))
# Output: 10000 loops, best of 3: 125 µs per loop
list
đối tượng:Sử dụng chức năng hiểu danh sách nếu đó là chức năng tùy chỉnh, sử dụng list(map())
nếu có chức năng dựng sẵn
list
đối tượng, bạn chỉ cần lặp lại:Luôn luôn sử dụng map()
!
Tôi đã chạy thử nghiệm nhanh so sánh ba phương thức để gọi phương thức của một đối tượng. Sự khác biệt về thời gian, trong trường hợp này là không đáng kể và là vấn đề của chức năng được đề cập (xem phản hồi của @Alex Martelli ). Ở đây, tôi đã xem xét các phương pháp sau:
# map_lambda
list(map(lambda x: x.add(), vals))
# map_operator
from operator import methodcaller
list(map(methodcaller("add"), vals))
# map_comprehension
[x.add() for x in vals]
Tôi đã xem các danh sách (được lưu trữ trong biến vals
) của cả số nguyên (Python int
) và số dấu phẩy động (Python float
) để tăng kích thước danh sách. Lớp giả sau đây DummyNum
được xem xét:
class DummyNum(object):
"""Dummy class"""
__slots__ = 'n',
def __init__(self, n):
self.n = n
def add(self):
self.n += 5
Cụ thể, add
phương pháp. Các __slots__
thuộc tính là một tối ưu hóa đơn giản bằng Python để xác định tổng số bộ nhớ cần thiết bởi lớp (thuộc tính), giảm kích thước bộ nhớ. Dưới đây là các ô kết quả.
Như đã nêu trước đây, kỹ thuật được sử dụng tạo ra sự khác biệt tối thiểu và bạn nên viết mã theo cách dễ đọc nhất đối với bạn hoặc trong trường hợp cụ thể. Trong trường hợp này, việc hiểu danh sách ( map_comprehension
kỹ thuật) là nhanh nhất cho cả hai loại bổ sung trong một đối tượng, đặc biệt là với các danh sách ngắn hơn.
Truy cập pastebin này để biết nguồn được sử dụng để tạo cốt truyện và dữ liệu.
map
chỉ nhanh hơn nếu hàm được gọi theo cùng một cách chính xác (nghĩa là [*map(f, vals)]
so với [f(x) for x in vals]
). Như vậy list(map(methodcaller("add"), vals))
là nhanh hơn [methodcaller("add")(x) for x in vals]
. map
có thể không nhanh hơn khi đối tác lặp sử dụng một phương thức gọi khác có thể tránh một số chi phí (ví dụ: x.add()
tránh methodcaller
chi phí biểu thức hoặc lambda). Đối với trường hợp thử nghiệm cụ thể này, [*map(DummyNum.add, vals)]
sẽ nhanh hơn (vì về cơ bản DummyNum.add(x)
và x.add()
có cùng hiệu suất).
list()
các cuộc gọi rõ ràng là hơi chậm hơn so với hiểu danh sách. Để so sánh công bằng bạn cần viết [*map(...)]
.
list()
các cuộc gọi tăng chi phí. Nên dành nhiều thời gian hơn để đọc qua các câu trả lời. Tôi sẽ chạy lại các thử nghiệm này để so sánh công bằng, tuy nhiên không đáng kể sự khác biệt có thể có.
Tôi nghĩ rằng cách Pythonic nhất là sử dụng một sự hiểu biết danh sách thay vì map
và filter
. Lý do là sự hiểu biết danh sách rõ ràng hơn map
và filter
.
In [1]: odd_cubes = [x ** 3 for x in range(10) if x % 2 == 1] # using a list comprehension
In [2]: odd_cubes_alt = list(map(lambda x: x ** 3, filter(lambda x: x % 2 == 1, range(10)))) # using map and filter
In [3]: odd_cubes == odd_cubes_alt
Out[3]: True
Như bạn thấy, một sự hiểu biết không yêu cầu lambda
biểu hiện thêm như map
nhu cầu. Hơn nữa, một sự hiểu biết cũng cho phép lọc dễ dàng, trong khi map
yêu cầu filter
cho phép lọc.
Tôi đã thử mã bởi @ alex-martelli nhưng thấy một số khác biệt
python -mtimeit -s "xs=range(123456)" "map(hex, xs)"
1000000 loops, best of 5: 218 nsec per loop
python -mtimeit -s "xs=range(123456)" "[hex(x) for x in xs]"
10 loops, best of 5: 19.4 msec per loop
bản đồ chiếm cùng một lượng thời gian ngay cả đối với các phạm vi rất lớn trong khi sử dụng khả năng hiểu danh sách mất rất nhiều thời gian như hiển nhiên từ mã của tôi. Vì vậy, ngoài việc được coi là "unpythonic", tôi không gặp phải bất kỳ vấn đề hiệu suất nào liên quan đến việc sử dụng bản đồ.
map
trả về một danh sách. Trong Python 3, map
được đánh giá một cách lười biếng, do đó, việc gọi đơn giản là map
không tính toán bất kỳ thành phần danh sách mới nào, do đó bạn có được thời gian ngắn như vậy.