문제

I need a way to call a function from an abstract method, i.e.

class A(object):

      @abc.abstractmethod
      def method1(self):
           raise Exception("Unimplemented method")

      def method2(self):
           print "method1 finished"

class B(A):
     def method1(self):
         print "executing method1 from class B"

I need a way to automatically call method2 of class A, after the method1 has been executed (It should be done on the class A side - independently from inherited classes).

Is there a nice way of doing this?

도움이 되었습니까?

해결책

You could use a metaclass, which wraps method1 at the time the class is created:

from functools import wraps

class MetaA(type):
    def __new__(meta, name, bases, attr):
        method1 = attr['method1']
        if not getattr(method, '__isabstractmethod__'):
            @wraps(method1)
            def wrapper(self, *args, **kw):
                res = method1(self, *args, **kw)
                self.method2()
                return res
            attr['method1'] = wrapper
        return super(MetaA, meta).__new__(meta, name, bases, attr)

class A(object):
     __metaclass__ = MetaA

     @abc.abstractmethod
     def method1(self):
         raise Exception("Unimplemented method")

     def method2(self):
         print "method1 finished"

This applies what is basically a decorator to a specific method whenever a (sub)class is created.

Another approach, somewhat hackish, is to intercept the method access, but would work. You'd implement a __getattribute__ hook on A that adds a wrapper:

from functools import wraps

class A(object):
     @abc.abstractmethod
     def method1(self):
         raise Exception("Unimplemented method")

     def method2(self):
         print "method1 finished"

     def __getattribute__(self, name):
         obj = super(A, self).__getattribute__(name)
         if name == 'method1':
             @wraps(obj)
             def wrapper(*args, **kw):
                 res = obj()
                 self.method2()
                 return res
             return wrapper
         return obj

Either approach results in:

>>> B().method1()
executing method1 from class B
method1 finished

By using the @functools.wraps() decorator the wrapper maintains several important attributes from the wrapped method, like its name and docstring.

다른 팁

This looks like a job for the Template method pattern, for example:

class A(object):
  def method1(self):
      # do something before
      try:
          self.method1_impl()
      finally:
          # do something after, for example:
          self.method2()

  @abc.abstractmethod
  def method1_impl(self):
      pass

  def method2(self):
      print "method1 finished"


class B(A):
    def method1_impl(self):
        print "executing method1 from class B"

While I'm not a proponent of this style (it tends to become hard to comprehend as the code grows and becomes more complex), this is occasionally used and has right to exist.

When this kind of situation happens, it can usually be solved by overriding "deeper" functionality.

Instead of having class B override method1, make it override method1_subfunction, and call both method1_subfunction and method2 from method1

class A(object):

    def method1(self):
        self.method1_subfunction()
        self.method2()

    @abc.abstractmethod
    def method1_subfunction(self):
        raise Exception("Unimplemented method")

    def method2(self):
        print "method1 finished"

class B(A):
    def method1_subfunction(self):
        print "executing method1 from class B"
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top