首页 > 解决方案 > 如何在调用某些方法时使用 getattr 或 getattribute 正确引发 ImportError

问题描述

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

请看下面的代码:

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(). 还尝试替换getattributegetattr,但它没有像我预期的那样给我结果,尽管它对所有代码都运行良好,但它在调用 SomeFn() 时不会引发错误。

感谢您查看这个。

标签: pythonpython-3.xooppep

解决方案


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

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

在这种情况下,非常明显的解决方案是改为implempent ,如果无法以任何其他方式解析属性__getattr__,它只会作为最后的手段调用。__getattribute__你说:

也用 getattr 替换 getattribute 不起作用

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

最后一点:

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

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

编辑:

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

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

obj.method()

这实际上是一个捷径

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

所以覆盖__getattribute__不是正确的解决方案(参见上面的),__getattr__在这里定义是无用的——你的DummyBeamDoFn有和Pipeline属性,所以__getattr__不会为这些名称调用。

beam.DoFn现在,您在调用时没有得到任何异常的原因beam.Pipeline是这些名称绑定到您的Empty ,而不是该类的实例,因此您实际上从不调用Empty.__call__. 类__call__中定义的方法仅在调用该类的实例时使用,而不是在您实例化该类时使用(在这种情况下,__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或“管道you either have to make them instances of空空”时引发异常or keep them as they are and renameto空。which is the first method called by类型。call( type` 是所有类的默认元类)。


推荐阅读