2012-03-30 25 views
23

Tôi đã viết một số trình bao bọc có một đối tượng khác làm thuộc tính. Trình bao bọc này (chuyển tiếp) tất cả các yêu cầu thuộc tính với __getattr____setattr__ đối tượng được lưu trữ làm thuộc tính. Tôi cần những gì khác để cung cấp cho proxy của mình để trình bao bọc trông giống như lớp bọc trong các trường hợp thông thường?Cách giả mạo/proxy một lớp trong Python

Tôi cho rằng tôi cần sửa những thứ như kế thừa, có thể __repr__, ... Tôi cần phải xử lý những gì khác và cách khắc phục thừa kế để instanceof() hoạt động?

EDIT: nỗ lực của tôi để thực hiện một chức năng proxy, tuy nhiên như tôi không hiểu được công thức hoàn toàn, nó không thành công :(

setattr_=object.__setattr__ 
getattr_=object.__getattribute__ 

class Proxy(object): 
    __slots__=["_func", "_params", "_kwargs", "_obj", "_loaded", "__weakref__"] 
    def __init__(self, func, *params, **kwargs): 
     setattr_(self, "_func", func) 
     setattr_(self, "_params", params) 
     setattr_(self, "_kwargs", kwargs) 

     setattr_(self, "_obj", None) 
     setattr_(self, "_loaded", False) 

    def _get_obj(self): 
     if getattr_(self, "_loaded")==False: 
      print("Loading") 
      setattr_(self, "_obj", getattr_(self, "_func")(*getattr_(self, "_params"), **getattr_(self, "_kwargs"))) 
      setattr_(self, "_loaded", True) 

     return getattr_(self, "_obj") 
    # 
    # proxying (special cases) 
    # 
    def __getattribute__(self, name): 
     return getattr(getattr_(self, "_get_obj")(), name) 
    def __delattr__(self, name): 
     delattr(getattr_(self, "_get_obj")(), name) 
    def __setattr__(self, name, value): 
     setattr(getattr_(self, "_get_obj")(), name, value) 

    def __nonzero__(self): 
     return bool(getattr_(self, "_get_obj")()) 
    def __str__(self): 
     return str(getattr_(self, "_get_obj")()) 
    def __repr__(self): 
     return repr(getattr_(self, "_get_obj")()) 

    # 
    # factories 
    # 
    _special_names=[ 
     '__abs__', '__add__', '__and__', '__call__', '__cmp__', '__coerce__', 
     '__contains__', '__delitem__', '__delslice__', '__div__', '__divmod__', 
     '__eq__', '__float__', '__floordiv__', '__ge__', '__getitem__', 
     '__getslice__', '__gt__', '__hash__', '__hex__', '__iadd__', '__iand__', 
     '__idiv__', '__idivmod__', '__ifloordiv__', '__ilshift__', '__imod__', 
     '__imul__', '__int__', '__invert__', '__ior__', '__ipow__', '__irshift__', 
     '__isub__', '__iter__', '__itruediv__', '__ixor__', '__le__', '__len__', 
     '__long__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', 
     '__neg__', '__oct__', '__or__', '__pos__', '__pow__', '__radd__', 
     '__rand__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__', 
     '__repr__', '__reversed__', '__rfloorfiv__', '__rlshift__', '__rmod__', 
     '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', 
     '__rtruediv__', '__rxor__', '__setitem__', '__setslice__', '__sub__', 
     '__truediv__', '__xor__', 'next', 
    ] 

    @classmethod 
    def _create_class_proxy(cls, theclass): 
     """creates a proxy for the given class""" 

     def make_method(name): 
      def method(self, *args, **kw): 
       return getattr(getattr_(self, "_get_obj")(), name)(*args, **kw) 
      return method 

     namespace={} 
     for name in cls._special_names: 
      if hasattr(theclass, name): 
       namespace[name]=make_method(name) 
     return type("%s(%s)"%(cls.__name__, theclass.__name__), (cls,), namespace) 

    def __new__(cls, obj, *args, **kwargs): 
     """ 
     creates an proxy instance referencing `obj`. (obj, *args, **kwargs) are 
     passed to this class' __init__, so deriving classes can define an 
     __init__ method of their own. 
     note: _class_proxy_cache is unique per deriving class (each deriving 
     class must hold its own cache) 
     """ 
     try: 
      cache=cls.__dict__["_class_proxy_cache"] 
     except KeyError: 
      cls._class_proxy_cache=cache={} 
     try: 
      theclass=cache[obj.__class__] 
     except KeyError: 
      cache[obj.__class__]=theclass=cls._create_class_proxy(obj.__class__) 
     ins=object.__new__(theclass) 
     theclass.__init__(ins, obj, *args, **kwargs) 
     return ins 

if __name__=='__main__': 
    def t(x, y): 
     print("Running t") 
     return x+y 

    a=Proxy(t, "a", "b") 
    print("Go") 
    print(a+"c") 
+1

Không quan tâm, tại sao bạn không kế thừa? – brice

+1

Bởi vì wrapper này là nghĩa vụ phải bọc tất cả các loại đối tượng. Nó là một proxy tổng quát. – Gerenuk

+0

Bạn đã thay đổi công thức một chút. Nó trông giống như những gì bạn thực sự muốn là 'functools.partial', nhưng có lẽ không; vấn đề bạn đang thực sự cố gắng giải quyết là gì? – SingleNegationElimination

Trả lời

20

Vấn đề này khá tốt giải quyết bằng công thức này:

Object Proxying (Python recipe)

ý tưởng chung bạn phải tuân theo là hầu hết các phương pháp trên lớp được truy cập thông qua một số sự kết hợp của __getattr____getattribute__ hoặc trên lớp, hoặc trên metaclass, nhưng điều này không áp dụng cho các phương thức đặc biệt python, các phương thức bắt đầu và kết thúc với dấu gạch dưới kép, cho những người được tìm thấy, chúng phải là phương pháp thực tế trên lớp thực tế, không thể ủy quyền thuộc tính.

Phương pháp nào trong số những phương pháp đó bạn phải cung cấp rõ ràng sẽ phụ thuộc vào phương thức nào mà lớp proxy được cung cấp. Các phương pháp đặc biệt bạn cần cung cấp cho isinstance() là các phương pháp __instancecheck__ and __subclasscheck__. cho repr() để hoạt động, bạn cũng phải xác định __repr__() thích hợp trên chính lớp proxy.

+0

Điều đó có vẻ rất thú vị, tuy nhiên tôi thấy rất khó hiểu tất cả các chi tiết: (Tôi đã cố gắng biến nó thành proxy cho các cuộc gọi hàm - xem phần chỉnh sửa. Tuy nhiên, tôi cho rằng mình cần để làm thế hệ tên đặc biệt bằng cách nào đó sau khi đối tượng được khởi tạo, có thể giúp tôi hiểu những gì tôi cần thay đổi không? – Gerenuk

3

Nói chung, bạn có thể sử dụng wrapt thư viện (pypi), mà không những nâng nặng cho bạn:

Module wrapt tập trung rất nhiều vào tính đúng đắn. Do đó, nó vượt quá các cơ chế hiện có như functools.wraps() để đảm bảo rằng các trang trí bảo toàn khả năng introspectability, chữ ký, kiểm tra khả năng ... [...]

Để đảm bảo rằng chi phí càng nhỏ càng tốt, C mô-đun mở rộng được sử dụng cho các thành phần quan trọng về hiệu suất

supports creating custom wrapper classes. Để thêm các thuộc tính của riêng bạn, bạn cần khai báo chúng theo cách mà wrapt không cố gắng chuyển chúng vào cá thể được bao bọc. Bạn có thể:

  • Tiền tố thuộc tính bằng _self_ và thêm các thuộc tính để truy cập
  • Khai báo thuộc tính ở cấp lớp, ngoài việc trong __init__
  • Sử dụng khe, nếu thích hợp cho lớp học của bạn (không được đề cập trong tài liệu), như thế này:

    class ExtendedMesh(ObjectProxy): 
        __slots__ = ('foo') 
    
        def __init__(self, subject): 
         super().__init__(subject) 
         self.foo = "bar" 
    

Nó cũng supports function wrappers, mà migh phù hợp với mục đích của bạn.

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