Python'un neden listeler için “düzleştirici” bir işlevi yok?


39

Erlang ve Ruby'nin her ikisi de dizileri düzleştirmek için işlevlerle birlikte geliyor. Bir dil eklemek için bu kadar basit ve kullanışlı bir araç gibi görünüyor. Biri bunu yapabilir:

>>> mess = [[1, [2]], 3, [[[4, 5]], 6]]
>>> mess.flatten()
[1, 2, 3, 4, 5, 6]

Ya da:

>>> import itertools
>>> mess = [[1, [2]], 3, [[[4, 5]], 6]]
>>> list(itertools.flatten(mess))
[1, 2, 3, 4, 5, 6]

Bunun yerine, Python'da, dizileri sıfırdan düzleştirmek için bir işlev yazma zorluğundan geçmek gerekir. Bu benim için aptalca görünüyor, dizileri düzleştirmek yapmak için ortak bir şey. İki diziyi birleştirmek için özel bir işlev yazmak zorunda gibi.

Googled'i verimli bir şekilde yaptım, bu yüzden burada soruyorum; İçinde yüzbinlerce farklı batarya bulunan Python 3 gibi olgun bir dilin basit bir düzleştirme dizisi yöntemi sunmamasının belirli bir nedeni var mı? Böyle bir işlevi dahil etme fikri bir noktada tartışıldı ve reddedildi mi?


2
@detly: Son zamanlarda farklı kaynaklardan veri almak için birkaç sorgu kullanırken düzleştirmeyi kaçırdım. Her sorgu bir sözlük listesi döndürür, bu yüzden sonunda bir sözlük listesine dönüştürülecek bir sözlük listesi vardır. Bir döngü + kullandım extendama düzleştirmek çok daha zarif olurdu. Bununla birlikte, bu örüntü standart kütüphanede dümdüz yaslanmayı haklı çıkaracak kadar yaygınsa yararım.
Giorgio,

4
“Diyelim ki, kodunuza yanlışlıkla verilerinizin yapısını değiştiren bir hata eklediğinizi varsayalım. Dümdüz çalışacak, ancak tamamen yanlış sonuçlar doğuracak.”: Statik olarak yazılmış dilleri sevmemin bir nedeni bu. ;-)
Giorgio,


2
@BryanOakley Önceki yorumu da görün (çok düzeyli listeler için olmasa da, genel olarak yassılaşma yaygındır)
Izkata

3
Mathemaica'da yerleşiktir ve yoğun bir şekilde kullanıyorum.
Alexandersson

Yanıtlar:


34

flattenStandart kütüphaneye eklenecek bir işleve ilişkin öneriler zaman zaman python-dev ve python-fikirler posta listelerinde yer almaktadır. Python geliştiricileri genellikle aşağıdaki noktalara cevap verir:

  1. Bir seviye düzleştirme (yinelenebilir yinelemeyi tek yinelemeye çevirme) önemsiz bir tek satırlık ifadedir (x for y in z for x in y)ve her durumda zaten adın altındaki standart kitaplıkta bulunur itertools.chain.from_iterable.

  2. Genel amaçlı, çok seviyeli bir düzleştirme için kullanım örnekleri nelerdir? Bunlar, fonksiyonun standart kütüphaneye eklenmesi için gerçekten zorlayıcı mı?

  3. Genel amaçlı çok seviyeli bir yassılaştırma ne zaman düzleşeceğine ve ne zaman yalnız kalacağına nasıl karar verir? "Yinelenebilir arayüzü destekleyen her şeyi düzleştir" gibi bir kuralın işe yarayacağını ancak bunun sonsuz bir döngüye yol açacağını düşünebilirsiniz flatten('a').

Bakınız örneğin Raymond Hettinger :

Comp.lang.python adresinde ad naeameam ele alınmıştır. İnsanlar, kendileri düzleştirilmiş versiyonlarını yazmaktan, zevksiz çözümler içermeyen meşru kullanım davaları bulmaktan daha fazla zevk alıyor gibi görünüyorlar.

Genel amaçlı bir düzleştirici, neyin atomik olduğunu ve neyin daha alt bölümlere ayrılabileceğini anlatmak için bir yol gerektirir. Ayrıca, algoritmanın yaprakların yanı sıra düğümlerdeki verilere sahip ağaç benzeri veri yapılarına sahip girişleri kapsayacak şekilde nasıl genişletilmesi gerektiği de açık değildir (ön sipariş veren, posta gönderen, sıralama geçişi, vb.)


Sadece açık olmak gerekirse, bu bir seviyeli flattenfonksiyonun olarak tanımlanabileceği anlamına gelir lambda z: [x for y in z for x in y].
Christopher Martin,

