2012-09-20 34 views
12

Tôi biết cú pháp gạch dưới đôi nghĩa là gì đối với các thuộc tính/phương thức của lớp Python, nhưng nó có nghĩa là một cái gì đó cho đối số phương thức?Dấu gạch dưới kép cho phương thức Python * đối số *

Dường như bạn không thể chuyển đối số bắt đầu bằng dấu gạch dưới kép cho phương thức. Nó gây nhầm lẫn bởi vì bạn có thể làm điều đó cho các chức năng bình thường.

Hãy xem xét kịch bản này:

def egg(__a=None): 
    return __a 

print "egg(1) =", 
print egg(1) 
print 


class Spam(object): 

    def egg(self, __a=None): 
     return __a 

print "Spam().egg(__a=1) =", 
print Spam().egg(__a=1) 

Chạy sản lượng kịch bản này:

egg(1) = 1 

Spam().egg(__a=1) = 
Traceback (most recent call last): 
    File "/....py", line 15, in <module> 
    print Spam().egg(__a=1) 
TypeError: egg() got an unexpected keyword argument '__a' 

Tôi đã kiểm tra này với Python 2.7.2.


Một số ví dụ khác

này hoạt động:

def egg(self, __a): 
    return __a 


class Spam(object): 

    egg = egg 

Spam().egg(__a=1) 

này không:

class Spam(object): 

    def _egg(self, __a=None): 
     return __a 

    def egg(self, a): 
     return self._egg(__a=a) 

Spam().egg(1) 

Trả lời

12

Tên mangling áp dụng cho tất cả các định với dấu gạch dưới đôi hàng đầu, regardless of where they occur (thứ hai để câu cuối cùng trong phần đó):

Chuyển đổi này độc lập với ngữ cảnh cú pháp trong đó mã định danh được sử dụng.

Điều này đơn giản hơn để triển khai và xác định đồng nhất hơn. Nó có vẻ ngu ngốc, nhưng toàn bộ tên mangling đối phó là một chút xấu xí hack IMHO; và bạn không được dự kiến ​​sẽ sử dụng các tên như vậy cho bất cứ điều gì ngoại trừ thuộc tính/phương pháp anyway.

Spam().egg(_Spam__a=1), cũng như Spam().egg(1), không hoạt động. Nhưng mặc dù bạn có thể làm cho nó hoạt động, dấu gạch dưới dẫn đầu (bất kỳ số nào trong số chúng) không có vị trí trong tên thông số. Hoặc trong bất kỳ biến địa phương nào (ngoại lệ: _) cho vấn đề đó.

Chỉnh sửa: Dường như bạn đã tìm thấy trường hợp góc không ai xem xét. Tài liệu là không chính xác ở đây, hoặc việc thực hiện là thiếu sót. Nó xuất hiện tên đối số từ khóa không bị xáo trộn. Nhìn vào bytecode (Python 3.2):

>>> dis.dis(Spam.egg) 
    3   0 LOAD_FAST    0 (self) 
       3 LOAD_ATTR    0 (_egg) 
       6 LOAD_CONST    1 ('__a') # keyword argument not mangled 
       9 LOAD_FAST    1 (a) 
      12 CALL_FUNCTION   256 
      15 RETURN_VALUE 
>>> dis.dis(Spam._egg) 
    2   0 LOAD_FAST    1 (_Spam__a) # parameter mangled 
       3 RETURN_VALUE 

này có thể được bắt nguồn từ thực tế là đối số từ khóa tương đương với đi qua một dict (trong trường hợp này {'__a': 1}) có phím sẽ không được đọc sai một trong hai. Nhưng thành thật mà nói, tôi chỉ gọi nó là một trường hợp góc xấu xí trong một trường hợp đặc biệt xấu xí và tiếp tục. Nó không quan trọng bởi vì bạn không nên sử dụng định danh như vậy anyway.

+0

Nếu nó xảy ra "không phân biệt nơi chúng xảy ra", tôi hy vọng các ví dụ cuối cùng (người tôi thêm) hoạt động. Tôi đoán tôi biết tại sao nó không mặc dù; nó phải ở trong "không gian tên lớp" hoặc một cái gì đó như thế (tôi không biết từ thích hợp cho điều đó) là gì, đúng không? – tkf

+0

Tôi cũng mong đợi điều đó và tôi khá ngạc nhiên. Tôi đoán đó là một lỗ hổng trong tài liệu hoặc triển khai. Tôi đang chỉnh sửa ngay bây giờ để thêm các phát hiện của mình. – delnan

+0

Tôi đồng ý rằng nó không phải là thông số thanh lịch, nhưng hãy xem xét khi bạn có một cái gì đó như 'tự .__ dict __. Cập nhật (kwds)' trong chức năng nhưng muốn kiểm soát hành vi của hàm. Tên đối số hợp lệ duy nhất mà tôi có thể nghĩ đến để kiểm soát hành vi là hành vi bắt đầu bằng dấu gạch dưới kép. – tkf

3

Nó được chuyển đổi sang _Spam__a:

In [20]: class Spam(object): 
    ....:  
    ....:   def egg(self, __a=None): 
    ....:    return __a 
    ....: 

In [21]: Spam.egg.__func__.__code__.co_varnames 
Out[21]: ('self', '_Spam__a') 
1

đúp gạch dưới hoặc Name Mangling trong một bối cảnh lớp học là một định danh cá nhân. Trong ví dụ của bạn hãy thử dir (Spam.egg) và bạn sẽ thấy rằng tham số __a bây giờ là _Spam__egg.

Bây giờ bạn có thể sử dụng:

Spam().egg(_Spam__a=1) 
Các vấn đề liên quan