it-swarm-vi.com

Tại sao các phương thức 'riêng tư' của Python không thực sự riêng tư?

Python cung cấp cho chúng ta khả năng tạo các phương thức và biến 'riêng tư' trong một lớp bằng cách thêm hai dấu gạch dưới vào tên, như thế này: __myPrivateMethod(). Làm thế nào, sau đó, người ta có thể giải thích điều này

>>> class MyClass:
...     def myPublicMethod(self):
...             print 'public method'
...     def __myPrivateMethod(self):
...             print 'this is private!!'
... 
>>> obj = MyClass()
>>> obj.myPublicMethod()
public method
>>> obj.__myPrivateMethod()
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: MyClass instance has no attribute '__myPrivateMethod'
>>> dir(obj)
['_MyClass__myPrivateMethod', '__doc__', '__module__', 'myPublicMethod']
>>> obj._MyClass__myPrivateMethod()
this is private!!

Thỏa thuận là gì?!

Tôi sẽ giải thích điều này một chút cho những người không hiểu điều đó.

>>> class MyClass:
...     def myPublicMethod(self):
...             print 'public method'
...     def __myPrivateMethod(self):
...             print 'this is private!!'
... 
>>> obj = MyClass()

Những gì tôi đã làm ở đó là tạo ra một lớp với một phương thức công khai và một phương thức riêng tư và khởi tạo nó.

Tiếp theo, tôi gọi phương thức công khai của nó.

>>> obj.myPublicMethod()
public method

Tiếp theo, tôi thử và gọi phương thức riêng tư của nó.

>>> obj.__myPrivateMethod()
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: MyClass instance has no attribute '__myPrivateMethod'

Mọi thứ có vẻ tốt ở đây; chúng tôi không thể gọi nó. Trên thực tế, đó là 'riêng tư'. Chà, thực ra thì không. Chạy dir () trên đối tượng cho thấy một phương thức ma thuật mới mà python tạo ra một cách kỳ diệu cho tất cả các phương thức 'riêng tư' của bạn.

>>> dir(obj)
['_MyClass__myPrivateMethod', '__doc__', '__module__', 'myPublicMethod']

Tên của phương thức mới này luôn là một dấu gạch dưới, theo sau là tên lớp, theo sau là tên phương thức.

>>> obj._MyClass__myPrivateMethod()
this is private!!

Quá nhiều cho việc đóng gói, nhỉ?

Trong mọi trường hợp, tôi luôn nghe thấy Python không hỗ trợ đóng gói, vậy tại sao lại thử? Đưa cái gì?

557
willurd

Việc xáo trộn tên được sử dụng để đảm bảo rằng các lớp con không vô tình ghi đè lên các phương thức và thuộc tính riêng của các siêu lớp của chúng. Nó không được thiết kế để ngăn chặn sự truy cập có chủ ý từ bên ngoài.

Ví dụ:

>>> class Foo(object):
...     def __init__(self):
...         self.__baz = 42
...     def foo(self):
...         print self.__baz
...     
>>> class Bar(Foo):
...     def __init__(self):
...         super(Bar, self).__init__()
...         self.__baz = 21
...     def bar(self):
...         print self.__baz
...
>>> x = Bar()
>>> x.foo()
42
>>> x.bar()
21
>>> print x.__dict__
{'_Bar__baz': 21, '_Foo__baz': 42}

Tất nhiên, nó bị hỏng nếu hai lớp khác nhau có cùng tên.

524
Alya

Ví dụ về hàm riêng

import re
import inspect

class MyClass :

    def __init__(self) :
        pass

    def private_function ( self ) :
        try :
            function_call = inspect.stack()[1][4][0].strip()

            # See if the function_call has "self." in the begining
            matched = re.match( '^self\.', function_call )
            if not matched :
                print 'This is Private Function, Go Away'
                return
        except :
            print 'This is Private Function, Go Away'
            return

        # This is the real Function, only accessible inside class #
        print 'Hey, Welcome in to function'

    def public_function ( self ) :
        # i can call private function from inside the class
        self.private_function()

