จุดประสงค์ของคำว่า 'ตัวเอง' คืออะไร?


1130

วัตถุประสงค์ของselfคำใน Python คืออะไร? ฉันเข้าใจว่ามันหมายถึงวัตถุเฉพาะที่สร้างขึ้นจากคลาสนั้น แต่ฉันไม่สามารถดูได้ว่าทำไมจึงต้องเพิ่มอย่างชัดเจนในทุกฟังก์ชั่นเป็นพารามิเตอร์ เพื่อแสดงใน Ruby ฉันสามารถทำสิ่งนี้:

class myClass
    def myFunc(name)
        @name = name
    end
end

ซึ่งฉันเข้าใจค่อนข้างง่าย อย่างไรก็ตามใน Python ฉันต้องรวมself:

class myClass:
    def myFunc(self, name):
        self.name = name

ทุกคนสามารถพูดคุยกับฉันผ่านสิ่งนี้ได้ไหม? มันไม่ใช่สิ่งที่ฉันได้เจอในประสบการณ์ของฉัน


111
คุณอาจพบว่าบทความนี้น่าสนใจ "ทำไมต้องมีตัวตนชัดเจน" โดย Guido van Rossum: neopythonic.blogspot.com/2008/10//
unutbu

14
ดู "ทำไมต้อง 'ตัวเอง' ใช้อย่างชัดเจนในคำจำกัดความและวิธีการโทร": docs.python.org/faq/…
unutbu

37
"สิ่งที่ฉันเข้าใจค่อนข้างง่าย" --- อัตนัยค่อนข้างคุณไม่คิดว่า? สิ่งที่ทำให้@nameใช้งานง่ายกว่าself.name? IMO หลังนั้นใช้งานง่ายกว่า
Santa

14
นั่นคือความแตกต่างที่สำคัญระหว่างฟังก์ชั่นและวิธีการเรียน ฟังก์ชั่นลอยอิสระไม่มีภาระผูกพัน เมธอด class (อินสแตนซ์) จำเป็นต้องทราบว่าเป็นคุณสมบัติพาเรนต์ (และคุณสมบัติพาเรนต์) ดังนั้นคุณต้องผ่านเมธอดที่อ้างอิงไปยังคลาสพาเรนต์ (เป็นตัวเอง ) เป็นเพียงกฎข้อหนึ่งที่มีนัยยะน้อยกว่าที่คุณต้องทำให้เป็นภายในก่อนที่จะเข้าใจ OOP ภาษาอื่น ๆ เลือกน้ำตาลประโยคผ่านความเรียบง่ายทางความหมายหลามไม่ใช่ภาษาอื่น
Evan Plaice

9
ฉันไม่คิดว่า "ชัดเจนดีกว่าโดยปริยาย" อธิบายตัวเลือกการออกแบบนี้ได้ดีจริงๆ @fooและself.fooมีความชัดเจนเท่ากันเนื่องจากไม่ต้องมีการแก้ปัญหาโดยปริยาย (เช่นใน C ++ สมาชิกอินสแตนซ์สามารถเข้าถึงได้ "โดยนัย" โดยไม่มี "ชัดเจน" โดยใช้เนมสเปซ) ข้อแตกต่างคือทับทิมแนะนำ semantic ใหม่ (@) ในขณะที่ Python ไม่ได้ หรือไม่ความหมายใหม่มีมูลค่าจำนวน verbosity หลีกเลี่ยงเป็นอัตนัย แม้ว่ามันควรจะสังเกตว่าภาษาสมัยใหม่ส่วนใหญ่เลือกที่จะแนะนำแนวคิดที่นี่ (เช่น php's $ this, JS's this)
จิง

คำตอบ:


710

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

Python อาจทำอย่างอื่นเพื่อแยกความแตกต่างชื่อปกติจากแอตทริบิวต์ - ไวยากรณ์พิเศษอย่าง Ruby มีหรือต้องการการประกาศเช่น C ++ และ Java ทำหรืออาจจะเป็นสิ่งที่แตกต่างกันมากขึ้น แต่ก็ไม่ได้ Python ทั้งหมดสำหรับการทำสิ่งที่ชัดเจนทำให้มันชัดเจนว่าอะไรคืออะไรและแม้ว่ามันจะไม่ได้ทำทุกที่ แต่มันก็ทำเพื่อคุณสมบัติเช่นกัน self.ว่าทำไมการกำหนดไปยังความต้องการเช่นแอตทริบิวต์จะรู้ว่าสิ่งเช่นกำหนดให้และนั่นเป็นเหตุผลที่มันต้องการ


