ความหมายของเครื่องหมายขีดล่างเดี่ยวและคู่หน้าชื่อวัตถุคืออะไร?


1291

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

นอกจากนี้ความหมายนั้นยังคงเหมือนเดิมไม่ว่าวัตถุที่เป็นปัญหาจะเป็นตัวแปรฟังก์ชันวิธีการหรือไม่?


2
คำตอบสั้น ๆ ที่ดีจากหัวข้ออื่น: stackoverflow.com/a/8689983/911945
Anton Tarasenko

คำตอบ:


1155

ขีดเส้นใต้เดียว

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

เพื่ออ้างอิงPEP-8 :

_single_leading_underscore: ตัวบ่งชี้ "การใช้ภายใน" อ่อน เช่นfrom M import *ไม่นำเข้าวัตถุที่ชื่อขึ้นต้นด้วยเครื่องหมายขีดล่าง

เครื่องหมายขีดล่างคู่ (การจับคู่ชื่อ)

จากPython docs :

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

และคำเตือนจากหน้าเดียวกัน:

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

ตัวอย่าง

>>> class MyClass():
...     def __init__(self):
...             self.__superprivate = "Hello"
...             self._semiprivate = ", world!"
...
>>> mc = MyClass()
>>> print mc.__superprivate
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: myClass instance has no attribute '__superprivate'
>>> print mc._semiprivate
, world!
>>> print mc.__dict__
{'_MyClass__superprivate': 'Hello', '_semiprivate': ', world!'}

17
เกิดอะไรขึ้นถ้ามีชื่อตัวแปรที่ประกาศด้วย 2 ขีดล่างซึ่งไม่ได้อยู่ในชั้นเรียน มันเป็นแค่ตัวแปรธรรมดาใช่ไหม?
Dhruv Ramani

5
ความหมายของการ__ขีดเส้นใต้คู่เพียงแค่เป็นชื่อตัวแปรคืออะไร? ชอบa, __ = foo()
AJ

104
คำตอบนี้ทำให้เข้าใจผิดอย่างมากเนื่องจากทำให้ผู้อ่านเชื่อว่ามีการใช้ dunderscore ในการสร้างแอตทริบิวต์ของอินสแตนซ์ "superprivate" นี่ไม่ใช่กรณีดังที่อธิบายไว้ที่นี่โดย Raymond Hettinger ซึ่งระบุอย่างชัดเจนว่ามีการใช้ dunderscore อย่างไม่ถูกต้องเพื่อทำเครื่องหมายสมาชิกส่วนตัวในขณะที่ถูกออกแบบมาให้ตรงข้ามกับส่วนตัว
Markus Meskanen

13
@MarkusMeskanen ฉันไม่เห็นด้วยคำตอบนั้นระบุการใช้ dunderscore อย่างชัดเจนเพื่อสร้างอินสแตนซ์ของตัวแปรและวิธีการระดับส่วนตัว ในขณะที่ dunderscore ถูกออกแบบมาเพื่อทำให้วิธีการและตัวแปรเหล่านี้เขียนทับได้ง่ายโดย subclasses (ทำให้เป็นสาธารณะ), การใช้ dunderscore เก็บรักษาอินสแตนซ์ส่วนตัวสำหรับใช้ภายในคลาสนั้น
arewm

6
@MarkusMeskanen: อิสรภาพสำหรับคลาสย่อยที่จะใช้ชื่อเดียวกับที่ Superclass ทำโดยไม่ต้องปิดกั้น Superclass - ในคำอื่น ๆ ชื่อ dunder ของ superclasses จะเป็นแบบส่วนตัว
Ethan Furman

311

