Python 3'te filtre, harita ve azaltma nasıl kullanılır


321

filter,, mapve reducePython 2'de mükemmel çalışır. İşte bir örnek:

>>> 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

Ancak Python 3'te aşağıdaki çıktıları alıyorum:

>>> 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

Birisi bana bunun neden olduğunu açıklayabilirse sevinirim.

Daha fazla netlik için kodun ekran görüntüsü:

Python 2 ve 3'ün yan yana IDLE oturumları


1
Kısacası, liste tek veri tipi değildir. Bir liste istiyorsanız, bir liste istediğinizi söyleyin. Ancak çoğu durumda, yine de başka bir şey istersiniz.
Veky

Yanıtlar:


346

Değişiklikleri Python 3.0'daki Yenilikler bölümünde okuyabilirsiniz . Çok şey değiştiği için 2.x'ten 3.x'e geçtiğinizde iyice okumalısınız.

Burada yanıtın tamamı dokümantasyondan alıntılar.

Liste Yerine Görüntüleme ve Yineleyiciler

Bazı iyi bilinen API'ler artık liste döndürmüyor:

  • [...]
  • map()ve filter()dönüş yineleyicileri. Gerçekten bir listeye ihtiyacınız varsa, hızlı bir düzeltme örnektir list(map(...)), ancak daha iyi bir düzeltme genellikle bir liste kavraması (özellikle orijinal kod lambda kullandığında) kullanmak veya kodu bir listeye ihtiyaç duymadan yeniden yazmaktır. Özellikle map()yan etkiler, fonksiyonun yan etkileri için çağrılır; doğru dönüşüm düzenli bir fordöngü kullanmaktır (bir liste oluşturmak sadece israf olacaktır).
  • [...]

yerleşikleri

  • [...]
  • Kaldırıldı reduce(). functools.reduce()Gerçekten ihtiyacınız varsa kullanın ; bununla birlikte, açık bir fordöngünün yüzde 99'u daha okunabilir.
  • [...]

21
list(map(...) Her yere ekleyerek .. dünyanın nasıl okunabilirliğe yardımcı olduğu .. pythonişlevsel birleştiricilerin aşamalı / akış uygulamasına dayanamıyor gibi görünüyor. Diğer diller Bir düzine işlemi arka arkaya bir koleksiyona zincirleyebilirim ve okunabilir. Buraya? ne istiyorsun - bir düzine yol iç içe in??
javadba

11
Zorunlu bir bağlamda çalışıyorsanız, for-loop muhtemelen daha okunabilir seçenektir. Ancak işlevsel bir bağlamı tercih etmek için iyi nedenler vardır - ve bundan usule geri dönmek oldukça çirkin olabilir.
MatrixManAtYrService

2
@javadba Bir "gerçek zamanlı aktarım uygulamasında" listçağrıyı eklemeniz gerektiğinden emin misiniz ? "Akış" anlamının "hiçbir liste oluşturulmadığını, bir sonrakine geçmeden önce girdinin her bir elemanını tamamen işlediğini" düşündüm.
18'de

@MatrixManAtYrService Python 2 davranışının ihtiyacınız olan şey olduğundan eminseniz, her zaman yeniden tanımlayabilirsiniz map.
18'de

6
Okunabilirlik argümanının böyle bir değişikliğe nasıl yol açtığını hala anlayamıyorum. Performans nedenlerinden ötürü anlayabilseydim ...
Minato

86

Yineleyicilerin döndürülmesi ve azaltılması için işlevsellik mapve filterkasıtlı olarak değiştirildi ve yerleşik olarak yerleştirildi ve yerleştirildi functools.reduce.

Böylece, filterve için map, list()daha önce yaptığınız gibi sonuçları görmek için onları sarın .

>>> 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
>>>

Şimdi öneri, harita ve filtre kullanımınızı jeneratör ifadeleri veya liste kavrayışları ile değiştirmenizdir. Misal:

>>> 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]
>>>

Döngüler için okumak için zamanın yüzde 99'unun azaltmaktan daha kolay olduğunu söylüyorlar, ama sadece sadık kalacağım functools.reduce.