23
@Georg: clsหมายถึงคลาสอ็อบเจ็กต์ไม่ใช่อินสแตนซ์อ็อบเจ็กต์
SilentGhost

17
@SilentGhost: จริง ๆ แล้วชื่อของพารามิเตอร์แรกคือสิ่งที่คุณต้องการให้เป็น ในวิธีการเรียนการประชุมคือการใช้clsและselfจะใช้ตามอัตภาพสำหรับวิธีการเช่น ถ้าฉันต้องการฉันสามารถใช้selfสำหรับ classmethods และclsเมธอดอินสแตนซ์ ฉันยังสามารถใช้bobและfnordถ้าฉันชอบ
SingleNegationElimination

67
ฉันคิดว่ามันน่าสนใจที่ชุมชนไม่ได้เลือกแทนthis selfไม่selfเคยมีประวัติบางอย่างที่ฉันไม่ได้ตระหนักถึงในที่มีอายุมากกว่าการเขียนโปรแกรมภาษา?
Jules GM

24
@Julius selfมาจากการประชุมของ Modula-3 ดูคำตอบนี้สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับตัวเลือกนี้ (คำเตือน: เหมืองของมัน)
Bakuriu

9
@Julius selfคำหลัก (Smalltalk, 1980) นำหน้าthisคำหลัก (จาก C ++) ดู: stackoverflow.com/questions/1079983/…
Wes Turner

425

ลองคลาสเวกเตอร์อย่างง่ายกัน:

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

เราต้องการมีวิธีที่คำนวณความยาว จะเป็นอย่างไรถ้าเราต้องการนิยามไว้ในชั้นเรียน

    def length(self):
        return math.sqrt(self.x ** 2 + self.y ** 2)

มันควรเป็นอย่างไรเมื่อเรานิยามมันเป็นวิธีการ / ฟังก์ชั่นระดับโลก?

def length_global(vector):
    return math.sqrt(vector.x ** 2 + vector.y ** 2)

ดังนั้นโครงสร้างทั้งหมดยังคงเหมือนเดิม ฉันจะใช้สิ่งนี้ได้อย่างไร หากเราสันนิษฐานว่าสักครู่ที่เราไม่ได้เขียนlengthวิธีสำหรับVectorชั้นเรียนของเราเราสามารถทำสิ่งนี้ได้:

Vector.length_new = length_global
v = Vector(3, 4)
print(v.length_new()) # 5.0

งานนี้เพราะพารามิเตอร์แรกของlength_globalสามารถนำมาใช้เป็นพารามิเตอร์ในself นี้จะไม่เป็นไปได้โดยชัดเจนlength_newself


อีกวิธีในการทำความเข้าใจความต้องการที่ชัดเจนselfก็คือการดูว่า Python เติมน้ำตาลในประโยคไว้ที่ใด เมื่อคุณจำไว้ว่าโดยทั่วไปแล้วการโทรเช่น

v_instance.length()

ถูกแปลงภายในเป็น

Vector.length(v_instance)

มันง่ายที่จะดูว่าselfพอดีที่ไหนคุณไม่ได้เขียนวิธีการเช่นใน Python; สิ่งที่คุณเขียนคือวิธีการเรียนซึ่งจะต้องใช้อินสแตนซ์เป็นพารามิเตอร์แรก ดังนั้นคุณจะต้องวางพารามิเตอร์อินสแตนซ์ที่ใดที่หนึ่งอย่างชัดเจน


4
Vector.length_new = length_global ... จริง ๆ แล้วฉันเริ่มใช้ไวยากรณ์เช่นนี้ในการประกาศคลาสของฉัน เมื่อใดก็ตามที่ฉันต้องการสืบทอดบางวิธีจากคลาสอื่นฉันเพียงคัดลอกการอ้างอิงไปยังเมธอดอย่างชัดเจน
Jeeyoung Kim