คำตอบที่ยอดเยี่ยมจนถึงขณะนี้ แต่เกร็ดเล็กเกร็ดบางส่วนขาดหายไป เครื่องหมายขีดเส้นใต้เดียวนำหน้าไม่ได้เป็นเพียงแค่การประชุม: ถ้าคุณใช้from foobar import *และโมดูลfoobarไม่ได้กำหนด__all__รายการชื่อที่นำเข้าจากโมดูลจะไม่รวมสิ่งที่มีเครื่องหมายขีดเส้นใต้ สมมติว่าส่วนใหญ่เป็นการประชุมเนื่องจากกรณีนี้เป็นมุมที่ค่อนข้างคลุมเครือ ;-)

การประชุมระดับขีดถูกนำมาใช้กันอย่างแพร่หลายไม่เพียง แต่สำหรับส่วนตัวชื่อ แต่ยังสำหรับสิ่งที่ C ++ จะเรียกการคุ้มครองคน - ตัวอย่างเช่นชื่อของวิธีการที่มีความตั้งใจอย่างเต็มที่ที่จะถูกแทนที่โดย subclasses (แม้คนที่มีที่จะแทนที่เนื่องจากใน คลาสฐานพวกเขาraise NotImplementedError! -) มักจะเป็นชื่อเดียวที่ขีดเส้นใต้เดียวเพื่อระบุถึงรหัสโดยใช้อินสแตนซ์ของคลาสนั้น (หรือคลาสย่อย) ที่วิธีการดังกล่าวไม่ได้ตั้งใจจะเรียกโดยตรง

ตัวอย่างเช่นเมื่อต้องการสร้างคิวแบบเธรดที่ปลอดภัยด้วยวินัยการจัดคิวที่แตกต่างจาก FIFO ให้อิมพอร์ตคิวหนึ่งคลาสย่อยคิวคิวคิวและแทนที่เมธอดดังกล่าวเป็น_getและ_put; "รหัสลูกค้า" ไม่เคยเรียกวิธีการเหล่านั้น ("hook") แต่เป็นวิธีการสาธารณะ ("การจัดระเบียบ") เช่นputและget(ซึ่งเป็นที่รู้จักกันในรูปแบบการออกแบบวิธีแม่แบบ - ดูตัวอย่างที่นี่เพื่อการนำเสนอที่น่าสนใจจากวิดีโอ ของการพูดคุยของฉันในเรื่องด้วยการเพิ่มบทสรุปของการถอดเสียง)

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


1
ดังนั้นคุณจะตัดสินใจว่าจะใช้_var_nameหรือใช้var_name+ ยกเว้นมันได้__all__อย่างไร
endolith

3
@endolith ใช้เครื่องหมายขีดเส้นใต้เพื่อส่งสัญญาณไปยังผู้อ่านรหัสของคุณซึ่งพวกเขาอาจไม่ควรใช้สิ่งนี้ (เช่นเนื่องจากคุณอาจเปลี่ยนเป็นเวอร์ชัน 2.0 หรือ 1.1) ใช้ชัดเจน__all__ทุกครั้งที่คุณต้องการทำให้โมดูลfrom spam import *เป็นมิตร (รวมถึงที่ล่ามแบบโต้ตอบ) ดังนั้นเวลาส่วนใหญ่ของคำตอบคือทั้งสอง
abarnert

@AlexMartelli กฎที่เกี่ยวข้องกับการนำเข้านี้ถูกกล่าวถึงอย่างถูกกฎหมายในเอกสารหรือที่อื่น ๆ หรือไม่?
Vicrobot

1
ฉันชอบการเปรียบเทียบ C ++ ประการแรกผมไม่ชอบมันเมื่อมีคนโทรส่วนตัว_ เห็นได้ชัดว่าฉันกำลังพูดถึงการเปรียบเทียบเนื่องจากไม่มีอะไรเป็นส่วนตัวอย่างแท้จริงใน Python เมื่อดำน้ำในความหมายฉันจะบอกว่าเราสามารถผูกกับ_การป้องกันของ Java ตั้งแต่proctectedใน Java หมายถึง "เรียนที่ได้รับและ / หรือภายในแพคเกจเดียวกัน" แทนที่แพ็คเกจด้วยโมดูลเนื่องจาก PEP8 แจ้งให้เราทราบว่า_ไม่ใช่เพียงแค่การประชุมเมื่อพูดถึง*การนำเข้าและคุณก็มี และแน่นอน__จะเทียบเท่ากับ Java ส่วนตัวเมื่อพูดถึงตัวระบุภายในชั้นเรียน
Marius Mucenicu

