2011-07-14 37 views
11

Tôi đang cố gắng để viết một trang trí lớp học mà áp dụng một trang trí cho tất cả các lớp phương pháp:Viết một trang trí lớp học mà áp dụng một trang trí cho tất cả các phương pháp

import inspect 


def decorate_func(func): 
    def wrapper(*args, **kwargs): 
     print "before" 
     ret = func(*args, **kwargs) 
     print "after" 
     return ret 
    for attr in "__module__", "__name__", "__doc__": 
     setattr(wrapper, attr, getattr(func, attr)) 
    return wrapper 


def decorate_class(cls): 
    for name, meth in inspect.getmembers(cls, inspect.ismethod): 
     setattr(cls, name, decorate_func(meth)) 
    return cls 


@decorate_class 
class MyClass(object): 

    def __init__(self): 
     self.a = 10 
     print "__init__" 

    def foo(self): 
     print self.a 

    @staticmethod 
    def baz(): 
     print "baz" 

    @classmethod 
    def bar(cls): 
     print "bar" 


obj = MyClass() 
obj.foo() 
obj.baz() 
MyClass.baz() 
obj.bar() 
MyClass.bar() 

Nó gần như làm việc, nhưng @classmethod S cần một điều trị đặc biệt:

$ python test.py 
before 
__init__ 
after 
before 
10 
after 
baz 
baz 
before 
Traceback (most recent call last): 
    File "test.py", line 44, in <module> 
    obj.bar() 
    File "test.py", line 7, in wrapper 
    ret = func(*args, **kwargs) 
TypeError: bar() takes exactly 1 argument (2 given) 

Có cách nào xử lý vấn đề này một cách độc đáo không? Tôi đã kiểm tra @classmethod các phương pháp trang trí, nhưng tôi không thấy bất kỳ điều gì để phân biệt chúng với các loại phương pháp khác.

Cập nhật

Đây là giải pháp hoàn chỉnh cho các hồ sơ (sử dụng mô tả để xử lý @staticmethod S và @classmethod S độc đáo, và lừa aix để phát hiện @classmethod S VS phương pháp bình thường):

import inspect 


class DecoratedMethod(object): 

    def __init__(self, func): 
     self.func = func 

    def __get__(self, obj, cls=None): 
     def wrapper(*args, **kwargs): 
      print "before" 
      ret = self.func(obj, *args, **kwargs) 
      print "after" 
      return ret 
     for attr in "__module__", "__name__", "__doc__": 
      setattr(wrapper, attr, getattr(self.func, attr)) 
     return wrapper 


class DecoratedClassMethod(object): 

    def __init__(self, func): 
     self.func = func 

    def __get__(self, obj, cls=None): 
     def wrapper(*args, **kwargs): 
      print "before" 
      ret = self.func(*args, **kwargs) 
      print "after" 
      return ret 
     for attr in "__module__", "__name__", "__doc__": 
      setattr(wrapper, attr, getattr(self.func, attr)) 
     return wrapper 


def decorate_class(cls): 
    for name, meth in inspect.getmembers(cls): 
     if inspect.ismethod(meth): 
      if inspect.isclass(meth.im_self): 
       # meth is a classmethod 
       setattr(cls, name, DecoratedClassMethod(meth)) 
      else: 
       # meth is a regular method 
       setattr(cls, name, DecoratedMethod(meth)) 
     elif inspect.isfunction(meth): 
      # meth is a staticmethod 
      setattr(cls, name, DecoratedClassMethod(meth)) 
    return cls 


@decorate_class 
class MyClass(object): 

    def __init__(self): 
     self.a = 10 
     print "__init__" 

    def foo(self): 
     print self.a 

    @staticmethod 
    def baz(): 
     print "baz" 

    @classmethod 
    def bar(cls): 
     print "bar" 


obj = MyClass() 
obj.foo() 
obj.baz() 
MyClass.baz() 
obj.bar() 
MyClass.bar() 
+0