2
มันจะยุติธรรมที่จะบอกว่า "วิธีการตัวอย่าง" ของงูใหญ่เป็นเพียงน้ำตาล syntactic ของวิธีการคงที่ทั่วโลก (เช่นใน Java หรือ C ++) กับวัตถุตัวอย่างส่งผ่านไปยังแพคเกจคุณลักษณะหลาย ๆ ? --- สิ่งนี้เป็นครึ่งจริงตั้งแต่ใน polymorphism จุดประสงค์ที่สำคัญของ "this" (เช่นใน java) หรือ "self" คือการให้วิธีการที่ถูกต้องแก่คุณ Python มีสิ่งนี้ ดังนั้นการเรียก myobj.someMethod () เท่ากับ TheClassOfMyObj.someMethod (myobj) ในไพ ธ อน โปรดทราบว่า "TheClassOfMyObj" ถูกคำนวณโดย python จาก "self" โดยอัตโนมัติมิฉะนั้นคุณจะต้องค้นหาคำนั้น
เท็ดดี้เท็ดดี้

3
Infact ไม่เพียง แต่เป็นอินสแตนซ์เมธอดเพียงคลาสเมธอดเท่านั้น แต่เมธอดเป็นเพียงฟังก์ชันซึ่งเป็นสมาชิกของคลาสเช่นเดียวกับการVector.length_new = length_globalแสดง
RussW

1
"ใช้งานได้เนื่องจากพารามิเตอร์ตัวแรกของ length_global สามารถนำกลับมาใช้ใหม่เป็นพารามิเตอร์ตัวเองใน length_new สิ่งนี้จะไม่สามารถเกิดขึ้นได้หากปราศจากตัวตนที่ชัดเจน" - มันจะทำงานได้เหมือนกัน มันจะถูกใช้ซ้ำสำหรับตัวเองโดยนัย ... ตัวอย่างที่สองคือการใช้เหตุผลแบบวงกลม - คุณต้องวางตัวเองไว้ที่นั่นเพราะไพ ธ อนต้องการตัวตนที่ชัดเจน
Karoly Horvath

1
@ KarolyHorvath: แน่นอนว่ามันจะเป็นไปได้ที่จะมีภาษาที่มีรูปแบบที่วิธีการที่กำหนดไว้ภายในไม่จำเป็นต้องมีตัวเองชัดเจน แต่วิธีการที่กำหนดไว้ภายนอกทำ แต่ฉันบอกว่ามีความสอดคล้องกันในการกำหนดตัวตนที่ชัดเจนในทั้งสองกรณีซึ่งทำให้มีเหตุผลที่ถูกต้องที่จะทำเช่นนี้ ภาษาอื่นอาจเลือกแนวทางที่แตกต่างกัน
Debilski

421

สมมติว่าคุณมีคลาสClassAที่มีวิธีการที่methodAกำหนดเป็น:

def methodA(self, arg1, arg2):
    # do something

และObjectAเป็นตัวอย่างของชั้นนี้

ตอนนี้เมื่อObjectA.methodA(arg1, arg2)ถูกเรียกใช้ python จะแปลงภายในให้คุณเป็น:

ClassA.methodA(ObjectA, arg1, arg2)

selfตัวแปรหมายถึงวัตถุเอง


94
ฉันอ่านคำตอบและความเข้าใจทั้งหมดฉันอ่านคำตอบนี้แล้วมันก็สมเหตุสมผล
เซท


2
ทำไมไม่เก็บความกล้าไว้ข้างในเหมือนอย่าง Ruby
Cees Timmerman

แต่ใน __init __ (self) method มันยอมรับตัวเองแล้วแม้ว่าจะไม่ได้สร้างวัตถุมันจะอ้างถึงตัวเองได้อย่างไร
saurav

อย่างจริงจังนี่เป็นตัวอย่างที่ดีกว่าของ Debilski มากเพราะมันไม่ซับซ้อนเกินไปและผู้คนอาจไม่คุ้นเคยกับเวกเตอร์
NoName

215

เมื่อวัตถุถูกยกตัวอย่างวัตถุจะถูกส่งไปยังพารามิเตอร์ของตนเอง

ป้อนคำอธิบายรูปภาพที่นี่

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

ป้อนคำอธิบายรูปภาพที่นี่

Object ถูกส่งไปยังพารามิเตอร์ของตัวเองเพื่อให้วัตถุสามารถเก็บข้อมูลของตัวเองไว้ได้

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

ดูภาพประกอบด้านล่าง:

ป้อนคำอธิบายรูปภาพที่นี่


เดี๋ยวก่อนที่นั่นเมื่อเข้าถึงแอตทริบิวต์ของ Bob เช่นโดย "bob.name ()" คุณจะใช้ bob () .. self.name เพื่อพูดจาก ' init ' จริงไหม?
udarH3