2
ในขณะที่คำตอบที่ดีก็ยังส่งเสริมตนเองอย่างหนัก
เว็บไฮบริด dev

298

__foo__: นี่เป็นเพียงข้อตกลงวิธีสำหรับระบบ Python ในการใช้ชื่อที่จะไม่ขัดแย้งกับชื่อผู้ใช้

_foo: นี่เป็นเพียงการประชุมวิธีที่โปรแกรมเมอร์จะระบุว่าตัวแปรนั้นเป็นแบบส่วนตัว (ไม่ว่าจะมีความหมายอะไรใน Python)

__foo: นี่มีความหมายที่แท้จริง: ล่ามมาแทนที่ชื่อนี้ด้วย_classname__fooเพื่อให้แน่ใจว่าชื่อจะไม่ทับซ้อนกับชื่อที่คล้ายกันในคลาสอื่น

ไม่มีรูปแบบอื่น ๆ ของขีดเส้นใต้มีความหมายในโลกหลาม

ไม่มีความแตกต่างระหว่างคลาสตัวแปรโกลบอลและอื่น ๆ ในแบบแผนเหล่านี้


6
เพิ่งเจอ__fooและอยากรู้อยากเห็น มันจะทับซ้อนกับชื่อเมธอดที่คล้ายกันกับคลาสอื่นได้อย่างไร? ฉันหมายความว่าคุณยังคงต้องเข้าถึงมันเหมือนinstance.__foo()(ถ้ามันไม่ได้เปลี่ยนชื่อโดยล่าม) ใช่ไหม?
Bibhas Debnath

81
ผู้ชายคนนี้ระบุว่าfrom module import *จะไม่นำเข้าวัตถุที่อยู่ใต้คำนำหน้า ดังนั้น_fooเป็นมากกว่าแค่การประชุม
dotancohen

4
@Bibhas: ถ้าระดับBชั้นย่อยAและทั้งสองใช้foo()แล้วB.foo()จะลบล้างสืบทอดมาจาก.foo() Aตัวอย่างของการBจะสามารถเข้าถึงยกเว้นผ่านB.foo() super(B).foo()
naught101

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

206

._variable เป็นแบบกึ่งอิสระและมีความหมายสำหรับการประชุมเท่านั้น

.__variableมักถูกพิจารณาว่าเป็น superprivate อย่างไม่ถูกต้องในขณะที่ความหมายที่แท้จริงคือเพียง namemangle เพื่อป้องกันการเข้าถึงโดยไม่ตั้งใจ[1]

.__variable__ โดยทั่วไปจะสงวนไว้สำหรับวิธีการหรือตัวแปรในตัว

คุณยังคงสามารถเข้าถึง.__mangledตัวแปรได้หากคุณต้องการอย่างยิ่ง เครื่องหมายขีดล่างคู่เป็นเพียง namemangles หรือเปลี่ยนชื่อตัวแปรเป็นอย่างเช่นinstance._className__mangled

ตัวอย่าง:

class Test(object):
    def __init__(self):
        self.__a = 'a'
        self._b = 'b'

>>> t = Test()
>>> t._b
'b'

t._b สามารถเข้าถึงได้เพราะถูกซ่อนไว้โดยการประชุมเท่านั้น

>>> t.__a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Test' object has no attribute '__a'

t .__ a ไม่พบเพราะไม่มีอีกแล้วเนื่องจาก namemangling

>>> t._Test__a
'a'

