วัตถุประสงค์ของการ "คืนตัวเอง" จากวิธีการเรียน?


46

ฉันเจอสิ่งนี้ในโครงการโอเพ่นซอร์ส เมธอดที่แก้ไขแอ็ตทริบิวต์อินสแตนซ์ส่งคืนการอ้างอิงไปยังอินสแตนซ์ จุดประสงค์ของการสร้างนี้คืออะไร?

class Foo(object):

  def __init__(self):
    self.myattr = 0

  def bar(self):
    self.myattr += 1
    return self

2
และนี่คือวิธีการเขียน jQuery ทั้งหมด เกือบทุกฟังก์ชั่นส่งคืนวัตถุ jQuery
CaffGeek

11
ทำไมคุณไม่เชื่อรหัสที่เขียนแบบนี้?
sepp2k

คำตอบ:


65

มันเป็นการยอมให้ผูกมัด

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

var Car = function () {
    return {
        gas : 0,
        miles : 0,
        drive : function (d) {
            this.miles += d;
            this.gas -= d;
            return this;
        },
        fill : function (g) {
            this.gas += g;
            return this;
        },
    };
}

ตอนนี้คุณสามารถพูดได้:

var c = Car();
c.fill(100).drive(50);
c.miles => 50;
c.gas => 50;

20
สำหรับบันทึกชนิดของการผูกมัดนี้จะเห็นทั่วไปในรหัสที่มีอินเตอร์เฟซได้อย่างคล่องแคล่ว
Lie Ryan

10

ในฐานะที่เป็น @Lie Ryan และ @Frank Shearar พูดถึงสิ่งนี้เรียกว่า "ส่วนต่อประสานที่คล่องแคล่ว" แต่รูปแบบดังกล่าวนั้นใช้เวลานานมาก

ส่วนที่ถกเถียงกันของรูปแบบนั้นก็คือใน OO คุณมีสถานะที่ไม่แน่นอนดังนั้นวิธีที่เป็นโมฆะจึงมีค่าตอบแทนโดยนัยเท่ากับthis- นั่นคือวัตถุที่มีสถานะที่อัปเดตนั้นเป็นค่าตอบแทน

ดังนั้นในภาษา OO ที่มีสถานะไม่แน่นอนทั้งสองนี้จะเทียบเท่ากันมากหรือน้อย:

a.doA()
a.doB()
a.doC()

...ตรงข้ามกับ

a.doA().doB().doC()

ดังนั้นฉันเคยได้ยินคนในอดีตต่อต้านอินเทอร์เฟซคล่องแคล่วเพราะพวกเขาชอบรูปแบบแรก ชื่ออื่นที่ฉันได้ยินสำหรับ "ส่วนต่อประสานที่คล่องแคล่ว" คือ "train wreck";)

ฉันพูดว่า "มากกว่าหรือน้อยกว่าที่เทียบเท่า" เพราะอินเตอร์เฟซที่คล่องแคล่วเพิ่มริ้วรอย พวกเขาไม่ต้อง "ส่งคืนสิ่งนี้" พวกเขาสามารถ "คืนใหม่" นั่นเป็นวิธีการบรรลุวัตถุที่ไม่เปลี่ยนรูปใน OO

ดังนั้นคุณสามารถมีคลาส A ที่ทำ (pseudocode)

function doA():
    return new A(value + 1)

function doB():
    return new A(value * 2)

function doC():
    return new A(sqrt(value))

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


ตกลง แต่ถ้าวิธีการของคุณกลับมาnew(...)นั่นจะทำให้หน่วยความจำรั่ว
smci

@smci นั้นจะขึ้นอยู่กับภาษาการใช้งานและการใช้งานเป็นอย่างมาก แน่นอนว่าถ้าเป็น C ++ คุณอาจจะมีความจำรั่วไปทั่วสถานที่ แต่ทั้งหมดนี้จะได้รับการทำความสะอาดด้วยภาษากับผู้เก็บขยะ การใช้งานบางอย่าง (ไม่ว่าจะมีหรือไม่มี GC) อาจตรวจจับได้เมื่อไม่มีการใช้ออบเจ็กต์ใหม่และไม่ได้จัดสรรอะไรเลย
8bittree