lớp DecorativeClassMethod và DecorativeMethod của bạn giống hệt nhau. chỉnh sửa để đặt một giải pháp chính xác xin vui lòng. –

+0

Chúng khác nhau: DecorativeMethod vượt qua đối tượng đối tượng trong khi DecorativeClassMethod thì không. –

+1

Chúng tương tự nhau nên phải kết hợp chúng để tránh trùng lặp. Hãy suy nghĩ dọc theo dòng 'self.func (cls hoặc obj, * args, ** kwargs)'. Tôi biết điều đó không giống nhau nhưng một tuyên bố 'if' đơn giản với bài kiểm tra đúng sẽ cuối cùng giúp bạn tránh khỏi việc có hai lớp gần như giống hệt nhau này. – robru

Trả lời

11

inspect.isclass(meth.im_self) phải cho bạn biết liệu meth là một phương thức lớp học:

def decorate_class(cls): 
    for name, meth in inspect.getmembers(cls, inspect.ismethod): 
     if inspect.isclass(meth.im_self): 
      print '%s is a class method' % name 
      # TODO 
     ... 
    return cls 
+1

+1 bạn đánh bại tôi. – SingleNegationElimination

+0

Nó đã làm các trick, cảm ơn! Tôi đã cập nhật câu hỏi của mình với giải pháp hoàn chỉnh. –

1

(Quá dài cho một bình luận)

tôi lấy sự tự do của việc thêm khả năng để xác định các phương pháp này cần được trang trí với giải pháp của bạn:

def class_decorator(*method_names): 

    def wrapper(cls): 

     for name, meth in inspect.getmembers(cls): 
      if name in method_names or len(method_names) == 0: 
       if inspect.ismethod(meth): 
        if inspect.isclass(meth.im_self): 
         # meth is a classmethod 
         setattr(cls, name, VerifyTokenMethod(meth)) 
        else: 
         # meth is a regular method 
         setattr(cls, name, VerifyTokenMethod(meth)) 
       elif inspect.isfunction(meth): 
        # meth is a staticmethod 
        setattr(cls, name, VerifyTokenMethod(meth)) 

     return cls 

    return wrapper 

Cách sử dụng:

@class_decorator('some_method') 
class Foo(object): 

    def some_method(self): 
     print 'I am decorated' 

    def another_method(self): 
     print 'I am NOT decorated' 
0

Trên đây câu trả lời không áp dụng trực tiếp cho python3. Dựa trên câu trả lời tuyệt vời khác tôi đã có thể đưa ra các giải pháp sau đây:

import inspect 
import types 
import networkx as nx 


def override_methods(cls): 
    for name, meth in inspect.getmembers(cls): 
     if name in cls.methods_to_override: 
      setattr(cls, name, cls.DecorateMethod(meth)) 
    return cls 


@override_methods 
class DiGraph(nx.DiGraph): 

    methods_to_override = ("add_node", "remove_edge", "add_edge") 

    class DecorateMethod: 

     def __init__(self, func): 
      self.func = func 

     def __get__(self, obj, cls=None): 
      def wrapper(*args, **kwargs): 
       ret = self.func(obj, *args, **kwargs) 
       obj._dirty = True # This is the attribute I want to update 
       return ret 
      return wrapper 

    def __init__(self): 
     super().__init__() 
     self._dirty = True 

Bây giờ bất cứ lúc nào một phương pháp trong các tuple methods_to_override được gọi, cờ bẩn được thiết lập. Tất nhiên, bất cứ điều gì khác có thể được đặt ở đó quá. Không cần phải bao gồm lớp DecorateMethod trong lớp mà phương pháp của chúng cần được ghi đè lên. Tuy nhiên, vì DecorateMehod sử dụng các thuộc tính cụ thể cho lớp, tôi muốn tạo thuộc tính lớp.

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