ด้วยการเข้าถึงinstance._className__variableแทนที่จะใช้ชื่อขีดเส้นใต้คู่คุณสามารถเข้าถึงค่าที่ซ่อนอยู่ได้


แต่ถ้าเป็น "__a" เป็นตัวแปรคลาสคุณก็ไม่สามารถเข้าถึงได้แม้จะมีคำแนะนำจาก python docs ..
Vitaliy Terziev

คุณสามารถอัปเดตคำตอบของคุณด้วยตัวอย่างของการขีดเส้นใต้สองครั้งที่เกี่ยวกับการสืบทอด?
ตัวแปร

116

ขีดเส้นใต้เดี่ยวที่จุดเริ่มต้น:

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

class BaseForm(StrAndUnicode):

    def _get_errors(self):
        "Returns an ErrorDict for the data provided for the form"
        if self._errors is None:
            self.full_clean()
        return self._errors

    errors = property(_get_errors)

(ข้อมูลโค้ดนี้นำมาจากซอร์สโค้ด django: django / forms / forms.py) ในรหัสerrorsนี้เป็นคุณสมบัติสาธารณะ แต่วิธีที่คุณสมบัตินี้เรียกว่า _get_errors เป็น "ส่วนตัว" ดังนั้นคุณไม่ควรเข้าถึง

ขีดเส้นใต้สองอันที่จุดเริ่มต้น:

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

class A(object):
    def __test(self):
        print "I'm a test method in class A"

    def test(self):
        self.__test()

a = A()
a.test()
# a.__test() # This fails with an AttributeError
a._A__test() # Works! We can access the mangled name directly!

เอาท์พุท:

$ python test.py
I'm test method in class A
I'm test method in class A

ตอนนี้สร้างคลาสย่อย B และทำการปรับแต่งสำหรับเมธอด __test

class B(A):
    def __test(self):
        print "I'm test method in class B"

b = B()
b.test()

ผลผลิตจะเป็น ....

$ python test.py
I'm test method in class A

ดังที่เราได้เห็น A.test () ไม่ได้เรียกวิธีการทดสอบ B .__ () ตามที่เราคาดหวัง แต่อันที่จริงนี่เป็นพฤติกรรมที่ถูกต้องสำหรับ __ วิธีการสองวิธีที่เรียกว่า __test () จะถูกเปลี่ยนชื่อโดยอัตโนมัติ (mangled) เป็น _A__test () และ _B__test () ดังนั้นพวกเขาจึงไม่ได้แทนที่โดยไม่ตั้งใจ เมื่อคุณสร้างวิธีการที่เริ่มต้นด้วย __ หมายความว่าคุณไม่ต้องการให้ใครก็ตามสามารถลบล้างมันได้และคุณตั้งใจจะเข้าถึงจากภายในชั้นเรียนของตัวเองเท่านั้น

ขีดเส้นใต้สองอันที่จุดเริ่มต้นและตอนท้าย:

เมื่อเราเห็นวิธีที่ชอบ__this__อย่าเรียกมันว่า นี่คือวิธีการที่หลามมีไว้สำหรับโทรหาไม่ใช่คุณ ลองดู:

>>> name = "test string"
>>> name.__len__()
11
>>> len(name)
11

>>> number = 10
>>> number.__add__(40)
50
>>> number + 50
60

มีโอเปอเรเตอร์หรือฟังก์ชันเนทีฟเสมอซึ่งเรียกวิธีการเวทย์มนตร์เหล่านี้ บางครั้งมันเป็นเพียงการขอหลามแบบหลามในบางสถานการณ์ ตัวอย่างเช่น__init__()เรียกว่าเมื่อวัตถุถูกสร้างขึ้นหลังจาก__new__()ถูกเรียกให้สร้างตัวอย่าง ...

ลองยกตัวอย่าง ...

class FalseCalculator(object):

    def __init__(self, number):
        self.number = number

    def __add__(self, number):
        return self.number - number

    def __sub__(self, number):
        return self.number + number