1
“Genel amaçlı bir düzleştiricinin, neyin atomik olduğunu ve neyin daha alt bölümlere ayrılabileceğini anlatmak için bir yola ihtiyacı var.”: Bu, OOP kullanılarak çözülebilen bir sorun gibi görünüyor: her nesnenin bir flattenyöntemi olabilir . flattenNesne bir bileşikse, bu yöntemin uygulanması tekrarlı olarak alt bileşenini çağırmalıdır . Ne yazık ki, AFAIK Python'da her değer bir nesne değildir. Ruby'de olsa çalışması gerekir.
Giorgio

1
IMO için "giriş için" devam etmekten ziyade tek seviyeli bir yassılaştırma için düzleştirilmiş bir yardımcı zaten yeterli bir durumdur IMO. kolayca okunabilir
dtc

2
Giorgio Python bu tür yöntemlerden uzaklaşıyor. Protokoller tercih edilir ve bunların çoğu zaman çok fazla uygulama yapmanız gerekmediğinden OOP tasarımından çok daha yumuşak olduklarını biliyorum.
jpmc26

8

Böyle bir yöntemle geliyor, ama düzleşmiyor. Buna " zincir " denir . Listeye geri dönmek için list () işlevini kullanmanız gereken bir yineleyiciyi döndürür. Bir * kullanmak istemiyorsanız, ikinci "from_iterator" versiyonunu kullanabilirsiniz. Python 3'te aynı şekilde çalışır. Liste girişi liste listesi değilse başarısız olur.

[[1], [2, 3], [3, 4, 5]] #yes
[1, 2, [5, 6]] #no

Bir zamanlar compiler.ast modülünde bir düzleştirme yöntemi vardı, ancak bu 2.6'da kullanımdan kaldırıldı ve ardından 3.0'da kaldırıldı. Keyfi olarak iç içe geçmiş listeler için gerekli olan keyfi derinlik yinelemesi, Python'un muhafazakar maksimum yineleme derinliği ile iyi çalışmaz. Derleyicinin kaldırılmasının nedeni büyük oranda karışıklık olmasıydı . Derleyici ast dönüştü ama yassı kaldı.

Numpy dizileriyle ve bu kütüphanenin düzleşmesiyle keyfi derinlik elde edilebilir.


Dediğiniz gibi chain.from_iteratorişlev, yalnızca iki boyutlu listeleri düzleştirmek için kullanılabilir. Bir aslında kabul düzleştirmek fonksiyonu, herhangi bir iç içe listeleri ve iadeler tek boyutlu bir liste miktarlarda, hala (en azından bence) pek çok davada kitlesel yararlı olacağını
HUBRO

2
@Hubro: "birçok durumda" - altı ad verebilir misin?
Gareth Rees

1
@GarethRees: Burada birkaç örnek verdim: programmers.stackexchange.com/questions/254279/…
Hubro

Ayrıca, eğer bu diğer dil aslında bir listeyi açıklandığı şekilde düzleştirmek için böyle bir özellik sağlarsa, Python'a bu basit yeteneği eklemeyi destekleyen en zorlayıcı argümanlardan biri olduğunu savunmak isterim.
Bobort,

Bir yineleyici ya da jeneratör döndürüyor mu?
jpmc26

-1

... belki kendin yazmak zor değil çünkü

def flatten(l): return flatten(l[0]) + (flatten(l[1:]) if len(l) > 1 else []) if type(l) is list else [l]

... ve sonra istediğiniz her şeyi düzleştirin :)

>>> flatten([1,[2,3],4])
[1, 2, 3, 4]
>>> flatten([1, [2, 3], 4, [5, [6, {'name': 'some_name', 'age':30}, 7]], [8, 9, [10, [11, [12, [13, {'some', 'set'}, 14, [15, 'some_string'], 16], 17, 18], 19], 20], 21, 22, [23, 24], 25], 26, 27, 28, 29, 30])
[1, 2, 3, 4, 5, 6, {'age': 30, 'name': 'some_name'}, 7, 8, 9, 10, 11, 12, 13, set(['set', 'some']), 14, 15, 'some_string', 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
>>> 

8
asker bunun farkında: "Python'da, dizileri sıfırdan düzleştirmek için bir işlev yazma derdinden geçmek zorunda". Bu, “Bu bana aptalca geliyor, dizileri yassılaştırmak çok yaygın bir şey. Bu iki diziyi birleştirmek için özel bir işlev yazmak zorunda kalmak gibi” diye sorulan soruyu cevaplamaya bile çalışmıyor.
gnat

1
Konu dışı ... Ama süper havalı :-) !!
SeF,

Bu cevap OP'ye iyi bir geliştirici olmadığını söylemek gibi çünkü işlevin kendisini nasıl kodlayacağını bilmiyordu. Cevabınızın başlangıcını değiştirmenizi öneriyorum, çünkü bu konu dışı olsa bile soruya rastlayanlar için yararlı bir kod
Federico Bonelli
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.