3
เมื่อคุณเขียน bob.name () ในความคิดเห็นข้างต้นคุณหมายถึงว่า bob มีวิธีการที่ชื่อว่า name () เนื่องจากความจริงที่ว่าคุณเพิ่มวงเล็บหลังชื่อ ในตัวอย่างนี้อย่างไรก็ตามไม่มีวิธีดังกล่าว 'bob.name' (ซึ่งไม่มีวงเล็บ) กำลังเข้าถึงแอตทริบิวต์ที่เรียกว่าชื่อจากเมธอด init (ตัวสร้าง) โดยตรง เมื่อวิธีการพูดของบ๊อบถูกเรียกมันเป็นวิธีการที่เข้าถึงแอตทริบิวต์ชื่อและส่งกลับในคำสั่งพิมพ์ หวังว่านี่จะช่วยได้
sw123456

3
ไม่คุณจะได้รับค่าของ self.name ซึ่งสำหรับวัตถุ bob นั้นจริง ๆ แล้วเป็น bob.name เนื่องจากชื่อของวัตถุนั้นถูกส่งไปยังพารามิเตอร์ของตัวเองเมื่อมันถูกสร้างขึ้น (อินสแตนซ์) หวังอีกครั้งว่าสิ่งนี้จะช่วยได้ รู้สึกฟรีเพื่อโพสต์หลักถ้ามี
sw123456

2
ชื่อถูกกำหนดให้กับ self.name เมื่อเริ่มต้น หลังจากที่วัตถุถูกสร้างขึ้นตัวแปรทั้งหมดที่เป็นของวัตถุนั้นจะถูกนำหน้าด้วย 'ตนเอง' จำไว้ว่าตัวเองถูกแทนที่ด้วยชื่อของวัตถุเมื่อมันถูกสร้างขึ้นจากชั้นเรียน
sw123456

5
นี่คือวิธีที่คุณอธิบายเนื้อหา! เป็นงานที่ดี :)
penta

80

ฉันชอบตัวอย่างนี้:

class A: 
    foo = []
a, b = A(), A()
a.foo.append(5)
b.foo
ans: [5]

class A: 
    def __init__(self): 
        self.foo = []
a, b = A(), A()
a.foo.append(5)
b.foo
ans: []

18
vars ที่ไม่มีตัวตนดังนั้น vars คงที่ของคลาสอย่างเช่นในจาวา
teddy teddy

5
เท็ดดี้เท็ดดี้คุณไม่ถูกต้องทั้งหมด พฤติกรรม (คงที่หรือไม่คงที่เช่น) ขึ้นไม่เพียงselfแต่ในแต่ยังอยู่ในประเภทตัวแปร ลองทำตัวอย่างแรกด้วยจำนวนเต็มอย่างง่ายแทนที่จะเป็นรายการ ผลลัพธ์จะแตกต่างกันมาก
Konstantin

2
จริงๆแล้วคำถามของฉันกับเรื่องนี้คือทำไมคุณได้รับอนุญาตให้พูดa.fooในตัวอย่างแรกมากกว่าA.foo? เห็นได้ชัดว่าfooเป็นของชั้น ...
เรดอน Rosborough

คุณสามารถเรียกสมาชิกคงที่จากอินสแตนซ์ของวัตถุในภาษาส่วนใหญ่ ทำไมจึงแปลกใจ
Paarth

2
@RadonRosborough เพราะในตัวอย่างแรกaและbเป็นทั้งป้ายกำกับ (หรือตัวชี้) สำหรับA()(คลาส) a.fooอ้างอิงถึงA().fooวิธีการเรียน ในตัวอย่างที่สองแม้ว่าaจะกลายเป็นการอ้างอิงไปยังอินสแตนซ์ของเช่นเดียวกับA() bตอนนี้พวกเขาเป็นอินสแตนซ์แทนที่จะเป็นวัตถุคลาสเองตัวเองอนุญาตให้fooวิธีการทำงานกับอินสแตนซ์
LegendaryDude

40

ฉันจะสาธิตด้วยรหัสที่ไม่ใช้คลาส :

def state_init(state):
    state['field'] = 'init'

def state_add(state, x):
    state['field'] += x

def state_mult(state, x):
    state['field'] *= x

def state_getField(state):
    return state['field']