number = FalseCalculator(20)
print number + 10      # 10
print number - 20      # 40

สำหรับรายละเอียดเพิ่มเติมโปรดดูที่คู่มือ PEP-8 สำหรับวิธีมหัศจรรย์เพิ่มเติมให้ดูPDFนี้


1
หลังจากแก้ไขคำตอบนี้ด้วยตัวเองฉันชอบstackoverflow.com/a/8689983/1048186
Josiah Yoder

"ตามที่เราเห็นคุณหมายถึงอะไร A.test () ไม่ได้เรียกวิธีการทดสอบ B .__ () - คุณเรียก A.test () ที่ไหน?
ตัวแปร

18

บางครั้งคุณมีสิ่งที่ดูเหมือนจะเป็น tuple กับขีดเส้นใต้นำใน

def foo(bar):
    return _('my_' + bar)

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

from sphinx.locale import l_, _

และใน sphinx.locale, _ () ถูกกำหนดให้เป็นนามแฝงของฟังก์ชั่นการแปลบางภาษา


11

เนื่องจากผู้คนมากมายพูดถึงการพูดคุยของเรย์มอนด์ฉันจะทำให้มันง่ายขึ้นโดยเขียนสิ่งที่เขาพูดว่า:

ความตั้งใจของขีดเส้นใต้คู่ไม่ได้เกี่ยวกับความเป็นส่วนตัว ความตั้งใจที่จะใช้มันอย่างนี้

class Circle(object):

    def __init__(self, radius):
        self.radius = radius

    def area(self):
        p = self.__perimeter()
        r = p / math.pi / 2.0
        return math.pi * r ** 2.0

    def perimeter(self):
        return 2.0 * math.pi * self.radius

    __perimeter = perimeter  # local reference


class Tire(Circle):

    def perimeter(self):
        return Circle.perimeter(self) * 1.25

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

สมมติว่าคุณไม่ให้มีการอ้างอิงของท้องถิ่นในperimeter Circleตอนนี้ชั้นมาTireแทนที่การดำเนินการโดยไม่ต้องสัมผัสperimeter areaเมื่อคุณโทรTire(5).area()ในทางทฤษฎีมันควรจะใช้Circle.perimeterสำหรับการคำนวณ แต่ในความเป็นจริงมันกำลังใช้Tire.perimeterซึ่งไม่ใช่พฤติกรรมที่ตั้งใจ นั่นเป็นเหตุผลที่เราต้องการการอ้างอิงในแวดวง

แต่ทำไม__perimeterแทน_perimeter? เพราะ_perimeterยังให้ชั้นเรียนที่ได้รับโอกาสในการแทนที่:

class Tire(Circle):

    def perimeter(self):
        return Circle.perimeter(self) * 1.25

    _perimeter = perimeter

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

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


1
ขอบคุณสไลด์ไม่แสดงอย่างถูกต้องดังนั้นฉันจึงลงเอยด้วยการไม่สนใจว่าทำไมรหัสของฉันจึงล้มเหลว
cgte

8

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

class PrivateVarC(object):

    def get_x(self):
        pass

    def set_x(self, val):
        pass

    rwvar = property(get_p, set_p)  

    ronly = property(get_p) 

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


7

