การกำหนดฟังก์ชั่นโมดูลส่วนตัวในหลาม


238

ตามhttp://www.faqs.org/docs/diveintopython/fileinfo_private.html :

เช่นเดียวกับภาษาส่วนใหญ่ Python มีแนวคิดเกี่ยวกับองค์ประกอบส่วนตัว:

  • ฟังก์ชั่นส่วนตัวซึ่งไม่สามารถเรียกได้ นอกโมดูล

อย่างไรก็ตามถ้าฉันกำหนดสองไฟล์:

#a.py
__num=1

และ:

#b.py
import a
print a.__num

เมื่อฉันb.pyพิมพ์มันออกมา1โดยไม่มีข้อยกเว้น เป็นการดำน้ำที่ไม่ถูกต้องหรือฉันเข้าใจผิดบางอย่าง? และจะมีวิธีการบางอย่างไม่กำหนดหน้าที่ของโมดูลเป็นส่วนตัว?


ไม่ใช่ diveintopython ที่ผิด แต่ในตัวอย่างของพวกเขา: >>> import fileinfo >>> m = fileinfo.MP3FileInfo() >>> m.__parse("/music/_singles/kairo.mp3") 1 Traceback (innermost last): File "<interactive input>", line 1, in ? AttributeError: 'MP3FileInfo' instance has no attribute '__parse' fileinfo.MP3FileInfo () เป็นตัวอย่างของคลาส ซึ่งให้ข้อยกเว้นนี้เมื่อคุณใช้ขีดเส้นใต้คู่ ในกรณีของคุณคุณไม่ได้สร้างคลาสคุณเพิ่งสร้างโมดูล ดูเพิ่มเติมที่: stackoverflow.com/questions/70528/…
Homero Esmeraldo

คำตอบ:


323

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

