2011-07-15 13 views

Trả lời

27

Không có sự khác biệt về Python giữa các thuộc tính và phương thức. Phương pháp chỉ là một thuộc tính, có loại chỉ là instancemethod, điều đó xảy ra là có thể gọi (hỗ trợ __call__).

Nếu bạn muốn thực hiện điều này, phương pháp __getattr__ của bạn sẽ trả về một hàm (lambda hoặc def thông thường, bất kỳ bộ nào bạn cần) và có thể kiểm tra điều gì đó sau cuộc gọi.

+1

Cảm ơn. Tôi tìm thấy [this] (http://venodesigns.net/2010/11/02/emulating-rubys-method_missing-in-python/) trên một chút googling. – missingfaktor

+2

Để tham khảo, liên kết được đề cập xung quanh sự mơ hồ bằng cách xác định danh sách tên thuộc tính cần được coi là phương pháp (có vẻ như nó đánh bại mục đích, vì bạn chỉ có thể _define_ từng phương thức đó và ủy quyền cho một bài). –

4

Python không phân biệt giữa các phương pháp và thuộc tính (a.k.a. "biến mẫu") theo cách Ruby thực hiện. Các phương thức và các thuộc tính đối tượng khác được tra cứu chính xác theo cùng một cách trong Python - thậm chí không Python biết sự khác biệt ở giai đoạn tra cứu. Cho đến khi thuộc tính được tìm thấy, nó chỉ là một chuỗi.

Vì vậy, nếu bạn đang yêu cầu một cách để đảm bảo rằng __getattr__chỉ được gọi là phương pháp, tôi e rằng bạn có thể sẽ không tìm thấy giải pháp thanh lịch. Nhưng thật dễ dàng để chỉ đơn giản trả lại một hàm (hoặc thậm chí là một nhãn hiệu mới dynamically bound method) từ __getattr__.

+1

Đối với lý do tương tự bạn sẽ sử dụng 'method_missing' trong Ruby hoặc' doesNotUnderstand' trong Smalltalk. – missingfaktor

+0

Tôi hiểu tại sao bạn muốn sử dụng '__getattr__'.Tôi chỉ không hiểu tại sao bạn "chỉ muốn chặn các lời gọi phương thức". – senderle

+0

Thiết kế của tôi không yêu cầu chặn truy cập trường, nhưng nó không có vẻ là một trở ngại lớn. Tôi tìm thấy giải pháp được cung cấp bởi @Emil đủ tốt cho các yêu cầu của tôi. – missingfaktor

1

Mặc dù tôi không khuyên bạn nên dùng nó !!!!!!!!!!!!!!!!!!!!!

loại sắp đến gần hơn để triển khai hành vi gọi phương thức đặc biệt cho mỗi tên không tương ứng với thuộc tính/phương pháp có thể gọi. Tất nhiên họ vẫn không thực sự có các không gian tên riêng biệt nên có thể cảm thấy hơi lạ. Nó hoạt động bằng cách ghi đè __getattribute__ hoạt động ở mức thấp hơn sau đó __getattr__ nó cố gắng tìm nạp thuộc tính nếu nó không trả về một phương thức đặc biệt được gọi để gọi với tên mà bạn gọi, nếu nó thành công nếu nó được gọi nếu không kết thúc tốt đẹp kết quả với một đối tượng proxy hoạt động gần như chính xác theo cùng một cách, ngoại trừ nó thực hiện cuộc gọi với phương pháp đặc biệt của bạn.

Nó không cho phép bạn truy cập đối tượng gọi vì tôi không thể nghĩ ra cách tốt để làm điều đó mà không có bộ nhớ bị rò rỉ (đối tượng gọi) nếu nó đã là thuộc tính không thể gọi mà bạn lưu trữ (suy nghĩ duy nhất tôi có thể nghĩ đến là bắt đầu một chuỗi mới xóa nó sau một phút, do đó bạn có thể gọi nó là trừ khi bạn đang sử dụng nó trong một đóng cửa mà sẽ không được hỗ trợ trong trường hợp đó).

Chỉnh sửa: Tôi quên có thể gọi có thể có một số mặt tích cực sai.

phụ thuộc vào thư viện http://pypi.python.org/pypi/ProxyTypes

from peak.util.proxies import ObjectWrapper 
from functools import partial 

def m(name, *args, **kwargs): 
    print(name,repr(args),repr(kwargs)) 


class CallProxy(ObjectWrapper): 
    def __init__(self, obj, m, method_name): 
     ObjectWrapper.__init__(self, obj) 
     object.__setattr__(self, "_method_name", method_name) 
     object.__setattr__(self, "_m", m) 

    def __call__(self, *args, **kwargs): 
     return self._m(self._method_name, *args,**kwargs) 


class Y(object): 
    def __init__(self): 
     self.x = [3] 
    def __getattribute__(self, name): 
     try: 
      val = object.__getattribute__(self, name) 
      if not callable(val): 
       return CallProxy(val, m, name) 
      else: 
       return val 
     except AttributeError: 
      return partial(m, name) 

In [2]: y=Y() 

In [3]: y.x 
Out[3]: [3] 

In [4]: y.z 
Out[4]: <functools.partial at 0x2667890> 

In [5]: y.zz([12]) 
('zz', '([12],)', '{}') 

In [6]: y.x.append(5) 

In [7]: y.x 
Out[7]: [3, 5] 

In [8]: y.x(1,2,3,key="no") 
('x', '(2, 3)', "{'key': 'no'}") 
3

Bạn có thể thực hiện một missing_method như tính năng theo cách dưới đây:

https://gist.github.com/gterzian/6400170

import unittest 
from functools import partial 

class MethodMissing: 
    def method_missing(self, name, *args, **kwargs): 
     '''please implement''' 
     raise NotImplementedError('please implement a "method_missing" method') 

    def __getattr__(self, name): 
     return partial(self.method_missing, name) 


class Wrapper(object, MethodMissing): 
    def __init__(self, item): 
     self.item = item 

    def method_missing(self, name, *args, **kwargs): 
     if name in dir(self.item): 
      method = getattr(self.item, name) 
      if callable(method): 
       return method(*args, **kwargs) 
      else: 
       raise AttributeError(' %s has not method named "%s" ' % (self.item, name)) 


class Item(object): 
    def __init__(self, name): 
     self.name = name 

    def test(self, string): 
     return string + ' was passed on' 


class EmptyWrapper(object, MethodMissing): 
    '''not implementing a missing_method''' 
    pass 


class TestWrapper(unittest.TestCase): 
    def setUp(self): 
     self.item = Item('test') 
     self.wrapper = Wrapper(self.item) 
     self.empty_wrapper = EmptyWrapper() 

    def test_proxy_method_call(self): 
     string = self.wrapper.test('message') 
     self.assertEqual(string, 'message was passed on') 

    def test_normal_attribute_not_proxied(self,): 
     with self.assertRaises(AttributeError): 
      self.wrapper.name 
      self.wrapper.name() 

    def test_empty_wrapper_raises_error(self,): 
     with self.assertRaises(NotImplementedError): 
      self.empty_wrapper.test('message') 


if __name__ == '__main__': 
    unittest.main()