คลาส“ ส่วนตัว” (การนำไปใช้งาน) ใน Python


110

ฉันกำลังเขียนโค้ดโมดูล Python ขนาดเล็กซึ่งประกอบด้วยสองส่วน:

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

ในตอนแรกฉันตัดสินใจที่จะ "ซ่อน" คลาสการนำไปใช้งานนี้โดยกำหนดไว้ในฟังก์ชันโดยใช้มัน แต่สิ่งนี้ขัดขวางความสามารถในการอ่านและไม่สามารถใช้ได้หากฟังก์ชันหลายฟังก์ชันใช้คลาสเดียวกันซ้ำ

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

คำตอบ:


179

ใช้คำนำหน้าขีดล่างเดียว:

class _Internal:
    ...

นี่คืออนุสัญญา Python อย่างเป็นทางการสำหรับสัญลักษณ์ 'ภายใน' "จากการนำเข้าโมดูล *" ไม่นำเข้าอ็อบเจ็กต์ที่มีเครื่องหมายขีดล่าง

แก้ไข: อ้างอิงถึงอนุสัญญาขีดล่างเดียว


3
ฉันไม่ทราบว่ากฎขีดล่างที่ขยายไปยังชั้นเรียน ฉันไม่ต้องการเกะกะเนมสเปซของฉันเมื่อนำเข้าดังนั้นพฤติกรรมนี้จึงเป็นสิ่งที่ฉันกำลังมองหา ขอบคุณ!
oparisy

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

11
python.org/dev/peps/pep-0008 - _single_leading_underscore: ตัวบ่งชี้ "การใช้งานภายใน" ที่อ่อนแอ เช่น "from M import *" ไม่นำเข้าออบเจ็กต์ที่มีชื่อขึ้นต้นด้วยเครื่องหมายขีดล่าง - คลาสสำหรับการใช้งานภายในมีขีดล่างนำหน้า
ไมล์

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

66

ในระยะสั้น:

  1. คุณไม่สามารถบังคับใช้ความเป็นส่วนตัวได้ ไม่มีคลาส / วิธีการ / ฟังก์ชันส่วนตัวใน Python อย่างน้อยความเป็นส่วนตัวไม่เข้มงวดเหมือนในภาษาอื่น ๆ เช่น Java

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

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

สำหรับข้อมูลเพิ่มเติม:


ในการตอบสนองต่อ # 1 คุณสามารถบังคับใช้ความเป็นส่วนตัวของวิธีการได้ การใช้เครื่องหมายขีดล่างคู่เช่น __method (self) จะทำให้ไม่สามารถเข้าถึงได้จากภายนอกชั้นเรียน แต่มีวิธีการโดยการเรียกมันว่า Foo () ._ Foo__method (). ฉันเดาว่ามันแค่เปลี่ยนชื่อเป็นอะไรที่ลึกลับกว่านั้น
Evan Fosmark

1
คำพูดนั้นถูกต้อง
Paul Draper


11

รูปแบบที่ฉันใช้เป็นบางครั้งคือ:

กำหนดคลาส:

class x(object):
    def doThis(self):
        ...
    def doThat(self):
        ...

สร้างอินสแตนซ์ของคลาสโดยเขียนทับชื่อคลาส:

x = x()

กำหนดสัญลักษณ์ที่แสดงฟังก์ชันการทำงาน:

doThis = x.doThis
doThat = x.doThat

ลบอินสแตนซ์ตัวเอง:

del x

ตอนนี้คุณมีโมดูลที่เปิดเผยเฉพาะหน้าที่สาธารณะของคุณ


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

มีชื่อสำหรับเทคนิคนี้หรือไม่?
Bishwas Mishra


4

เพื่อแก้ไขปัญหาของข้อตกลงการออกแบบและดังที่คริสโตเฟอร์กล่าวไว้ไม่มีสิ่งที่เรียกว่า "ส่วนตัว" ใน Python สิ่งนี้อาจฟังดูบิดเบี้ยวสำหรับคนที่มาจากพื้นหลัง C / C ++ (เช่นฉันในขณะที่กลับมา) แต่ในที่สุดคุณอาจจะรู้ว่าการประชุมต่อไปนี้มีมากมายเพียงพอ

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

นอกจากนี้การมีทุกสิ่งที่เป็นสาธารณะก็มีสิทธิประโยชน์ที่ยอดเยี่ยมเช่นคุณสามารถทดสอบหน่วยอะไรก็ได้จากภายนอก (ซึ่งคุณไม่สามารถทำได้ด้วยโครงสร้างส่วนตัวของ C / C ++)


4

ใช้เครื่องหมายขีดล่างสองอันเพื่อนำหน้าชื่อของตัวระบุ "ส่วนตัว" สำหรับคลาสในโมดูลให้ใช้ขีดล่างนำหน้าเพียงตัวเดียวและจะไม่นำเข้าโดยใช้ "จากการนำเข้าโมดูล *"

class _MyInternalClass:
    def __my_private_method:
        pass

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


ทำไมต้อง _ สองคน? หนึ่งก็เพียงพอแล้ว
ล็อต

ขีดล่างเดียวเพียงพอที่จะป้องกันไม่ให้ "นำเข้า" จากการนำเข้าคลาสและฟังก์ชัน คุณต้องมีเครื่องหมายขีดล่างสองอันเพื่อกระตุ้นให้เกิดการเปลี่ยนแปลงชื่อของ Python ควรจะชัดเจนขึ้น ฉันแก้ไข
chroder

ตอนนี้การติดตาม ทำไมชื่อแหลกลาญ? ประโยชน์ที่เป็นไปได้ของสิ่งนั้นคืออะไร?
ล็อต

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