Düzenleme : Yüzde 99 rakam doğrudan Guido van Rossum tarafından yazılan What's New In Python 3.0 sayfasından alınır.


5
Liste kavrayışlarında fazladan işlevler oluşturmanıza gerek yoktur. Sadece kullanın[i*i*i for i in range(1,11)]
Xiao

2
Kesinlikle haklısın. Filtre / harita örneklerine benzer görünmesini sağlamak için işlevi liste anlama örneklerinde tuttum.
Joshua D. Boyd

5
i ** 3 ayrıca i * i * i'ye eşdeğerdir
Breezer

5
@Breezer aslında i**3arayacak i.__pow__(3)ve i*i*i i.__mul__(i).__mul__(i)(veya bunun gibi bir şey). Ints ile önemli değil, ancak numpy numaraları / özel sınıflarla bile farklı sonuçlar üretebilir.
syntonym

1
"Guido'nun X kararı verdiğini" duyduğumuzda, bu ağrının muhtemel bir sonuç olduğunu fark ettim . Bu harika bir örnek: Python'da zaten ayrıntılı list(list(list(.. )))olanı yapmak .
javadba

12

Diğer yanıtlara bir ek olarak, bu, bu işlevlerin adlarını bir liste döndüren ve reducegenel ad alanında tanıtılan işlevlerle yeniden eşleştirecek bir bağlam yöneticisi için iyi bir kullanım örneği gibi görünür .

Hızlı bir uygulama şöyle görünebilir:

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

Şuna benzer bir kullanımla:

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

Hangi baskılar:

190
[1, 2]

Sadece 2 sent :-)


1
pythonBir dil olarak bir karmaşa - ama v mükemmel kütüphanelere iyi sahiptir: numpy, pandas, statsmodelsve ana dilin ağrıyı azaltmak için buraya göstermek gibi .. ben kolaylık kütüphaneler buliding olmuştu arkadaşlar - ama enerji kayıp ve gayret var uzak bir data.frame/ datatable, veya xarray. Ama denemek için kudos ..
javadba

7

Yana reduceyöntem Python3 gelen işlevi inşa çıkarıldığını, ithal unutmayın functoolskodunuzda. Lütfen aşağıdaki kod snippet'ine bakın.

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

2

Filtre, harita ve azaltma fonksiyonlarına örnekler:

sayılar = [10,11,12,22,34,43,54,34,67,87,88,98,99,87,44,66]

// Filtre

oddNumbers = list (filtre (lambda x: x% 2! = 0, sayılar))

yazdırmak (oddNumbers)

//Harita

multiplyOf2 = liste (harita (lambda x: x * 2, sayılar))

yazdırmak (multiplyOf2)

Azaltmak //

Azaltma işlevi, yaygın olarak kullanılmadığından, Python 3'teki yerleşik işlevlerden kaldırılmıştır. Functools modülünde hala kullanılabilir, böylece şunları yapabilirsiniz:

functools ithalat azaltmak

sumOfNumbers = azalt (lambda x, y: x + y, sayılar)

yazdırmak (sumOfNumbers)


0

Harita, filtre ve azaltmanın avantajlarından biri, karmaşık bir şey yapmak için bunları "zincirlediğinizde" ne kadar okunaklı olduklarıdır. Ancak, yerleşik sözdizimi okunaklı değildir ve tümü "geri" dir. Bu yüzden PyFunctionalpaketi kullanmanızı öneririm ( https://pypi.org/project/PyFunctional/ ). İşte ikisinin bir karşılaştırması:

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

İşlevsel sürüm

Çok okunaklı bir sözdizimi. Söyleyebilirsin:

"Bir dizi uçuş varış yerim var. Bunlardan şehir dict değerlerinde ise dict anahtarını almak istiyorum. Son olarak, süreçte oluşturduğum boş listeleri filtreleyin."

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 != []) \

Varsayılan Python sürümü

Geriye dönük. Şunu söylemelisin:

"Tamam, bir liste var. Boş listeleri filtrelemek istiyorum. Neden? Çünkü şehir dict değerlerinde ise dict anahtarını ilk aldım. Oh, bunu yaptığım liste flight_destiments_dict. "

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())
               )
    )
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.