2013-04-29 20 views
6

Lấy lớp sauLàm thế nào để hạn chế tính có sẵn trên một lớp Python

class Person(object): 

def __init__(self, first_name, last_name): 
    self.first_name = first_name 
    self.last_name = last_name 

Làm thế nào tôi có thể ngăn chặn việc sử dụng sau đây?

p1 = Person('Foo', 'Bar') 
p1.firstname='Fooooooo' 

Đoạn mã trên sẽ thực hiện thành công trong Python, tuy nhiên, một sai lầm đã được thực hiện với tên của tài sản, tức là mất tích của nó _ giữa firstname

UPDATE: Điều này nghe như "khỉ Vá ", tại sao tôi muốn làm điều này?

Mục đích của tôi chỉ đơn giản là giúp tránh người dùng đặt sai thuộc tính, thực thi mã và xem hành vi không mong muốn và không nhận biết sai lầm ngay lập tức.

Cách Pythonic đề xuất ở đây là gì?

+0

Tôi nghĩ đây là một ví dụ về những gì mọi người gọi là "vá khỉ", và tôi nghĩ đó là một trong những điều mọi người yêu thích về Python. Nghe có vẻ như bạn đang chiến đấu với ngôn ngữ. – BenDundee

+1

Nếu bạn thực sự phải, '__slots__' có thể đạt được những gì bạn đang tìm kiếm để làm. Tuy nhiên, nó thực sự không nên được sử dụng cho điều này ... Tôi sẽ xem xét rằng '__slots__' lạm dụng. Cuối cùng, đây là lý do tại sao bạn cần cung cấp một API nhất quán và tài liệu tốt. Nếu người dùng gặp lỗi, họ sẽ tìm ra rằng họ không sử dụng đúng API của bạn. – mgilson

+2

@BenDundee: monkeypatching chủ yếu là thêm/sửa đổi hành vi khi chạy. Trong khi dựa vào các cơ chế tương tự, ví dụ trên chắc chắn là một lỗi đánh máy, không phải là một con khỉ;) –

Trả lời

7

Trước hết, gần như luôn là một ý tưởng tồi để làm một việc như vậy. Nếu chỉ có lý do tại sao bạn muốn đó là đảm bảo rằng bạn không làm lỗi chính tả - có những công cụ tốt hơn cho điều đó (nghĩ IDE hoặc pylint). Nếu bạn là 100% tích cực mà bạn cần một điều như vậy, dưới đây là hai cách để làm điều đó:

Cách đầu tiên - bạn có thể làm điều này bằng cách sử dụng phương pháp __setattr__. Xem python __setattr__ documentation

class Person(object): 

    def __init__(self, first_name, last_name): 
     self.__dict__['first_name'] = first_name 
     self.__dict__['last_name'] = last_name 
    def __setattr__(self, name, value): 
     if name in self.__dict__: 
      super(Person, self).__setattr__(name, value) 
     else: 
      raise AttributeError("%s has no attribute %s" %(self.__class__.__name__, name)) 

và đầu ra:

In [49]: a = Person(1, 2) 

In [50]: a.a = 2 
--------------------------------------------------------------------------- 
AttributeError       Traceback (most recent call last) 
/usr/local/lib/python2.7/dist-packages/django/core/management/commands/shell.pyc in <module>() 
----> 1 a.a = 2 

/usr/local/lib/python2.7/dist-packages/django/core/management/commands/shell.pyc in __setattr__(self, name, value) 
     8    super(Person, self).__setattr__(name, value) 
     9   else: 
---> 10    raise AttributeError("%s has no attribute %s" %(self.__class__.__name__, name)) 

AttributeError: Person has no attribute a 

Ngoài ra, bạn có thể làm điều này bằng __slots__ (python __slots__ documentation):

class Person(object): 
    __slots__ = ("first_name", "last_name") 

    def __init__(self, first_name, last_name): 
     self.first_name = first_name 
     self.last_name = last_name 

đầu ra:

In [32]: a = Person("a", "b") 

In [33]: a.first_name 
Out[33]: 'a' 

In [34]: a.a = 1 
--------------------------------------------------------------------------- 
AttributeError       Traceback (most recent call last) 
/usr/local/lib/python2.7/dist-packages/django/core/management/commands/shell.pyc in <module>() 
----> 1 a.a = 1 

AttributeError: 'Person' object has no attribute 'a' 

Cách thứ nhất linh hoạt hơn vì nó cho phép hack mã này hơn nữa bằng cách sử dụng trực tiếp __dict__, nhưng điều đó thậm chí còn sai lệch hơn bây giờ. Cách tiếp cận thứ hai preallocates không gian cho số lượng nhất định các biến dụ (tham khảo), có nghĩa là tiêu thụ bộ nhớ ít hơn.

+0

Trong khi (gần như - điều này nên gọi là 'super .__ setattr__', không phải' super .__ setitem__') về mặt kỹ thuật đúng ("loại tồi tệ nhất"), đây là một ví dụ hoàn hảo về những gì KHÔNG làm trong Python. Chiến đấu với ngôn ngữ chỉ là một sự lãng phí thời gian và một PITA lớn. –

+0

xin lỗi về setitem, đã sửa lỗi đó. Tôi đồng ý rằng hầu hết thời gian nó là sai, nhưng một trường hợp tôi đã sử dụng nó - và thấy nó hữu ích - là tạo ra một ORM (bạn chống lại ngôn ngữ có anyway). Và về lâu dài, nó không lãng phí thời gian. –

+0

+1 cho '__slots__' – Aprillion

Các vấn đề liên quan