ทำไมถึงเป็น string.join (list) แทนที่จะเป็น list.join (string)


1762

สิ่งนี้ทำให้ฉันสับสนอยู่เสมอ ดูเหมือนว่าจะดีกว่านี้:

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

กว่านี้:

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

มีเหตุผลที่เฉพาะเจาะจงเช่นนี้หรือไม่?


1
สำหรับหน่วยความจำและความเข้าใจง่าย ๆ ให้-ประกาศว่าคุณกำลังเข้าร่วมรายการและแปลงเป็นสตริงผลลัพธ์เป็นแบบเชิง
แคลคูลัส

11
@ JawSaw: นั่นทำให้สับสนมากขึ้น mem
einpoklum

34
ฉันคิดว่าคำตอบสั้น ๆ ก็คือมันเป็นเพราะระบบการพิมพ์ของ Python นั้นไม่แข็งแรงพอและมันง่ายกว่าที่จะใช้ฟังก์ชั่นนี้หนึ่งครั้งstrมากกว่าที่จะนำไปใช้กับมันในทุกประเภทที่ทำซ้ำได้
BallpointBen

3
ฉันคิดว่าความคิดดั้งเดิมคือเนื่องจาก join () ส่งคืนสตริงจึงต้องถูกเรียกจากบริบทสตริง การวางการเข้าร่วม () ในรายการไม่สมเหตุสมผลในรายการนั้นเป็นคอนเทนเนอร์ของวัตถุและไม่ควรมีฟังก์ชันแบบใช้ครั้งเดียวเฉพาะกับสตริงเท่านั้น
Joshua Burns

คำตอบ:


1248

เป็นเพราะทุก ๆ iterable สามารถเข้าร่วม (เช่น, รายการ, tuple, dict, set) แต่ผลลัพธ์และ "joiner" จะต้องเป็นสตริง

ตัวอย่างเช่น:

'_'.join(['welcome', 'to', 'stack', 'overflow'])
'_'.join(('welcome', 'to', 'stack', 'overflow'))
'welcome_to_stack_overflow'

การใช้อย่างอื่นที่ไม่ใช่สตริงจะทำให้เกิดข้อผิดพลาดต่อไปนี้:

TypeError: ลำดับรายการ 0: พบอินสแตนซ์ str ที่คาดไว้ int


57
ฉันไม่เห็นด้วยกับแนวคิดแม้ว่ามันจะเป็นความรู้สึกที่ดี list.join(string)ปรากฏวิธีการเชิงวัตถุstring.join(list)มากขึ้นในขณะที่ฟังฉันมากกว่าขั้นตอน
Eduardo Pignatelli

22
ดังนั้นทำไมมันไม่ใช้กับ iterable?
Steen Schütt

10
@TimeSheep: รายการจำนวนเต็มไม่มีการเข้าร่วมที่มีความหมายแม้ว่าจะสามารถทำซ้ำได้
เรียกซ้ำ

16
ฉันพยายามใช้print(str.join('-', my_list))และใช้งานได้ดีขึ้น
pimgeek

13
@TimeSheep เนื่องจาก iterable ไม่ใช่ประเภทคอนกรีต iterable เป็นส่วนต่อประสานชนิดใด ๆ ที่กำหนด__iter__วิธีการ การร้องขอ iterables ทั้งหมดเพื่อนำไปใช้ยังjoinทำให้ซับซ้อนอินเตอร์เฟสทั่วไป (ซึ่งครอบคลุมถึง iterables ผ่านไม่ใช่สายอักขระ) สำหรับกรณีการใช้งานที่เฉพาะเจาะจง การกำหนดjoinขั้นตอนด้าน Strins ปัญหานี้ในราคาของคำสั่ง "ใช้งานไม่ได้" ทางเลือกที่ดีกว่าอาจจะทำให้ฟังก์ชั่นนี้มีอาร์กิวเมนต์แรกว่าเป็น iterable และตัวที่สอง (เป็นทางเลือก) เป็นสตริงของ joiner - แต่เรือลำนั้นแล่นไปแล้ว
user4815162342

319

