2012-03-01 39 views
8

Tôi muốn thêm một thuộc tính vào một phương thức thể hiện trong một trong các lớp của tôi. Tôi đã thử câu trả lời được đưa ra trong this question, nhưng câu trả lời này chỉ hoạt động cho các chức năng - theo như tôi có thể nói.Thêm thuộc tính vào các phương thức mẫu trong Python

Như một ví dụ, tôi muốn để có thể làm điều gì đó như:.

class foo(object): 
    ... 
    def bar(self): 
     self.bar.counter += 1 
     return self.bar.counter 
    bar.counter = 1 
    ... 

nhưng, khi tôi gọi foo bar()() tôi nhận được:

AttributeError: 'instancemethod' object has no attribute 'counter' 

Mục tiêu của tôi làm điều này là cố gắng gây ấn tượng rằng biến 'counter' là cục bộ cho phương thức bar(), và cũng để tránh làm lộn xộn không gian tên lớp của chúng ta với một thuộc tính khác. Có cách nào để làm việc này không? Có cách nào khác để làm điều này không?

+0

nên 'counter' được cấp lớp (tất cả các trường chia sẻ số lượng hiện tại), hoặc mức dụ (mỗi trường hợp sẽ bắt đầu từ 1)? –

+0

'truy cập' phải là trường hợp cấp – sbell

+0

Câu hỏi liên quan này có thể (hoặc có thể không) hữu ích: http://stackoverflow.com/questions/7034063/adding-attributes-the-instancemethods-in-python –

Trả lời

8

Trong Python 3 mã của bạn sẽ hoạt động, nhưng trong Python 2 có một số gói diễn ra khi phương pháp được tìm kiếm.

Lớp vs Instance

  • mức lớp: lưu trữ counter với chức năng (hoặc trực tiếp, hoặc bằng cách sử dụng một mặc định có thể thay đổi) có hiệu quả làm cho nó một thuộc tính mức lớp như chỉ có bao giờ một trong những chức năng, không có vấn đề bao nhiêu trường hợp bạn có (tất cả họ chia sẻ cùng một đối tượng chức năng).

  • mức dụ: để làm cho counter mức dụ thuộc tính bạn cần phải tạo ra các chức năng trong __init__, sau đó quấn nó với functools.partial (vì vậy nó hoạt động như một phương pháp thông thường), và sau đó lưu trữ nó trên dụ - bây giờ bạn có một đối tượng hàm cho mỗi cá thể.

Lớp Cấp

Việc thực hành chấp nhận cho một biến tĩnh giống như là sử dụng một đối số mặc định có thể thay đổi:

class foo(object): 
    ... 
    def bar(self, _counter=[0]): 
     _counter[0] += 1 
     return _counter[0] 

Nếu bạn muốn nó được đẹp hơn bạn có thể định nghĩa của riêng bạn có thể thay đổi vùng chứa:

class MutableDefault(object): 
    def __init__(self, start=0): 
     self.value = start 
    def __iadd__(self, other): 
     self.value += other 
     return self 
    def value(self): 
     return self.value 

và thay đổi mã của bạn như vậy:

class foo(object): 
    def bar(self, _counter=MutableDefault()): 
     _counter += 1 
     return _counter.value 

mức Instance

from functools import partial 

class foo(object): 
    def __init__(self): 
     def bar(self, _counter=MutableDefault(1)): # create new 'bar' each time 
      value = _counter.value 
      _counter += 1 
      return value 
     self.bar = partial(bar, self) 

Tóm tắt

Như bạn thấy, dễ đọc mất một hit nghiêm trọng khi di chuyển đến cấp độ dụ cho counter. Tôi đề nghị bạn đánh giá lại tầm quan trọng của việc nhấn mạnh rằng counter là một phần của bar và nếu điều đó thực sự quan trọng có thể làm cho lớp học của riêng mình trở thành một phần của các trường hợp foo. Nếu nó không thực sự quan trọng, hãy làm theo cách thông thường:

class foo(object): 
    def __init__(self): 
     self.bar_counter = 0 
    def bar(self): 
     self.bar_counter += 1 
     return self.bar_counter 
+0

Lưu ý rằng 'MutableDefault' không phải là nơi gần như được thực hiện đầy đủ - đó là trái như một bài tập cho người đọc. :) –

+0

Đối số mặc định chỉ được đánh giá một lần, vì vậy nó là cấp lớp. OP làm rõ trong một nhận xét cho câu hỏi rằng nó phải là thể hiện cấp. –

+0

@ Series8217: Yup, đó là lý do tại sao tôi hỏi câu hỏi - câu trả lời được cập nhật để hiển thị cách thực hiện ở cấp độ cá thể. –

0

Bạn cần một phương pháp __init__ để khởi sự thành viên dữ liệu:

class foo(object): 
    def __init__(self): 
     self.counter = 0 
    def bar(self): 
     self.counter += 1 
     return self.counter 

Gắn giá trị dữ liệu trực tiếp đến chức năng là decidedly phi Pythonic.

+0

Tôi đánh giá cao điều này, nhưng mục tiêu của tôi là buộc biến số truy cập vào phương thức thanh (vì những lý do tôi đã nêu trong câu hỏi của tôi). Tôi đã sửa đổi câu trả lời của bạn để: lớp foo (object): def __init __ (tự): self.bar.counter = 0 def thanh (tự): self.bar.counter + = 1 trở self.bar .counter nhưng bây giờ tôi nhận được cùng một AttributeError trong __init__ gọi – sbell

+0

Không buộc 'counter' vào' bar' như OP được yêu cầu. –

+0

@EthanFurman: OP đã yêu cầu một cách "có nhiều sắc" hơn để thực hiện việc này. Việc đính kèm dữ liệu trực tiếp vào các hàm là ngược lại với Pythonic. –

0

Nếu bạn muốn bộ đếm liên tục trong tất cả các trường hợp, bạn có thể làm như sau, đây vẫn không phải là Pythonic.

class foo(object): 
    def bar(self): 
     self.bar.im_func.counter += 1 
     return self.bar.im_func.counter 
    bar.counter = 0 
2

Xin lỗi vì đã tìm ra bài đăng cũ hơn nhưng thực sự tìm thấy ai đó có giải pháp.

Đây là bài đăng trên blog mô tả sự cố bạn đang gặp phải và cách tạo trình trang trí làm những gì bạn muốn. Không chỉ bạn sẽ nhận được các chức năng bạn cần nhưng tác giả thực hiện một công việc tuyệt vời để giải thích lý do tại sao Python hoạt động theo cách này.

http://metapython.blogspot.com/2010/11/python-instance-methods-how-are-they.html

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