@ 8bittree: เรากำลังพูดถึง Python คำถามนี้ถูกแท็ก Python 8 ปีที่แล้ว Python GC นั้นยอดเยี่ยมไม่ควรรั่วหน่วยความจำตั้งแต่แรก การโทร__init__ซ้ำ ๆ จะแนะนำค่าใช้จ่ายด้วย
smci

8

ภาษาส่วนใหญ่ตระหนักถึงสำนวน 'คืนตนเอง' และไม่สนใจหากไม่ได้ใช้ในบรรทัด อย่างไรก็ตามเป็นที่น่าสังเกตว่าใน Python ฟังก์ชั่นจะส่งกลับNoneโดยค่าเริ่มต้น

เมื่อฉันอยู่ในโรงเรียน CS ผู้สอนของฉันทำเรื่องใหญ่เกี่ยวกับความแตกต่างระหว่างฟังก์ชั่นขั้นตอนและกิจวัตร คำถามเรียงความที่เป็นตะคริวมือหลายข้อเริ่มต้นด้วยดินสอกลที่กำลังร้อนแรงอยู่ในมือของฉันเกี่ยวกับสิ่งที่

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

การผูกมัดตามที่พวกเขาทำนั้นเป็นเพียงการใส่คำตอบสำหรับการปฏิบัติการครั้งสุดท้ายในครั้งถัดไปและการรันไทม์ของ Python สามารถปรับให้เหมาะสมกับสิ่งนั้น รายการความเข้าใจเป็นรูปแบบในตัวนี้ (ทรงพลังมาก!)

ดังนั้นใน Python มันไม่สำคัญที่ทุกเมธอดหรือฟังก์ชั่นจะส่งคืนสิ่งต่าง ๆ ซึ่งเป็นเหตุผลว่าทำไมค่าเริ่มต้นคือไม่มี

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

หากวิธีการล้มเหลวควรกลับสู่ความสำเร็จหรือล้มเหลวหรือเพิ่มข้อยกเว้นที่จะจัดการ

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

ประเภทที่ จำกัด ภาษากรีดร้องและตะโกนและแตกเมื่อคุณพยายามทำเช่นนี้ แต่การตีความภาษา (Python, Lua, Lisp) นั้นมีความหลากหลายมากกว่า


4

ในสมอลทอล์คทุกวิธีที่ไม่ส่งคืนอย่างชัดเจนมี "คืนตัวเอง" โดยปริยาย

นั่นเป็นเพราะ (ก) การที่ทุกวิธีส่งคืนบางสิ่งทำให้โมเดลการคำนวณมีความเหมือนกันมากขึ้นและ (ข) มันมีประโยชน์มากที่วิธีการคืนค่าตัวเอง Josh K เป็นตัวอย่างที่ดี


ที่น่าสนใจ ... ในPythonทุกวิธีที่ไม่ส่งคืนอย่างชัดเจนมีนัย "ส่งคืนไม่มี"
งาน

2

ข้อดีของการส่งคืนวัตถุ ( return self)

  • ตัวอย่าง:

    print(foo.modify1().modify2())
    
    # instaed of
    foo.modify1()
    foo.modify2()
    print(foo)
    
  • รูปแบบที่นิยมมากขึ้นในชุมชนการเขียนโปรแกรม (ฉันคิดว่า)

ข้อดีของการกลายพันธุ์วัตถุ (ไม่ใช่return self)

  • ความสามารถในการใช้returnเพื่อวัตถุประสงค์อื่น
  • กุยโดแวนรอสซัม อนุมัติสไตล์นี้ (ฉันไม่เห็นด้วยกับข้อโต้แย้งของเขา)
  • สไตล์ยอดนิยมในชุมชน Python (ฉันคิดว่า)
  • เริ่มต้น (รหัสแหล่งที่มาน้อยกว่า)

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

@Nat การดำเนินการประมวลผลสตริงแตกต่างจากการดำเนินการที่เหลือเป็นอย่างไร
xged

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

@ Matt ฉันเพิ่งจำได้ว่าสตริง Python นั้นไม่เปลี่ยนรูป นั่นตอบคำถามของฉันอย่างเต็มที่
xged

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