สิ่งนี้ถูกกล่าวถึงในเมธอด String ... ในที่สุดก็มีเธรดใน Python-Dev achive และได้รับการยอมรับจาก Guido กระทู้นี้เริ่มต้นในเดือนมิถุนายนปี 1999 และstr.joinรวมอยู่ใน Python 1.6 ซึ่งเปิดตัวในเดือนกันยายน 2000 (และรองรับ Unicode) Python 2.0 ( strวิธีการที่รองรับรวมถึงjoin) เปิดตัวในเดือนตุลาคม 2000

  • มีสี่ตัวเลือกที่เสนอในกระทู้นี้:
    • str.join(seq)
    • seq.join(str)
    • seq.reduce(str)
    • join เป็นฟังก์ชั่นในตัว
  • กุยโด้ต้องการที่จะสนับสนุนไม่เพียงlists, tuples, แต่ลำดับทั้งหมด / iterables
  • seq.reduce(str) เป็นเรื่องยากสำหรับผู้มาใหม่
  • seq.join(str) แนะนำการพึ่งพาที่ไม่คาดคิดจากลำดับถึง str / unicode
  • join()ในขณะที่ฟังก์ชั่นในตัวจะรองรับเฉพาะประเภทข้อมูลที่เฉพาะเจาะจง ดังนั้นการใช้ namespace ในตัวจึงไม่ดี หากjoin()รองรับประเภทข้อมูลจำนวนมากการสร้างการใช้งานที่ได้รับการปรับให้เหมาะสมนั้นอาจทำได้ยากหากนำไปใช้โดยใช้__add__วิธีการก็จะเป็น O (n²)
  • sepไม่ควรละสตริงตัวคั่น ( ) ชัดเจนดีกว่าโดยปริยาย

ไม่มีเหตุผลอื่นที่นำเสนอในชุดข้อความนี้

นี่คือความคิดเพิ่มเติม (ของฉันและเพื่อนของฉัน):

  • การสนับสนุน Unicode กำลังจะมา แต่ก็ยังไม่สิ้นสุด ในเวลานั้น UTF-8 มีแนวโน้มมากที่สุดที่จะแทนที่ UCS2 / 4 ในการคำนวณความยาวบัฟเฟอร์ทั้งหมดของสตริง UTF-8 จำเป็นต้องรู้กฎการเข้ารหัสอักขระ
  • ในเวลานั้น Python ได้ตัดสินใจเกี่ยวกับกฎอินเตอร์เฟสลำดับทั่วไปซึ่งผู้ใช้สามารถสร้างคลาสที่เหมือนลำดับ (iterable) แต่ Python ไม่รองรับการขยายประเภทบิวด์อินจนถึง 2.2 ในเวลานั้นมันเป็นเรื่องยากที่จะให้คลาส iterable ขั้นพื้นฐาน (ซึ่งถูกกล่าวถึงในความคิดเห็นอื่น)

การตัดสินใจของกุยจะถูกบันทึกไว้ในจดหมายประวัติศาสตร์ , การตัดสินใจเกี่ยวกับstr.join(seq):

ตลก แต่ดูเหมือนจะไม่ถูกต้อง! Barry ไปเลย ... -
Guido van Rossum


251

เพราะjoin()วิธีการอยู่ในชั้นเรียนสตริงแทนที่จะเป็นรายการระดับ?

ฉันเห็นด้วยว่ามันดูตลก

ดูhttp://www.faqs.org/docs/diveintopython/odbchelper_join.html :

บันทึกประวัติศาสตร์เมื่อฉันเรียน Python เป็นครั้งแรกฉันคาดว่าการเข้าร่วมจะเป็นวิธีการของรายการซึ่งจะใช้ตัวคั่นเป็นอาร์กิวเมนต์ ผู้คนจำนวนมากรู้สึกแบบเดียวกันและมีเรื่องราวเบื้องหลังวิธีการเข้าร่วม ก่อนหน้า Python 1.6 สตริงไม่มีวิธีการที่มีประโยชน์เหล่านี้ทั้งหมด มีโมดูลสตริงแยกต่างหากซึ่งมีฟังก์ชั่นสตริงทั้งหมด แต่ละฟังก์ชั่นใช้สตริงเป็นอาร์กิวเมนต์แรก ฟังก์ชั่นนี้ถือว่ามีความสำคัญมากพอที่จะใส่ลงไปในสายของตัวเองซึ่งทำให้ฟังก์ชั่นเช่นล่าง, บน, และแยก แต่โปรแกรมเมอร์ Python ของฮาร์ดคอร์หลายคนคัดค้านวิธีการเข้าร่วมใหม่โดยยืนยันว่ามันควรจะเป็นวิธีการของรายการแทนหรือว่ามันไม่ควรย้ายเลย แต่ก็ยังคงเป็นส่วนหนึ่งของโมดูลสตริงเก่า (ซึ่งยังมีจำนวนมาก ของสิ่งที่มีประโยชน์ในนั้น)

--- ทำเครื่องหมายผู้แสวงบุญดำดิ่งสู่งูหลาม