ไปที่https://dbader.org/blog/meaning-of-underscores-in-python

  • Single Leading Underscore (_var) : หลักการตั้งชื่อที่ระบุชื่อมีไว้สำหรับใช้ภายใน โดยทั่วไปแล้วไม่ได้บังคับใช้โดยล่าม Python (ยกเว้นในการนำเข้าไวด์การ์ด) และมีความหมายเป็นคำแนะนำให้โปรแกรมเมอร์เท่านั้น
  • Single Trailing Underscore (var_) : ใช้โดยการประชุมเพื่อหลีกเลี่ยงการตั้งชื่อที่ขัดแย้งกับคำหลัก Python
  • เครื่องหมายขีดเส้นใต้คู่นำหน้า (__ var) : ชื่อทริกเกอร์ mangling เมื่อใช้ในบริบทของคลาส บังคับใช้โดยล่าม Python
  • นำหน้าและขีดล่างสองเท่า (__ var__) : ระบุวิธีพิเศษที่กำหนดโดยภาษา Python หลีกเลี่ยงรูปแบบการตั้งชื่อนี้สำหรับแอตทริบิวต์ของคุณเอง
  • เครื่องหมายขีดล่างเดี่ยว (_) : บางครั้งใช้เป็นชื่อสำหรับตัวแปรชั่วคราวหรือไม่มีนัยสำคัญ (“ ไม่สนใจ”) นอกจากนี้: ผลลัพธ์ของนิพจน์สุดท้ายใน Python REPL

5

คำตอบที่ยอดเยี่ยมและถูกต้องฉันได้ให้ตัวอย่างง่ายๆพร้อมกับคำนิยาม / ความหมายที่เรียบง่าย

ความหมาย:

some_variable --►เป็นสาธารณะทุกคนสามารถดูได้

_some_variable - ►เป็นสาธารณะทุกคนสามารถดูสิ่งนี้ได้ แต่เป็นแบบแผนเพื่อระบุว่าเป็นส่วนตัว ... การเตือนห้ามใช้ Python

__some_varaible - ► Python แทนที่ชื่อตัวแปรด้วย _classname__some_varaible (ชื่อ AKA ชื่อ mangling) และลด / ซ่อนการมองเห็นและเป็นเหมือนตัวแปรส่วนตัว

เพียงเพื่อความซื่อสัตย์ที่นี่ตามเอกสาร Python

ตัวแปรอินสแตนซ์ "ส่วนตัว" ที่ไม่สามารถเข้าถึงได้ยกเว้นจากภายในวัตถุไม่มีอยู่ใน Python "

ตัวอย่าง:

class A():
    here="abc"
    _here="_abc"
    __here="__abc"


aObject=A()
print(aObject.here) 
print(aObject._here)
# now if we try to print __here then it will fail because it's not public variable 
#print(aObject.__here)

_ _some_varaible - .... และมันช่วยลด / ซ่อนการมองเห็นและเป็นเหมือนตัวแปรส่วนตัว ไม่ได้ชื่อ mangling เป็นประเด็น แต่ก็ไม่ได้ซ่อนวิธีการ
AMC

4

การขีดเส้นใต้เดี่ยวชั้นนำเป็นการประชุม ไม่มีความแตกต่างจากมุมมองของล่ามหากชื่อเริ่มต้นด้วยการขีดเส้นใต้เดียวหรือไม่

ดับเบิลขีดชั้นนำและต่อท้ายจะใช้สำหรับในตัววิธีการเช่น__init__, __bool__ฯลฯ

เครื่องหมายขีดล่างคู่ที่ไม่มีคู่ท้ายเป็นระเบียบเช่นกันอย่างไรก็ตามเมธอดคลาสจะถูกmangledโดยล่าม สำหรับตัวแปรหรือชื่อฟังก์ชั่นพื้นฐานไม่มีความแตกต่าง


3

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

แต่ชื่อ __double_underscore ไม่ใช่ชื่อที่ mangled ในโมดูลตัวอย่างเช่น สิ่งที่เกิดขึ้นคือชื่อที่ขึ้นต้นด้วยเครื่องหมายขีดล่างหนึ่ง (หรือมากกว่า) จะไม่ถูกนำเข้าหากคุณนำเข้าทั้งหมดจากโมดูล (จากโมดูลนำเข้า *) หรือชื่อที่แสดงในวิธีใช้ (โมดูล)


1
นอกจากนี้ชื่อที่ขึ้นต้นด้วยขีดเส้นใต้อย่างน้อยหนึ่งอันที่มีขีดเส้นใต้สองอันขึ้นไปจะมีลักษณะการทำงานเหมือนกับชื่ออื่นอีกครั้ง
Bentley4