myself = {}
state_init(myself)
state_add(myself, 'added')
state_mult(myself, 2)

print( state_getField(myself) )
#--> 'initaddedinitadded'

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

ตอนนี้เรามาสาธิตโค้ดข้างต้นโดยใช้เครื่องจักรระดับไพ ธ อนในตัวเพื่อแสดงให้เห็นว่ามันเหมือนกัน

class State(object):
    def __init__(self):
        self.field = 'init'
    def add(self, x):
        self.field += x
    def mult(self, x):
        self.field *= x

s = State()
s.add('added')    # self is implicitly passed in
s.mult(2)         # self is implicitly passed in
print( s.field )

[ย้ายคำตอบของฉันจากคำถามปิดที่ซ้ำกัน]


1
ฉันหวังว่าหลามจะคอยดูแลตัวจัดการเช่นเดียวกับรูบี้
Cees Timmerman

20

ข้อความที่ตัดตอนมาจากเอกสาร Python เกี่ยวกับตนเอง :

ใน Modula-3 ไม่มีการจดชวเลข [ใน Python] สำหรับการอ้างอิงสมาชิกของออบเจ็กต์จากวิธีการ: ฟังก์ชันเมธอดถูกประกาศด้วยอาร์กิวเมนต์แรกที่ชัดเจนที่แสดงถึงออบเจกต์ซึ่งมีการเรียกโดยปริยาย

บ่อยครั้งที่อาร์กิวเมนต์แรกของวิธีการนั้นเรียกว่าตัวเอง นี่คืออะไรมากกว่าแบบแผน: ชื่อตัวเองไม่มีความหมายพิเศษกับ Python อย่างไรก็ตามโปรดทราบว่าหากไม่ปฏิบัติตามอนุสัญญารหัสของคุณอาจอ่านได้ง่ายกว่าโปรแกรมเมอร์ Python รายอื่นและเป็นไปได้ว่าโปรแกรมคลาสเบราว์เซอร์อาจถูกเขียนขึ้นโดยอาศัยการประชุมดังกล่าว

สำหรับข้อมูลเพิ่มเติมโปรดดูที่การกวดวิชาเอกสารหลามในชั้นเรียน


20

เช่นเดียวกับเหตุผลอื่น ๆ ทั้งหมดที่ระบุไว้แล้วจะช่วยให้เข้าถึงวิธีการแทนที่ได้ง่ายขึ้น Class.some_method(inst)คุณสามารถโทรหา

ตัวอย่างของที่มีประโยชน์:

class C1(object):
    def __init__(self):
         print "C1 init"

class C2(C1):
    def __init__(self): #overrides C1.__init__
        print "C2 init"
        C1.__init__(self) #but we still want C1 to init the class too
>>> C2()
"C2 init"
"C1 init"

17

การใช้งานจะคล้ายกับการใช้thisคำหลักใน Java คือการอ้างอิงถึงวัตถุปัจจุบัน


2
คลาส myClass: def myFunc (ชื่อนี้): this.name = ชื่อ
LEMUEL ADANE

16

Python ไม่ใช่ภาษาที่สร้างขึ้นสำหรับการเขียนโปรแกรมเชิงวัตถุซึ่งแตกต่างจาก Java หรือ C ++

เมื่อเรียกใช้เมธอดสแตติกใน Python เพียงแค่เขียนเมธอดที่มีอาร์กิวเมนต์ปกติอยู่ภายใน

class Animal():
    def staticMethod():
        print "This is a static method"

อย่างไรก็ตามวิธีการวัตถุที่คุณต้องสร้างตัวแปรซึ่งเป็นสัตว์ในกรณีนี้ต้องมีการโต้แย้งตัวเอง

class Animal():
    def objectMethod(self):
        print "This is an object method which needs an instance of a class"

วิธีการตนเองยังใช้เพื่ออ้างถึงเขตข้อมูลตัวแปรภายในชั้นเรียน

class Animal():
    #animalName made in constructor
    def Animal(self):
        self.animalName = "";


    def getAnimalName(self):
        return self.animalName

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

หากคุณไม่เข้าใจคำเดียวที่ฉันกำลังพูด Google ก็คือ "การเขียนโปรแกรมเชิงวัตถุ" เมื่อคุณเข้าใจสิ่งนี้คุณไม่จำเป็นต้องถามคำถาม :)


