【问题标题】:How can I wrap an empty function to be implemented by a child class in a try-except block?如何将要由子类实现的空函数包装在 try-except 块中?
【发布时间】:2016-01-01 04:22:47
【问题描述】:

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

(删除了 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")

它们是在子类中实现的:

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

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

【问题讨论】:

  • NotImplementedError 是个例外;不要返回它,raise它作为一个例外。
  • (是的,还有一个NotImplemented singleton 对象,它在__add__ 等特殊方法中用作特殊返回值。这不是一个用例对象)。

标签: python oop python-3.x inheritance try-except


【解决方案1】:

NotImplementedError 是个例外;不要返回它,raise它作为一个例外:

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")

您不需要在这里“包装”任何东西。如果子类实现了该方法,则不会调用原方法,不会引发异常。

如果需要进一步处理,并且子类实现是可选的,但对外部 API 不可见,您可以要求子类以不同的名称实现从基类方法调用的方法:

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()方法。

【讨论】:

  • 我认为你写的关于使用私有名称的最后一个建议是 OP 真正追求的。我建议把它放在你的答案中,因为这是最好的部分。
  • @JohnZwinck:我不太确定; base-implementation-with-test-for-concrete-implementation 涉及返回NotImplemented too,这已经是原始基本方法的默认实现。
【解决方案2】:

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

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()

【讨论】:

    【解决方案3】:

    注意:这篇文章包含两种不同的实现技术来满足您的需求。


    间接解决

    解决此问题的最简单方法是重构代码,以便子类不会直接覆盖公共接口使用的函数。

    而是直接在基类中提供公共功能,并让子级覆盖所述函数的“工人”(实现细节),该函数稍后由调用的函数调用“外面”


    示例实现

    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
    


    选择加入保护

    如果您希望保持覆盖基类中提供的公共功能的可能性,同时仍然能够在以后轻松调用函数的 “受保护” 版本,您可以创建一个简单的包装器,如下所示:

    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
    

    【讨论】:

      猜你喜欢
      • 2014-12-31
      • 2016-08-13
      • 2013-11-13
      • 1970-01-01
      • 2011-11-08
      • 1970-01-01
      • 2021-07-14
      • 2013-02-20
      • 1970-01-01
      相关资源
      最近更新 更多