3

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

class parent(object):
    __default = "parent"
    def __init__(self, name=None):
        self.default = name or self.__default

    @property
    def default(self):
        return self.__default

    @default.setter
    def default(self, value):
        self.__default = value


class child(parent):
    __default = "child"

หากคุณสร้างอินสแตนซ์ลูกใน python REPL คุณจะเห็นด้านล่าง

child_a = child()
child_a.default            # 'parent'
child_a._child__default    # 'child'
child_a._parent__default   # 'parent'

child_b = child("orphan")
## this will show 
child_b.default            # 'orphan'
child_a._child__default    # 'child'
child_a._parent__default   # 'orphan'

สิ่งนี้อาจเห็นได้ชัดสำหรับบางคน แต่มันทำให้ฉันระวังตัวในสภาพแวดล้อมที่ซับซ้อนกว่านี้มาก


3

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

การอ้างอิง https://docs.python.org/2/tutorial/classes.html#private-variables-and-class-local-references


1
_ คล้ายกับตัวอย่างเช่นภายในใน c # มากกว่าส่วนตัว ขีดเส้นใต้สองเท่ามันคล้ายกับส่วนตัวมากขึ้นแล้วขีดเส้นใต้เป็นส่วนตัวฉันอยากจะบอกว่า
Ini

2

การรับข้อเท็จจริงของ _ และ __ นั้นง่ายมาก คำตอบอื่น ๆ แสดงพวกเขาค่อนข้างดี การใช้งานนั้นยากต่อการพิจารณา

นี่คือวิธีที่ฉันเห็น:

_

ควรใช้เพื่อระบุว่าฟังก์ชั่นนั้นไม่ได้มีไว้สำหรับการใช้สาธารณะเช่นเป็น API สิ่งนี้และข้อ จำกัด การนำเข้าทำให้มันทำงานเหมือนinternalใน c #

__

ควรใช้เพื่อหลีกเลี่ยงการชนกันของชื่อในลำดับชั้นของสืบทอดและเพื่อหลีกเลี่ยงการผูกปลาย เหมือนส่วนตัวใน c #

==>

หากคุณต้องการที่จะแสดงให้เห็นอะไรบางอย่างที่ไม่ได้สำหรับการใช้ที่สาธารณะ แต่มันควรจะทำหน้าที่เหมือนการใช้งานprotected _หากคุณต้องการที่จะแสดงให้เห็นอะไรบางอย่างที่ไม่ได้สำหรับการใช้ที่สาธารณะ แต่มันควรจะทำหน้าที่เหมือนการใช้งานprivate__

นี่เป็นคำพูดที่ฉันชอบมาก:

ปัญหาคือผู้แต่งชั้นเรียนอาจคิดว่า "ชื่อแอตทริบิวต์ / วิธีการนี้ควรเป็นส่วนตัวโดยสามารถเข้าถึงได้จากภายในคำจำกัดความของชั้นนี้เท่านั้น" และใช้ __private Convention แต่ในภายหลังผู้ใช้ของคลาสนั้นอาจสร้างคลาสย่อยที่ถูกต้องตามกฎหมายต้องเข้าถึงชื่อนั้น ดังนั้นทั้งคลาสซูเปอร์จะต้องมีการแก้ไข (ซึ่งอาจเป็นเรื่องยากหรือเป็นไปไม่ได้) หรือรหัสย่อยต้องใช้ชื่อ mangled ด้วยตนเอง (ซึ่งน่าเกลียดและเปราะบางที่สุด)

แต่ปัญหาที่อยู่ในความคิดของฉันว่าถ้าไม่มี IDE ที่เตือนคุณเมื่อคุณแทนที่วิธีการค้นหาข้อผิดพลาดอาจใช้เวลาสักครู่ถ้าคุณมีวิธีการ overriden จากคลาสฐานโดยไม่ได้ตั้งใจ

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