你好,他们提前感谢你对我的帮助,
请参考以下代码:
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和pipeline,但调用一些函数会引发错误,请参阅下面的代码以查看上面的代码。
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()
时会引发错误。我还尝试用getattr
替换getattribute
,它没有像我预期的那样给出结果,它在调用SomeFn()时不会引发错误,尽管它对所有代码都运行得很好。
谢谢你调查这个。
发布于 2020-04-27 11:22:25
正如回溯中所示(您应该已经发布了FWIW),您的错误不在于调用SomeFn()
,而在于访问SomeFn
类定义中的beam.DoFn
。原因很明显:您非常明确地指示Python通过简单地覆盖Beam.__getattribute__
来做到这一点。
请注意,object.__getattribute__
是属性查找的官方默认实现(每当Python看到obj.name
或getattr(obj,"name")时都会调用它,除非您完全了解覆盖它的含义并且没有更好的解决方案,否则最好不要使用它。
在这种情况下,非常明显的解决方案是实现__getattr__
,只有在无法以任何其他方式解析该属性时,__getattribute__
才会将其作为最后的手段来调用。你可以这么说:
还将getattribute替换为getattr不起作用
但我只是在您的代码片段上尝试了一下,它(当然)产生了我所期望的结果。这是否是你所期望的是另一个问题,但由于你既没有发布这个版本的代码,也没有在意解释它是如何“不工作”的,所以在这一点上你不能期待任何答案(提示:“不工作”是一个问题的完全无用的描述)。
作为最后的说明:
它将成功加载所有波束方法,如DoFn和pipeline……在上面的代码中,现在调用beam.DoFn
看起来你对术语有点迷惑。DoFn
和Pipeline
是类,而不是方法,而且(如前所述)错误是在访问beam.DoFn
时引发的,而不是在调用它时引发的。
编辑:
by不工作我的意思是,当我尝试访问beam.DoFn或SomeFn()时,它也不会给我错误,而使用getattr代替getattribute (...)我想要的是在不访问beam.DoFn的情况下调用someFn时引发错误
好的,看起来你不太了解方法调用表达式的执行顺序。当你这样做的时候
obj.method()
这实际上是一个快捷方式
method = obj.__getattribute__("method")
method.__call__()
因此,覆盖__getattribute__
不是正确的解决方案(参见上面),并且定义__getattr__
在这里是无用的-您的DummyBeam
类具有DoFn
和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
或̀Pipelineyou either have to make them instances of
Emptyor keep them as they are and rename
Empty.callto
Empty.newwhich is the first method called by
type.call(
type`时引发异常,请将其作为所有类的默认元类)。
https://stackoverflow.com/questions/61456920
复制