1 เพราะความแตกต่างระหว่างและstaticMethod() objectMethod(self)ฉันต้องการเพิ่มที่เพื่อเรียกแรกคุณจะพูดว่าAnimal.staticMethod()ในขณะที่objectMethod()ต้องการอินสแตนซ์:a = Animal(); a.objectMethod()
Laryx Decidua

สิ่งที่คุณพูดไม่จริง 100% นั่นเป็นเพียงการประชุม คุณยังสามารถเรียกใช้วิธีการคงที่จากวัตถุที่สร้างขึ้น คุณจะไม่สามารถใช้สมาชิกชั้นเรียนได้เพราะคุณไม่ได้ประกาศตัวเอง ฉันยังสามารถโทรหา Animal.objectMethod (animalObj) เพื่อโทรหาค่าคงที่ โดยทั่วไปนี่หมายถึงวิธีการคงที่เป็นเพียงวิธีการที่ไม่ได้ใช้ตัวแปรสมาชิก ไม่จำเป็นต้องประกาศตนเอง ฉันคิดว่ามันเป็นข้อกำหนดทางภาษาที่งี่เง่า ภาษาอย่าง Lua และ C ++ ให้ตัวแปร obj อยู่เบื้องหลัง
user441521

คุณทำการประกาศสตริง AnimalName ที่ไร้ประโยชน์และวิธีการทำ AnimalName ล้มเหลว
Cees Timmerman

3
@ytpillai ไม่เกี่ยวข้อง ไม่ควรแสดงรหัสที่สับสนและไม่ถูกต้องเป็นคำตอบ
Cees Timmerman

1
def getAnimalNameเพื่อไม่ปิดบังสตริงที่คุณพยายามจะส่งคืนและselfอ้างอิงถึงอินสแตนซ์ของคลาสไม่ใช่ฟิลด์ใด ๆ ที่อยู่ภายใน
Cees Timmerman

10

มันอยู่ที่นั่นเพื่อติดตาม Python Zen“ ชัดเจนดีกว่านัย” มันเป็นการอ้างอิงถึงคลาสวัตถุของคุณ ใน Java และ PHP, thisตัวอย่างเช่นมันเรียกว่า

ถ้าเป็นเขตในรูปแบบของคุณคุณสามารถเข้าถึงได้โดยuser_type_nameself.user_type_name


10

ประการแรกตัวเองเป็นชื่อธรรมดาคุณสามารถใส่อะไรก็ได้ลงไปแทน

มันหมายถึงวัตถุเองดังนั้นเมื่อคุณใช้มันคุณจะประกาศว่า. name และ .age เป็นคุณสมบัติของวัตถุนักเรียน (หมายเหตุไม่ใช่ระดับนักเรียน) ที่คุณกำลังจะสร้าง

class Student:
    #called each time you create a new Student instance
    def __init__(self,name,age): #special method to initialize
        self.name=name
        self.age=age

    def __str__(self): #special method called for example when you use print
        return "Student %s is %s years old" %(self.name,self.age)

    def call(self, msg): #silly example for custom method
        return ("Hey, %s! "+msg) %self.name

#initializing two instances of the student class
bob=Student("Bob",20)
alice=Student("Alice",19)

#using them
print bob.name
print bob.age
print alice #this one only works if you define the __str__ method
print alice.call("Come here!") #notice you don't put a value for self

#you can modify attributes, like when alice ages
alice.age=20
print alice

รหัสอยู่ที่นี่


1
คำตอบของคุณดูเหมือนชัดเจนที่สุดสำหรับฉัน +1
Pawel

9

selfเป็นการอ้างอิงวัตถุไปยังวัตถุนั้นเองพวกมันเหมือนกัน Python ไม่ได้เรียกวิธีการในบริบทของวัตถุเอง selfใน Python อาจใช้เพื่อจัดการกับโมเดลวัตถุที่กำหนดเองหรือบางสิ่ง


8

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

1: อธิบายการใช้ตนเอง

โปรดทราบว่ามันไม่ใช่คำหลัก

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

2: ทำไมเรามีวิธีนี้และทำไมเราไม่สามารถกำจัดมันเป็นอาร์กิวเมนต์เช่น Java และมีคำหลักแทน

สิ่งที่ผมอยากจะเพิ่มก็คือตัวเลือกอาร์กิวเมนต์ช่วยให้ผมที่จะประกาศวิธีการคงอยู่ภายในชั้นเรียนโดยไม่ได้เขียนselfself

