我如何包装一个由 child class 实现的空函数在 try-except 块中?

How can I wrap an empty function to be implemented by a child class in a try-except block?

我正在创建一个抽象网络爬虫 class 爬虫。 此 class 只能与实现某些方法的 child class 一起使用。

(删除了 init 方法,因为它不相关)

class Crawler():
    def get_image_source_url(self, image_page_soup):
        return NotImplementedError("method get_image_source_url must be implemented")

    def get_image_thumbnail_url(self, image_page_soup):
        return NotImplementedError("method get_image_thumbnail_url must be implemented")

    def get_tags_container(self, image_page_soup):
        return NotImplementedError("method get_tags_container must be implemented")

它们在 child class:

class LittlevisualsCrawler(Crawler):
    def get_image_containers(self, image_page_soup):
        return image_page_soup.find('article', class_='photo')

    def get_image_source_url(self, image_page_soup):
        return image_page_soup.find('img')['src']

    def get_image_thumbnail_url(self, image_page_soup):
        return image_page_soup.find('img')['data-1280u']

    def get_tags_container(self, image_page_soup):
        return image_page_soup.find('ul', class_='tags') 

我想将函数包装在 try-except 块中,无论它是如何实现的。所以最终结果将是:(伪代码)

class Crawler():
    def get_image_source_url(self, image_page_soup):
        if not_implemented:
            return NotImplementedError("method get_image_thumbnail_url must be implemented")
        else:
            try:
            whatever the child class chooses to do
            except Exception:
            handle exception however i decide in the parent class

我不知道如何检查它是否已实现以及如何在没有 child class 覆盖所有内容的情况下包装函数,我是否清楚我要实现的目标?

NotImplementedError是个例外;不要return它,提出它作为例外:

class Crawler():
    def get_image_source_url(self, image_page_soup):
        raise NotImplementedError("method get_image_source_url must be implemented")

    def get_image_thumbnail_url(self, image_page_soup):
        raise NotImplementedError("method get_image_thumbnail_url must be implemented")

    def get_tags_container(self, image_page_soup):
        raise NotImplementedError("method get_tags_container must be implemented")

您不需要 'wrap' 此处的任何内容。如果 subclass 实现了该方法,则不会调用原始方法,也不会引发任何异常。

如果需要进一步处理,并且 subclass 实现是可选的但不应该对外部可见 API,您可以要求 subclasses 实现一个方法使用不同的名称,从基础 class 方法调用:

class Crawler():
    def _image_source_url_implementation(self, image_page_soup):
        raise NotImplementedError("method get_image_source_url must be implemented")

    def get_image_source_url(self, image_page_soup):
        try:
            url = self._image_source_url_implementation(image_page_soup)
        except NotImplementedError:
            # do something default
            url = 'something else'
        # process the produced URL further
        return processed_result

这里 self.get_image_source_url() 委托 一个可选的 self._image_source_url_implementation() 方法。

如果你想将子类型的实现包装在某些东西中,那么你需要使用不同的方法名称。例如:

class Crawler:
    def get_image_source_url(self, image_page_soup):
        try:
            self._get_image_source_url(image_page_soup)
        except NotImplementedError:
            raise
        except Exception:
            print('Some exception occurred, fall back to something else')
            # …

    def _get_image_source_url(self, image_page_soup):
        raise NotImplementedError()

class ChildCrawler(Crawler):
    def _get_image_source_url(self, image_page_soup):
        doStuff()

Note: This post contains two different implementation techniques to allow for what you want.


间接解决

解决此问题的最简单方法是重构代码,使 child class 不会直接覆盖 public 接口使用的函数。

而是直接在 base-class 中提供 public 功能,并使 children 覆盖 "worker"(实现细节) 之后被调用的函数调用的函数 "from the outside".


示例实现

class Base (object):
    def get_message (self):
        try:
            return self.get_message_impl ()
        except Exception as detail:
            print ("error:", detail)    
        return None

    def get_message_impl (self):
        raise Exception ("Not Implemented")
class Foo (Base):
    def get_message_impl (self):
        return "Hello World";
class Bar (Base):
    def get_message_impl (self):
        raise Exception ("Bar.get_message_impl always fails!")
f = Foo ()
b = Bar ()

f_msg = f.get_message ()
b_msg = b.get_message ()

print ("f_msg:", f_msg)
print ("b_msg:", b_msg)

输出

error: Bar.get_message_impl always fails!
f_msg: Hello World
b_msg: None


opt-in保护

如果您想保持覆盖基础 class 中提供的 public 功能的可能性,同时仍然能够轻松调用 "protected" 版本的功能,您可以创建一个简单的包装器,如下所示:

class Base (object):
    class Protected (object):
        def __init__ (self, target):
            self.target = target

        def get_message (self):
            try:
                return self.target.get_message ()
            except Exception as detail:
                print ("error:", detail)
            return None

    def __init__ (self):
        self.protected = self.Protected (self)

    def get_message (self):
        raise Exception ("Not Implemented")
class Foo (Base):
    def get_message (self):
        return "Hello World";
class Bar (Base):
    def get_message (self):
        raise Exception ("Bar.get_message_impl always fail!")
f = Foo ()
b = Bar ()

f_msg = f.protected.get_message () # protected from failure
b_msg = b.protected.get_message () # protected from failure

b_msg = b.get_message ()           # will raise exception