((Btw แม้ว่ามันจะเป็นความลับที่ใกล้เคียงกัน แต่สำหรับ C ++: คอมไพเลอร์ส่วนใหญ่จะมี#define private publicบรรทัดที่เรียบง่ายก่อนที่จะรวมไฟล์#includeของคุณ.hคือทั้งหมดที่ใช้สำหรับตัวแปลงสัญญาณที่ยุ่งเหยิงเพื่อทำให้แฮก "ความเป็นส่วนตัว" ... ! -) )


82
บันทึกย่อของคุณใน C ++ ไม่ถูกต้อง ด้วยการใช้ #define private public คุณกำลังเปลี่ยนรหัสที่ส่งไปยังคอมไพเลอร์ซึ่งเป็นที่ที่ชื่อ mangling เกิดขึ้น
rhinoinrepose

14
นอกจากนี้ C ++ mangling ยังคลุมเครือ แต่แทบจะไม่เป็นความลับ คุณสามารถ "วิปัสสนา" ไบนารีที่ผลิตโดย C ++ ได้เช่นกัน โอทีขอโทษ
ศ. Falken

47
เนื่องจากเป็นการอัปเดตของ @rhinoinrepose มันไม่เพียง แต่ไม่ถูกต้องเท่านั้น แต่เป็นพฤติกรรมที่ไม่ได้กำหนดตามมาตรฐานเพื่อกำหนดคำหลักใหม่ด้วยแมโครตัวประมวลผลล่วงหน้า
Cory Kramer

3
@AlexMartelli ไม่static void foo()เป็นส่วนตัวอย่างที่ได้รับ อย่างน้อยมันก็ถูกซ่อนไว้ที่ linker และฟังก์ชั่นอาจถูกลบออกโดย inline
user877329

3
ในชีวิตจริงผู้คนถูกดำเนินคดีหากพวกเขาทำผิดกฎหมาย
Lay González

288

อาจจะมีความสับสนระหว่างเอกชนชั้นและไพร่พลโมดูล

โมดูลส่วนตัวเริ่มต้นด้วยหนึ่งขีด
องค์ประกอบดังกล่าวจะไม่คัดลอกพร้อมเมื่อใช้from <module_name> import *รูปแบบของคำสั่งนำเข้า; อย่างไรก็ตามจะถูกนำเข้าหากใช้import <moudule_name>ไวยากรณ์ ( ดูคำตอบของ Ben Wilhelm )
เพียงลบขีดล่างหนึ่งอันออกจาก a. _ จำนวนตัวอย่างของคำถามและจะไม่แสดงในโมดูลที่นำเข้า a.py โดยใช้from a import *ไวยากรณ์

ระดับส่วนตัวเริ่มต้นด้วยสองขีด (aka Dunder คือ D-ouble ภายใต้คะแนน)
เช่นตัวแปรมีชื่อของ "mangled" เพื่อรวม ClassName ฯลฯ
มันยังสามารถเข้าถึงนอกของตรรกะระดับผ่านชื่อ mangled
แม้ว่าชื่อ mangling สามารถทำหน้าที่เป็นอุปกรณ์ป้องกันที่ไม่รุนแรงต่อการเข้าถึงที่ไม่ได้รับอนุญาตจุดประสงค์หลักของมันคือเพื่อป้องกันการชนกันของชื่อที่เป็นไปได้กับสมาชิกคลาสของคลาสบรรพบุรุษ ดูการอ้างอิงที่ตลก แต่ถูกต้องของ Alex Martelli เพื่อให้ความยินยอมแก่ผู้ใหญ่ในขณะที่เขาอธิบายการประชุมที่ใช้เกี่ยวกับตัวแปรเหล่านี้

>>> class Foo(object):
...    __bar = 99
...    def PrintBar(self):
...        print(self.__bar)
...
>>> myFoo = Foo()
>>> myFoo.__bar  #direct attempt no go
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__bar'
>>> myFoo.PrintBar()  # the class itself of course can access it
99
>>> dir(Foo)    # yet can see it
['PrintBar', '_Foo__bar', '__class__', '__delattr__', '__dict__', '__doc__', '__
format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__
', '__subclasshook__', '__weakref__']
>>> myFoo._Foo__bar  #and get to it by its mangled name !  (but I shouldn't!!!)
99
>>>

ดี TIL เหตุผลว่าทำไมพวกเขาไม่ได้บังคับใช้ระดับโมดูล__private_functionแต่? ฉันวิ่งเข้าไปในนี้และได้รับข้อผิดพลาดเพราะมัน
ซานต้า

ขอบคุณสำหรับคำที่ใจดี @Terrabits แต่ฉันยินดีเป็นอย่างยิ่งที่จะยืนต่ออเล็กซ์ (ข้างหลัง!) ในทุกสิ่ง 'Python' นอกจากนี้คำตอบของเขามักจะกระชับและมีอารมณ์ขันมากขึ้นในขณะที่ยังคงมีการใช้อำนาจในระดับสูงเนื่องจากอเล็กซ์มีส่วนร่วมในภาษาและชุมชนอย่างกว้างขวาง
mjv

1
@mjv นี่เป็นคำอธิบายที่เป็นประโยชน์มาก! ขอบคุณ! ฉันค่อนข้างงงกับพฤติกรรมนี้มาระยะหนึ่งแล้ว ฉันหวังว่าตัวเลือกนี้จะทำให้เกิดข้อผิดพลาดบางอย่างนอกเหนือจาก AttributeError หากคุณพยายามเข้าถึงคลาสส่วนตัวโดยตรง อาจเป็น "PrivateAccessError" หรือบางสิ่งบางอย่างอาจมีความชัดเจนมากกว่า / มีประโยชน์ (นับตั้งแต่ได้รับข้อผิดพลาดที่มันไม่ได้มีแอตทริบิวต์ไม่ได้จริงๆจริง)
HFBrowning

82

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

หากคุณกำหนดชื่อส่วนตัวในโมดูลชื่อเหล่านั้นจะถูกนำเข้าสู่สคริปต์ใด ๆ ที่ใช้ไวยากรณ์ 'import module_name' ดังนั้นสมมติว่าคุณได้กำหนดไว้อย่างถูกต้องในตัวอย่างโมดูลส่วนตัว _num ใน a.py เช่นนั้น ..

#a.py
_num=1

.. คุณจะสามารถเข้าถึงได้ใน b.py ด้วยสัญลักษณ์ชื่อโมดูล:

#b.py
import a
...
foo = a._num # 1

หากต้องการนำเข้าเฉพาะที่ไม่ใช่ส่วนตัวจาก a.py คุณต้องใช้ไวยากรณ์จาก :

#b.py
from a import *
...
foo = _num # throws NameError: name '_num' is not defined

อย่างไรก็ตามเพื่อความชัดเจนจะเป็นการดีกว่าหากมีความชัดเจนในการนำเข้าชื่อจากโมดูลแทนที่จะนำเข้าทั้งหมดด้วย '*':

#b.py
from a import name1 
from a import name2
...

1
คุณระบุฟังก์ชัน / ไลบรารีใดที่นำเข้า ในinit .py หรือไม่
FistOfFury

ไม่มีความเสี่ยงของการชนชื่อเมื่อ_namesถูกเรียกด้วยimport a- พวกเขาเข้าถึงได้เหมือนa._namesเมื่อใช้สไตล์นี้
Josiah Yoder

@FistOfFury ใช่คุณระบุฟังก์ชั่นที่นำเข้าใน__init__.pyไฟล์ ดูที่นี่สำหรับความช่วยเหลือเกี่ยวกับเรื่องนี้
Mike Williamson

29

Python อนุญาตสำหรับคลาสส่วนตัวสมาชิกมีส่วนนำหน้าขีดล่างคู่ เทคนิคนี้ใช้งานไม่ได้ในระดับโมดูลดังนั้นฉันคิดว่านี่เป็นข้อผิดพลาดใน Dive Into Python

นี่คือตัวอย่างของฟังก์ชันคลาสส่วนตัว:

class foo():
    def bar(self): pass
    def __bar(self): pass

f = foo()
f.bar()   # this call succeeds
f.__bar() # this call fails

2
ฉันคิดว่าเจตนาของ OP คือการเขียนฟังก์ชั่นที่ไม่สามารถเข้าถึงได้จากภายนอกตัวอย่างเช่นแพ็คเกจเชิงพาณิชย์ ในเรื่องนั้นคำตอบนี้ไม่สมบูรณ์ ฟังก์ชัน __bar () ยังสามารถเข้าถึงได้จากภายนอกผ่าน f._foo__bar () ดังนั้นขีดเส้นใต้สองชั้นจึงไม่ทำให้เป็นส่วนตัว
SevakPrime

24

คุณสามารถเพิ่มฟังก์ชั่นภายใน:

def public(self, args):
   def private(self.root, data):
       if (self.root != None):
          pass #do something with data

อย่างนี้ถ้าคุณต้องการความเป็นส่วนตัวในระดับนั้น


9
ทำไมนี่ไม่ใช่คำตอบที่ดีที่สุด
safay


1

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

ใน Python มันดูแปลกไปนิดหน่อย แต่ถ้ามีอะไรบางอย่างที่จำเป็นต้องซ่อนไว้จริงๆ จนถึงจุดที่ใช้ python API และการเก็บสิ่งต่าง ๆ ที่จำเป็นต้องซ่อนใน C (หรือภาษาอื่น ๆ ) น่าจะเป็นวิธีที่ดีที่สุด ความล้มเหลวที่ฉันจะไปสำหรับการวางรหัสในฟังก์ชั่นเรียกมันและให้มันคืนสินค้าที่คุณต้องการส่งออก


-11

Python มีสามโหมดผ่าน., ส่วนตัว, สาธารณะและได้รับการป้องกันในขณะที่นำเข้าโมดูลเฉพาะโหมดสาธารณะเท่านั้นที่สามารถเข้าถึงได้ดังนั้นโมดูลส่วนตัวและที่ได้รับการป้องกันจะไม่สามารถเรียกได้จากภายนอกโมดูลเช่นเมื่อมีการนำเข้า

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