ตัวอย่างรหัส:

class MyClass():
    def staticMethod():
        print "This is a static method"

    def objectMethod(self):
        print "This is an object method which needs an instance of a class, and that is what self refers to"

PS : ใช้งานได้ใน Python 3.x เท่านั้น

ในเวอร์ชันก่อนหน้าคุณต้องเพิ่ม@staticmethodมัณฑนากรอย่างชัดเจนไม่เช่นselfนั้นข้อโต้แย้งจะเป็นหน้าที่


7

ฉันแปลกใจที่ไม่มีใครเลี้ยง Lua ขึ้นมาได้ Lua ใช้ตัวแปร 'ตัวเอง' แต่สามารถละเว้นได้ แต่ยังคงใช้อยู่ C ++ ทำเช่นเดียวกันกับ 'this' ฉันไม่เห็นเหตุผลใด ๆ ที่จะต้องประกาศ 'ตนเอง' ในแต่ละฟังก์ชั่น แต่คุณควรจะสามารถใช้มันได้เช่นเดียวกับ lua และ C ++ สำหรับภาษาที่ภูมิใจในการสรุปก็แปลกที่คุณจะต้องประกาศตัวแปรตนเอง


6

ลองดูตัวอย่างต่อไปนี้ซึ่งอธิบายถึงวัตถุประสงค์ของ self

class Restaurant(object):  
    bankrupt = False

    def open_branch(self):
        if not self.bankrupt:
           print("branch opened")

#create instance1
>>> x = Restaurant()
>>> x.bankrupt
False

#create instance2
>>> y = Restaurant()
>>> y.bankrupt = True   
>>> y.bankrupt
True

>>> x.bankrupt
False  

self ใช้ / จำเป็นเพื่อแยกแยะระหว่างอินสแตนซ์

ที่มา: อธิบายตัวแปรตัวเองในไพ ธ อน - Pythontips


ใช่ฉันคิดว่าเรารู้ว่าทำไมถึงใช้ตัวเอง แต่คำถามคือทำไมภาษาทำให้คุณประกาศอย่างชัดเจน ภาษาอื่น ๆ อีกมากมายไม่ต้องการสิ่งนี้และภาษาที่ภูมิใจในตัวเองสั้น ๆ คุณคิดว่าพวกเขาจะให้ตัวแปรเบื้องหลังเพื่อใช้เช่น Lua หรือ C ++ (นี่)
user441521

3
@ kmario23 คุณตอบสนองมาจากที่นี่: pythontips.com/2013/08/07/the-self-variable-in-python-explained โปรดรับทราบผู้เขียนต้นฉบับเสมอเมื่อโพสต์คำตอบเป็นของคุณเอง
geekidharsh

@geekidharsh ขอบคุณฉันเพิ่มบันทึกย่อ!
kmario23

5

เป็นเพราะวิธีการที่หลามได้รับการออกแบบทางเลือกจะไม่ทำงาน Python ถูกออกแบบมาเพื่ออนุญาตให้มีการกำหนดวิธีการหรือฟังก์ชั่นในบริบทที่ทั้งนัยthis(a-la Java / C ++) หรือชัดแจ้ง@(a-la ruby) ไม่ทำงาน ลองมาตัวอย่างกับแนวทางชัดเจนกับอนุสัญญาหลาม:

def fubar(x):
    self.x = x

class C:
    frob = fubar

ตอนนี้fubarฟังก์ชั่นจะไม่ทำงานเพราะมันจะถือว่าselfเป็นตัวแปรทั่วโลก (และในfrobเช่นกัน) ทางเลือกอื่นคือเรียกใช้งานเมธอดด้วยขอบเขตโกลบอลที่ถูกแทนที่ (ซึ่งselfเป็นอ็อบเจ็กต์)

วิธีการโดยนัยจะเป็น

def fubar(x)
    myX = x

class C:
    frob = fubar

นี่จะหมายความว่าmyXจะถูกตีความว่าเป็นตัวแปรท้องถิ่นในfubar(และในfrobเช่นกัน) ทางเลือกอื่นที่นี่คือการเรียกใช้งานเมธอดที่มีขอบเขตโลคัลที่ถูกแทนที่ซึ่งถูกเก็บไว้ระหว่างการโทร แต่นั่นจะลบความเป็นไปได้ของตัวแปรโลคอลเมธอด