### End ###
206
arun

Khi tôi lần đầu tiên chuyển từ Java sang Python, tôi bị ghét này. Nó làm tôi sợ đến chết.

Hôm nay nó có thể chỉ là một điều tôi yêu nhất về Python.

Tôi thích được ở trên một nền tảng, nơi mọi người tin tưởng lẫn nhau và không cảm thấy như họ cần xây dựng những bức tường không thể xuyên thủng xung quanh mã của họ. Trong các ngôn ngữ được đóng gói mạnh, nếu API có lỗi và bạn đã tìm ra lỗi sai, bạn vẫn có thể không thể làm việc xung quanh nó vì phương thức cần thiết là riêng tư. Trong Python thái độ là: "chắc chắn". Nếu bạn nghĩ rằng bạn hiểu tình hình, có lẽ bạn thậm chí đã đọc nó, thì tất cả những gì chúng ta có thể nói là "chúc may mắn!".

Hãy nhớ rằng, đóng gói thậm chí không liên quan yếu đến "an ninh", hoặc giữ trẻ em ngoài bãi cỏ. Nó chỉ là một mẫu khác nên được sử dụng để làm cho một cơ sở mã dễ hiểu hơn.

141
Thomas Ahle

Từ http://www.faqs.org/docs/diveintopython/fileinfo_private.html

Nói đúng ra, phương pháp riêng là có thể truy cập bên ngoài lớp học của họ, chỉ không dễ dàng truy cập. Không có gì trong Python thực sự là riêng tư; trong nội bộ, tên của các phương thức riêng tư và các thuộc tính được đọc sai và không thay đổi trên bay để làm cho chúng có vẻ không thể truy cập bằng tên được đặt của họ. Bạn có thể truy cập phương thức __parse của Lớp MP3FileInfo theo tên _MP3FileInfo__parse. Xác nhận rằng điều này là thú vị, sau đó hứa với không bao giờ, bao giờ làm điều đó trong mã thực sự . Phương pháp riêng tư là riêng tư cho một lý do, nhưng giống như nhiều thứ khác trong Python, sự riêng tư của họ là cuối cùng là vấn đề quy ước, không phải lực lượng.

134
xsl

Cụm từ thường được sử dụng là "tất cả chúng ta đều đồng ý ở đây". Bằng cách chuẩn bị một dấu gạch dưới đơn (không phơi bày) hoặc nhân đôi dấu gạch dưới (ẩn), bạn đang nói với người dùng của lớp rằng bạn dự định thành viên sẽ 'riêng tư' theo một cách nào đó. Tuy nhiên, bạn tin tưởng mọi người khác cư xử có trách nhiệm và tôn trọng điều đó, trừ khi họ có lý do thuyết phục không (ví dụ: trình gỡ lỗi, hoàn tất mã).

Nếu bạn thực sự phải có một cái gì đó là riêng tư, thì bạn có thể triển khai nó trong một tiện ích mở rộng (ví dụ: trong C cho CPython). Tuy nhiên, trong hầu hết các trường hợp, bạn chỉ cần học cách làm Pythonic.

86
Tony Meyer

Không giống như bạn hoàn toàn không thể có được sự riêng tư của các thành viên trong bất kỳ ngôn ngữ nào (con trỏ mỹ phẩm trong C++, Reflection in .NET/Java).

Vấn đề là bạn gặp lỗi nếu bạn cố gắng gọi phương thức riêng một cách tình cờ. Nhưng nếu bạn muốn tự bắn vào chân mình, hãy tiếp tục và làm điều đó.

Chỉnh sửa: Bạn không cố gắng bảo mật công cụ của mình bằng cách đóng gói OO, phải không?

32
Maximilian

Hành vi tương tự tồn tại khi tên thuộc tính mô-đun bắt đầu bằng một dấu gạch dưới (ví dụ: _foo).

Các thuộc tính mô-đun có tên như vậy sẽ không được sao chép vào mô-đun nhập khi sử dụng phương thức from*, ví dụ:

from bar import *

Tuy nhiên, đây là một quy ước và không phải là một hạn chế ngôn ngữ. Đây không phải là thuộc tính riêng tư; chúng có thể được tham chiếu và thao tác bởi bất kỳ nhà nhập khẩu nào. Một số ý kiến ​​cho rằng vì điều này, Python không thể thực hiện đóng gói thực sự.

12
Ross

Đó chỉ là một trong những lựa chọn thiết kế ngôn ngữ. Ở một mức độ nào đó họ là hợp lý. Họ làm điều đó để bạn cần đi thật xa để thử và gọi phương thức, và nếu bạn thực sự cần nó đến mức đó, bạn phải có một lý do khá chính đáng!

Móc gỡ lỗi và kiểm tra đến với các ứng dụng có thể, tất nhiên được sử dụng có trách nhiệm.

12
ctcherry

Quy ước đặt tên class.__stuff cho phép lập trình viên biết anh ta không có ý định truy cập __stuff từ bên ngoài. Cái tên xáo trộn khiến không ai có thể làm điều đó một cách tình cờ.

Đúng, bạn vẫn có thể giải quyết vấn đề này, nó thậm chí còn dễ hơn các ngôn ngữ khác (BTW cũng cho phép bạn làm điều này), nhưng không có lập trình viên Python nào làm điều này nếu anh ta quan tâm đến việc đóng gói.

11
Nickolay

Với Python 3.4, đây là hành vi:

>>> class Foo:
        def __init__(self):
                pass
        def __privateMethod(self):
                return 3
        def invoke(self):
                return self.__privateMethod()


>>> help(Foo)
Help on class Foo in module __main__:

class Foo(builtins.object)
 |  Methods defined here:
 |
 |  __init__(self)
 |
 |  invoke(self)
 |
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |
 |  __dict__
 |      dictionary for instance variables (if defined)
 |
 |  __weakref__
 |      list of weak references to the object (if defined)

 >>> f = Foo()
 >>> f.invoke()
 3
 >>> f.__privateMethod()
 Traceback (most recent call last):
   File "<pyshell#47>", line 1, in <module>
     f.__privateMethod()
 AttributeError: 'Foo' object has no attribute '__privateMethod'

https://docs.python.org/3/tutorial/groupes.html#tut-private

Lưu ý rằng các quy tắc xáo trộn được thiết kế chủ yếu để tránh tai nạn; vẫn có thể truy cập hoặc sửa đổi một biến được coi là riêng tư. Điều này thậm chí có thể hữu ích trong các trường hợp đặc biệt, chẳng hạn như trong trình gỡ lỗi.

Ngay cả khi câu hỏi đã cũ, tôi hy vọng đoạn trích của tôi có thể hữu ích.

3
Alberto

Mối quan tâm quan trọng nhất về các phương thức và thuộc tính riêng là bảo các nhà phát triển không gọi nó bên ngoài lớp và đây là đóng gói. người ta có thể hiểu nhầm bảo mật từ đóng gói. khi một người cố tình sử dụng cú pháp như thế (dưới đây) mà bạn đã đề cập, bạn không muốn đóng gói.

obj._MyClass__myPrivateMethod()

Tôi đã di chuyển từ C # và lúc đầu, điều đó thật kỳ lạ đối với tôi nhưng sau một thời gian tôi đã nghĩ ra rằng chỉ có cách mà các nhà thiết kế mã Python nghĩ về OOP là khác. 

2
Afshin Amiri

Tại sao các phương thức 'riêng tư' của Python không thực sự riêng tư?

Theo tôi hiểu, họ không thể là riêng tư. Làm thế nào riêng tư có thể được thực thi?

Câu trả lời rõ ràng là "các thành viên riêng tư chỉ có thể được truy cập thông qua self", nhưng điều đó sẽ không hoạt động - self không đặc biệt trong Python, nó không có gì khác hơn là một tên thường được sử dụng cho tham số đầu tiên của hàm.

0
user200783