12
งูหลาม 3 stringห้องสมุดได้ลบออกทั้งหมดซ้ำซ้อนวิธีการเพื่อให้คุณไม่สามารถใช้str string.join()โดยส่วนตัวฉันไม่เคยคิดว่า 'ตลก' มันเข้าท่าดีเพราะคุณสามารถเข้าร่วมได้มากกว่ารายการ แต่ผู้เข้าร่วมมักจะเป็นสตริง!
Martijn Pieters

67

ฉันยอมรับว่ามันใช้งานง่ายในตอนแรก แต่ก็มีเหตุผลที่ดี การเข้าร่วมไม่สามารถเป็นรายการได้เนื่องจาก:

  • มันจะต้องใช้การได้สำหรับ iterables ที่แตกต่างกันเช่นกัน (tuples, generators, ฯลฯ )
  • มันจะต้องมีพฤติกรรมที่แตกต่างกันระหว่างสตริงประเภทต่างๆ

จริงๆแล้วมีวิธีการเข้าร่วมสองวิธี (Python 3.0):

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

หากเข้าร่วมเป็นวิธีการของรายการก็จะต้องตรวจสอบข้อโต้แย้งของตนเพื่อตัดสินใจว่าหนึ่งในนั้นโทร และคุณไม่สามารถเข้าร่วมไบต์และ str ด้วยกันดังนั้นวิธีที่พวกเขามีตอนนี้ทำให้รู้สึก


45

ทำไมมันจึงเป็นstring.join(list)แทนlist.join(string)?

นี่เป็นเพราะjoinเป็นวิธี "สตริง"! มันสร้างสตริงจาก iterable ใด ๆ หากเราติดวิธีการกับรายการสิ่งที่เกี่ยวกับเมื่อเรามี iterables ที่ไม่ใช่รายการ?

เกิดอะไรขึ้นถ้าคุณมี tuple ของสตริง? หากนี่เป็นlistวิธีการหนึ่งคุณจะต้องโยนตัววนซ้ำทุกตัวของสตริงlistก่อนที่คุณจะสามารถรวมองค์ประกอบต่างๆเข้าด้วยกันเป็นสตริงเดียว! ตัวอย่างเช่น:

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

มาม้วนวิธีการเข้าร่วมรายการของเราเอง:

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

และในการใช้งานโปรดทราบว่าเราต้องสร้างรายการจากแต่ละ iterable ก่อนเพื่อเข้าร่วมสตริงใน iterable นั้นเปลืองทั้งหน่วยความจำและพลังการประมวลผล:

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

ดังนั้นเราจะเห็นว่าเราต้องเพิ่มขั้นตอนพิเศษในการใช้วิธีการรายการของเราแทนที่จะใช้วิธีการสตริง builtin:

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

สมรรถนะ Caveat สำหรับเครื่องกำเนิดไฟฟ้า

Python ใช้อัลกอริธึมในการสร้างสตริงสุดท้ายด้วยstr.joinจริง ๆ แล้วจะต้องผ่านการทำซ้ำสองครั้งดังนั้นถ้าคุณให้นิพจน์ตัวสร้างมันจะต้องทำให้เป็นจริงในรายการก่อนที่จะสามารถสร้างสตริงสุดท้าย

ดังนั้นในขณะที่เดินผ่านเครื่องกำเนิดไฟฟ้ามักจะดีกว่ารายการความเข้าใจstr.joinเป็นข้อยกเว้น:

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

อย่างไรก็ตามการstr.joinดำเนินการยังคงเป็นความหมายของการดำเนินการ "สตริง" ดังนั้นจึงยังคงมีเหตุผลที่จะมีมันบนstrวัตถุกว่าใน iterables เบ็ดเตล็ด


24

คิดว่ามันเป็นการแยกมุมฉากตามธรรมชาติ

ผมเข้าใจว่าทำไมมันเป็นอะไรที่ใช้บังคับกับ iterable และไม่สามารถนำไปใช้ได้ง่ายเพียงแค่ในรายการ

เพื่อความสะดวกในการอ่านฉันต้องการเห็นมันเป็นภาษา แต่ฉันไม่คิดว่ามันจะเป็นไปได้จริง ๆ - ถ้ามันเป็นอินเทอร์เฟซก็สามารถเพิ่มอินเทอร์เฟซได้ แต่มันเป็นแค่การประชุม เพิ่มไปยังชุดของสิ่งที่ iterable


13

สาเหตุหลักมาจากผลลัพธ์ของ a someString.join()เป็นสตริง

