Pertanyaan Python bergabung: mengapa itu string.join (daftar) bukannya list.join (string)?


Ini selalu membingungkan saya. Sepertinya ini akan menjadi lebih baik:

my_list = ["Hello", "world"]
print my_list.join("-")
# Produce: "Hello-world"

Dari ini:

my_list = ["Hello", "world"]
print "-".join(my_list)
# Produce: "Hello-world"

Apakah ada alasan khusus seperti ini?


1378
2018-01-29 22:45


asal


Jawaban:


Itu karena setiap iterable dapat digabungkan, bukan hanya daftar, tetapi hasilnya dan "joiner" selalu string.

MISALNYA:

import urllib2
print '\n############\n'.join(
    urllib2.urlopen('http://data.stackexchange.com/users/7095'))

998
2018-01-29 22:51



Karena join() metode dalam kelas string, bukan kelas daftar?

Saya setuju itu terlihat lucu.

Lihat http://www.faqs.org/docs/diveintopython/odbchelper_join.html:

Catatan sejarah. Ketika saya pertama kali belajar   Python, saya berharap bergabung menjadi sebuah metode   daftar, yang akan mengambil   pembatas sebagai argumen. Banyak   orang merasakan hal yang sama, dan ada   cerita di belakang metode gabung. Sebelumnya   ke Python 1.6, string tidak memiliki semuanya   metode yang bermanfaat ini. Ada a   modul string terpisah yang berisi   semua fungsi string; setiap   fungsi mengambil string sebagai yang pertama   argumen. Fungsi-fungsi itu dianggap   cukup penting untuk dimasukkan ke dalam   string sendiri, yang masuk akal   untuk fungsi seperti bawah, atas, dan   membagi. Tetapi banyak Python hard-core   programmer keberatan dengan bergabung baru   metode, dengan alasan bahwa itu harus menjadi   metode daftar, atau itu   seharusnya tidak bergerak sama sekali tetapi tetap tinggal   bagian dari modul string lama (yang   masih memiliki banyak hal yang berguna di dalamnya).   Saya menggunakan metode gabung baru secara eksklusif,   tetapi Anda akan melihat kode yang ditulis baik   cara, dan jika itu benar-benar mengganggu Anda, Anda   dapat menggunakan fungsi string.join yang lama   sebagai gantinya.

--- Mark Pilgrim, Menyelam ke Python


227
2018-01-29 22:48



Ini telah dibahas dalam Metode string ... akhirnya thread di Python-Dev achive, dan diterima oleh Guido. Benang ini dimulai pada Juni 1999, dan str.join termasuk dalam Python 1.6 yang dirilis pada Sep 2000 (dan didukung Unicode). Python 2.0 (didukung str metode termasuk join) dirilis pada Oktober 2000.

  • Ada empat opsi yang diusulkan di utas ini:
    • str.join(seq)
    • seq.join(str)
    • seq.reduce(str)
    • join sebagai fungsi built-in
  • Guido ingin mendukung bukan hanya lists, tuples, tetapi semua urutan / iterables.
  • seq.reduce(str) sulit untuk pendatang baru.
  • seq.join(str) memperkenalkan ketergantungan yang tak terduga dari rangkaian ke str / unicode.
  • join() sebagai fungsi built-in hanya akan mendukung tipe data tertentu. Jadi menggunakan namespace yang dibangun tidak bagus. Jika join() mendukung banyak tipe data, membuat implementasi yang dioptimalkan akan sulit, jika diimplementasikan menggunakan __add__ metode maka itu O (n²).
  • String pemisah (sep) tidak boleh dihilangkan. Eksplisit lebih baik daripada implisit.

Tidak ada alasan lain yang ditawarkan di utas ini.

Berikut beberapa pemikiran tambahan (milik saya, dan teman saya):

  • Dukungan Unicode akan datang, tetapi itu belum final. Pada waktu itu UTF-8 kemungkinan besar akan menggantikan UCS2 / 4. Untuk menghitung total panjang buffer string UTF-8 perlu diketahui aturan character coding.
  • Pada saat itu, Python telah memutuskan pada aturan antarmuka urutan umum di mana pengguna dapat membuat kelas sekuens (iterable). Tetapi Python tidak mendukung memperluas tipe bawaan hingga 2.2. Pada waktu itu sulit untuk menyediakan kelas dasar yang dapat dipatahkan (yang disebutkan dalam komentar lain).

Keputusan Guido dicatat dalam surat sejarah, memutuskan str.join(seq):

Lucu, tetapi tampaknya benar! Barry, lakukanlah ...
  --Guido van Rossum


211
2017-09-30 15:21



Saya setuju bahwa itu berlawanan dengan intuisi pada awalnya, tapi ada alasan bagus. Bergabung tidak bisa menjadi metode daftar karena:

  • itu harus bekerja untuk iterables yang berbeda juga (tupel, generator, dll.)
  • itu harus memiliki perilaku berbeda antara berbagai jenis string.

Sebenarnya ada dua metode bergabung (Python 3.0):

>>> b"".join
<built-in method join of bytes object at 0x00A46800>
>>> "".join
<built-in method join of str object at 0x00A28D40>