อย่างไรก็ตามสถานการณ์ปัจจุบันทำงานได้ดี:

 def fubar(self, x)
     self.x = x

 class C:
     frob = fubar

นี่เมื่อเรียกว่าเป็นวิธีการที่frobจะได้รับวัตถุที่มันเรียกว่าผ่านselfพารามิเตอร์และfubarยังสามารถเรียกว่ามีวัตถุเป็นพารามิเตอร์และทำงานเหมือนกัน (มันเป็นเช่นเดียวกับที่C.frobผมคิดว่า)


3

ใน__init__วิธีการตัวเองหมายถึงวัตถุที่สร้างขึ้นใหม่ ในวิธีการเรียนอื่น ๆ มันหมายถึงอินสแตนซ์ที่มีวิธีการที่ถูกเรียก

ตัวเองเป็นชื่อเป็นเพียงการประชุมเรียกมันว่าคุณต้องการ! แต่เมื่อใช้มันตัวอย่างเช่นในการลบวัตถุคุณจะต้องใช้ชื่อเดียวกัน: __del__(var)ซึ่งvarถูกใช้ใน__init__(var,[...])

คุณควรดูclsด้วยเพื่อให้ได้ภาพที่ใหญ่ขึ้น โพสต์นี้อาจมีประโยชน์


3

ตนเองทำตัวเหมือนชื่อวัตถุปัจจุบันหรืออินสแตนซ์ของคลาส

# Self explanation.


 class classname(object):

    def __init__(self,name):

        self.name=name
        # Self is acting as a replacement of object name.
        #self.name=object1.name

   def display(self):
      print("Name of the person is :",self.name)
      print("object name:",object1.name)


 object1=classname("Bucky")
 object2=classname("ford")

 object1.display()
 object2.display()

###### Output 
Name of the person is : Bucky
object name: Bucky
Name of the person is : ford
object name: Bucky

1

self คงหนีไม่พ้น

มีเพียงคำถามที่ควรselfจะเป็นนัยหรือชัดเจน Guido van Rossumการแก้ไขคำกล่าวที่ว่าคำถามนี้มีการเข้าพักself

ดังนั้นselfอยู่ที่ไหน

selfถ้าเราก็จะยึดติดอยู่กับโปรแกรมการทำงานเราจะไม่จำเป็นต้อง เมื่อเราเข้าสู่Python OOPเราจะพบว่าselfมี

นี่คือกรณีการใช้งานทั่วไปclass Cพร้อมวิธีการm1

class C:
    def m1(self, arg):
        print(self, ' inside')
        pass

ci =C()
print(ci, ' outside')
ci.m1(None)
print(hex(id(ci))) # hex memory address

โปรแกรมนี้จะแสดงผล:

<__main__.C object at 0x000002B9D79C6CC0>  outside
<__main__.C object at 0x000002B9D79C6CC0>  inside
0x2b9d79c6cc0

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


หมายเหตุมีวิธีการเรียนสามประเภท:

  • วิธีการคงที่ (อ่าน: ฟังก์ชั่น)
  • วิธีการเรียน
  • วิธีการอินสแตนซ์ (กล่าวถึง)

0

จากเอกสาร ,

สิ่งพิเศษเกี่ยวกับวิธีการคือวัตถุตัวอย่างจะถูกส่งผ่านเป็นอาร์กิวเมนต์แรกของฟังก์ชั่น ในตัวอย่างของเราโทรเป็นว่าเทียบเท่ากับx.f() MyClass.f(x)โดยทั่วไปการเรียกเมธอดที่มีรายการอาร์กิวเมนต์ n เท่ากับการเรียกฟังก์ชันที่สอดคล้องกับรายการอาร์กิวเมนต์ที่สร้างขึ้นโดยการแทรกวัตถุอินสแตนซ์ของเมธอดก่อนอาร์กิวเมนต์แรก

ก่อนหน้านี้ตัวอย่างที่เกี่ยวข้อง

class MyClass:
    """A simple example class"""
    i = 12345

    def f(self):
        return 'hello world'

x = MyClass()


-2

เป็นการอ้างอิงที่ชัดเจนถึงวัตถุอินสแตนซ์ของคลาส


21
ฉันไม่คิดว่านี่จะช่วยริชชิลล่าในการทำความเข้าใจเหตุผลที่อยู่เบื้องหลัง
เฟรดSchölly

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