ลำดับ (รายการหรือ tuple หรืออะไรก็ตาม) ไม่ปรากฏในผลลัพธ์เพียงแค่สตริง เนื่องจากผลลัพธ์เป็นสตริงมันจึงสมเหตุสมผลเป็นเมธอดของสตริง


10

- ใน "-". เข้าร่วม (my_list) ประกาศว่าคุณกำลังแปลงเป็นสตริงจากการเข้าร่วมองค์ประกอบรายการมันเป็นผลเชิง (สำหรับหน่วยความจำง่ายและเข้าใจง่าย)

ฉันสร้าง cheatsheet ที่ละเอียดอ่อนของ methods_of_string สำหรับการอ้างอิงของคุณ

string_methonds_44 = {
    'convert': ['join','split', 'rsplit','splitlines', 'partition', 'rpartition'],
    'edit': ['replace', 'lstrip', 'rstrip', 'strip'],
    'search': ['endswith', 'startswith', 'count', 'index', 'find','rindex', 'rfind',],
    'condition': ['isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isnumeric','isidentifier',
                  'islower','istitle', 'isupper','isprintable', 'isspace', ],
    'text': ['lower', 'upper', 'capitalize', 'title', 'swapcase',
             'center', 'ljust', 'rjust', 'zfill', 'expandtabs','casefold'],
    'encode': ['translate', 'maketrans', 'encode'],
    'format': ['format', 'format_map']}

3

ทั้งสองไม่ดี

string.join (xs, delimit) หมายความว่าโมดูลสตริงตระหนักถึงการมีอยู่ของรายการซึ่งไม่มีความรู้ทางธุรกิจเนื่องจากโมดูลสตริงทำงานได้กับสตริงเท่านั้น

list.join (delimit) เป็นบิตที่ดีกว่าเพราะเราคุ้นเคยกับสตริงที่เป็นประเภทพื้นฐาน (และเป็นภาษาพูดพวกเขา) อย่างไรก็ตามนี่หมายความว่าการเข้าร่วมจะต้องทำการส่งแบบไดนามิกเพราะในบริบทของa.split("\n")ผู้รวบรวมไพ ธ อนอาจไม่ทราบว่าคืออะไรและจะต้องค้นหามัน (คล้ายกับการค้นหา vtable) ซึ่งมีราคาแพงถ้าคุณทำมันมาก ครั้ง

ถ้า python runtime compiler รู้ว่า list เป็นโมดูลในตัวก็สามารถข้ามการค้นหาแบบไดนามิกและเข้ารหัสความตั้งใจใน bytecode โดยตรงในขณะที่มันจำเป็นต้องแก้ไข "เข้าร่วม" ของ "a" แบบไดนามิกซึ่งอาจมีหลายเลเยอร์ ของการสืบทอดต่อการโทร (ตั้งแต่ระหว่างการโทรความหมายของการเข้าร่วมอาจมีการเปลี่ยนแปลงเนื่องจากไพ ธ อนเป็นภาษาแบบไดนามิก)

น่าเศร้านี่เป็นข้อบกพร่องขั้นสุดท้ายของสิ่งที่เป็นนามธรรม ไม่ว่าคุณจะเลือกสิ่งที่เป็นนามธรรมสิ่งที่เป็นนามธรรมของคุณจะทำให้รู้สึกในบริบทของปัญหาที่คุณกำลังพยายามที่จะแก้ปัญหาและดังนั้นคุณจึงไม่สามารถมีสิ่งที่เป็นนามธรรมที่สอดคล้องกันซึ่งไม่สอดคล้องกับอุดมการณ์พื้นฐานเมื่อคุณเริ่มติดกาว ร่วมกันโดยไม่รวมไว้ในมุมมองที่สอดคล้องกับอุดมการณ์ของคุณ รู้วิธีการของงูใหญ่มีความยืดหยุ่นมากขึ้นเพราะมันถูกกว่ามันขึ้นอยู่กับคุณที่จะจ่ายมากขึ้นเพื่อให้มันดู "ดีกว่า" โดยการทำเสื้อคลุมของคุณเองหรือ preprocessor ของคุณเอง


0

ตัวแปรmy_listและ"-"เป็นทั้งวัตถุ โดยเฉพาะพวกมันคืออินสแตนซ์ของคลาสlistและstrตามลำดับ ฟังก์ชั่นเป็นของชั้นเรียนjoin strดังนั้นจึงใช้ไวยากรณ์"-".join(my_list)เนื่องจากวัตถุ"-"กำลังmy_listเป็นอินพุต

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.