Jika bergabung adalah metode daftar, maka harus memeriksa argumennya untuk memutuskan yang mana yang akan dihubungi. Dan Anda tidak dapat bergabung dengan byte dan str bersama, jadi cara mereka sekarang masuk akal.


58
2018-01-29 23:03



kenapa sih string.join(list) dari pada list.join(string)?

Hal ini karena join adalah metode "string"! Ini menciptakan string dari setiap iterable. Jika kita menempelkan metode pada daftar, bagaimana jika kita memiliki iterables yang bukan daftar?

Bagaimana jika Anda memiliki tupel string? Jika ini adalah list metode, Anda harus membuang setiap iterator string seperti itu list sebelum Anda dapat menggabungkan elemen menjadi satu string! Sebagai contoh:

some_strings = ('foo', 'bar', 'baz')

Mari kita gulir metode daftar bergabung sendiri:

class OurList(list): 
    def join(self, s):
        return s.join(self)

Dan untuk menggunakannya, perhatikan bahwa pertama-tama kita harus membuat daftar dari setiap iterable untuk bergabung dengan string dalam iterable, membuang memori dan kekuatan pemrosesan:

>>> l = OurList(some_strings) # step 1, create our list
>>> l.join(', ') # step 2, use our list join method!
'foo, bar, baz'

Jadi kita melihat kita harus menambahkan langkah tambahan untuk menggunakan metode daftar kami, daripada hanya menggunakan metode string bawaan:

>>> ' | '.join(some_strings) # a single step!
'foo | bar | baz'

Kinerja Caveat untuk Generator

Algoritme Python digunakan untuk membuat string terakhir dengan str.join sebenarnya harus melewati iterable dua kali, jadi jika Anda memberikannya ekspresi generator, itu harus terwujud menjadi daftar terlebih dahulu sebelum dapat membuat string terakhir.

Jadi, ketika mengedarkan generator biasanya lebih baik daripada daftar pemahaman, str.join adalah pengecualian:

>>> import timeit
>>> min(timeit.repeat(lambda: ''.join(str(i) for i in range(10) if i)))
3.839168446022086
>>> min(timeit.repeat(lambda: ''.join([str(i) for i in range(10) if i])))
3.339879313018173

Namun demikian, str.join Operasi masih semantik operasi "string", jadi masih masuk akal untuk memilikinya di str objek dari pada bermacam-macam iterables.


36
2018-04-14 00:45



Anggap saja sebagai operasi ortogonal alami untuk dibagi.

Saya mengerti mengapa ini berlaku untuk apa pun yang dapat diteruskan dan tidak dapat dengan mudah diterapkan hanya dalam daftar.

Untuk keterbacaan, saya ingin melihatnya dalam bahasa tetapi saya tidak berpikir bahwa sebenarnya layak - jika iterability adalah antarmuka maka dapat ditambahkan ke antarmuka tetapi itu hanya sebuah konvensi dan jadi tidak ada cara sentral untuk menambahkannya ke himpunan hal-hal yang bersifat iterable.


22
2018-01-30 02:43



Terutama karena hasil a someString.join() adalah sebuah string.

Urutan (daftar atau tupel atau apa pun) tidak muncul dalam hasil, hanya string. Karena hasilnya adalah string, itu masuk akal sebagai metode string.


11
2018-01-29 22:51



Keduanya tidak baik.

string.join (xs, delimit) berarti bahwa modul string mengetahui keberadaan daftar, yang tidak diketahui oleh bisnisnya, karena modul string hanya bekerja dengan string.

list.join (delimit) sedikit lebih bagus karena kita begitu terbiasa dengan string yang merupakan tipe fundamental (dan secara lingual, mereka). Namun ini berarti bahwa bergabung harus dikirim secara dinamis karena dalam konteks arbitrer a.split("\n") compiler python mungkin tidak tahu apa itu, dan perlu mencari (analog ke vtable lookup), yang mahal jika Anda melakukannya berkali-kali.

jika compiler python runtime mengetahui bahwa daftar adalah modul built in, ia dapat melewati lookup dinamis dan menyandikan maksudnya ke bytecode secara langsung, sedangkan jika tidak perlu secara dinamis menyelesaikan "join" dari "a", yang mungkin naik beberapa lapisan pewarisan per panggilan (karena di antara panggilan, makna bergabung mungkin telah berubah, karena python adalah bahasa dinamis).

sayangnya, ini adalah cacat abstraksi tertinggi; tidak peduli apa pun abstraksi yang Anda pilih, abstraksi Anda hanya akan masuk akal dalam konteks masalah yang Anda coba pecahkan, dan karena itu Anda tidak akan pernah memiliki abstraksi yang konsisten yang tidak menjadi tidak konsisten dengan ideologi yang mendasari saat Anda mulai menempelkannya. bersama-sama tanpa membungkus mereka dalam pandangan yang konsisten dengan ideologi Anda. Mengetahui hal ini, pendekatan python lebih fleksibel karena lebih murah, terserah Anda untuk membayar lebih untuk membuatnya terlihat "lebih baik", baik dengan membuat wrapper Anda sendiri, atau preprocessor Anda sendiri.


1
2018-05-07 19:32