【问题标题】:How to use getattr or getattribute to correctly raise ImportError while calling some methods如何在调用某些方法时使用 getattr 或 getattribute 正确引发 ImportError
【发布时间】:2020-04-27 10:37:16
【问题描述】:

您好,提前感谢他们对我的帮助,

请看下面的代码:

import types

_MSG = ("Failed importing {name}. Please install {name}."
        " Using pip install {name}")

class Empty(): # pylint: disable=too-few-public-methods
    """Empty class for beam API."""
    def __call__(self, *args, **kwargs):
        return None

class DummyBeam(types.ModuleType): # pylint: disable=too-few-public-methods

    DoFn = Empty
    Pipeline = Empty

    def __init__(self, name="apache_beam"):
        super(DummyBeam, self).__init__(name)

    def __getattribute__(self, _):
        if getattr(DummyBeam, _, None) is Empty:

            err_msg = _MSG.format(name=self.__name__)
            raise ImportError(err_msg)

我想检查apache_beam是否未安装,它将成功加载所有光束类,如DoFn和管道,但调用某些函数会引发错误,请参阅下面的代码以查看上面正在使用的代码。

try:
  import apache_beam as beam
except ImportError:
  beam = DummyBeam()

class SomeFn(beam.DoFn):
  pass

class SomeOtherFn(beam.Pipeline):
  pass

SomeFn()

在上面的代码中,现在访问beam.DoFn 会引发错误,但我希望它在访问beam.DoFn 时不会引发错误,尽管它会在调用SomeFn() 时引发错误。还尝试将getattribute 替换为getattr,但它没有给我预期的结果,尽管它对所有代码都运行良好,但它在调用 SomeFn() 时不会引发错误。

感谢您对此进行调查。

【问题讨论】:

    标签: python python-3.x oop pep


    【解决方案1】:

    如回溯中所示(您应该已发布 FWIW),您的错误不在 calling SomeFn() 中,而是在 accessing beam.DoFn @987654323 中@ 类定义。原因很明显:您通过简单的覆盖Beam.__getattribute__ 非常明确地指示Python 这样做。

    请注意,object.__getattribute__ 是属性查找的官方默认实现(每次 Python 看到 obj.name 或 getattr(obj, "name") 时都会调用它,并且除非您完全使用它,否则最好不要管它。了解覆盖它的含义并且没有更好的解决方案。

    在这种情况下,非常明显的解决方案是实现__getattr__,它仅在无法以任何其他方式解析属性时由__getattribute__ 作为最后的手段调用。你这么说:

    用 getattr 替换 getattribute 也不起作用

    但我只是在您的代码 sn-p 上进行了尝试,它(当然)产生了我预期的结果。这是否是 所期望的是另一个问题,但是由于您既没有发布此版本的代码,也没有在意解释它是如何“不工作”的,所以在这一点上您不能指望任何答案(提示:“不工作”是对问题的完全无用描述)。

    最后一点:

    它将成功加载所有光束方法,如 DoFn 和管道 ... 在上面的代码中现在调用 beam.DoFn

    您似乎对术语有些困惑。 DoFnPipeline 是类,而不是方法,并且(如前所述)访问 beam.DoFn 时会引发错误,而不是在调用 时引发错误。

    编辑:

    by 不工作我的意思是当我尝试访问 beam.DoFn 或 SomeFn() 时使用 getattr 而不是 getattribute 时它不会给我错误 (...) 我想要的是在调用 someFn no access beam.DoFn 时引发错误

    好的,看起来您并没有完全了解方法调用表达式的执行顺序。当你这样做时

    obj.method()
    

    这实际上是一个快捷方式

    method = obj.__getattribute__("method")
    method.__call__()
    

    所以覆盖 __getattribute__ 不是正确的解决方案(参见上文),并且在这里定义 __getattr__ 是没用的 - 你的 DummyBeam 类具有 DoFnPipeline 属性,所以 __getattr__ 不会是为这些名称调用。

    现在调用beam.DoFnbeam.Pipeline 时没有出现任何异常的原因是这些名称绑定到您的Empty ,而不是该类的实例,所以您实际上从不调用Empty.__call__。类中定义的 __call__ 方法仅在调用该类的 instance 时使用,而不是在您实例化该类时使用(在这种情况下,调用的是元类的 __call__ 方法):

    >>> class MyCallable:    
    ...     def __init__(self):
    ...         print("in MyCallable.__init__")
    ...     def __call__(self):
    ...         print("in MyCallable.__call__")
    ... 
    >>>  
    ... c = MyCallable()
    in MyCallable.__init__
    >>> c()
    in MyCallable.__call__
    >>> 
    

    因此,如果您想要在有人尝试实例化 DoFn 或 ̀Pipelineyou either have to make them instances ofEmptyor keep them as they are and renameEmpty 时提出异常。致电to空。newwhich is the first method called bytype.call(type` 是所有类的默认元类)。

    【讨论】:

    • 感谢您解释 by 不起作用正确的话,我想要的是在调用 someFn no access beam.DoFn 时引发错误,如果您提供一些解决方案,它将有所帮助。上面的代码在访问 beam.DoFn 时引发错误,但在调用 someFn() 时想引发错误
    • 很好解释,非常感谢,我通过在 Empty 类中定义 new 而不是 call 并在那里引发异常工作正常,所以有不需要 types.ModuleType?我想在引发错误时使用self.__name__,所以它使用“apache_beam”,因为它现在使用 SomeFn 作为错误名称
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-03
    • 1970-01-01
    • 1970-01-01
    • 2014-04-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多