在Python中写一个无操作或伪类

6 投票
4 回答
11348 浏览
提问于 2025-04-18 14:42

假设我有这样的代码:

foo = fooFactory.create()

出于各种原因,fooFactory.create() 可能无法创建一个 Foo 的实例。

如果创建失败,我希望 fooFactory.create() 返回一个虚假的/无操作的对象。这个对象应该完全无效——无论怎么使用,它都不应该做任何事情,也不会抛出任何异常。需要注意的是,foo 没有返回值的方法。


我考虑了以下几种选择。

首先,创建一个模拟对象。这样做的好处是简单明了,正好能满足我的需求。坏处是,在生产代码中使用模拟对象感觉有点奇怪。更重要的是,我无法控制这个模拟库,所以它的行为可能会在某个时候发生变化,从而导致我的应用程序出错。

第二,创建一个虚拟对象 NoopFoo / DummyFoo 类。我会手动实现它需要支持的方法,并在方法体内简单地写上 pass。这样做的好处是我知道它不会导致我的应用程序出错。坏处是,如果将来使用了 Foo 的其他方法,我就得知道去更新 NoopFoo / DummyFoo,否则我的应用程序可能会出错。

有没有比这两种选择更好的方法呢?请注意,我是 Python 新手,如果涉及到更高级的 Python 特性,希望能多给点信息。谢谢!

4 个回答

1

考虑一下 nop 这个包,可以通过 pip install nop 来安装。请不要在其他地方使用 mock 包(除了测试之外),因为它会记录调用历史!

from nop import NOP

dummy = NOP()

dummy.foo()
NOP
dummy.foo().bar()
NOP
dummy.foo('x', 'y')
NOP
dummy.foo('x', 'y', z=3)
NOP

type(_)
<class 'nop.nop_base.NOP'>

它的源代码可以在 这里 找到。

5

之前的回答提到的那些虚拟对象在处理嵌套属性或方法时并不好用。这里是我的解决方案:

class Dummy:

    def __init__(*args, **kwargs):
        pass

    def __call__(self, *args, **kwargs):
        return self

    def __getattr__(self, *args, **kwargs):
        return self

现在我们可以访问嵌套的属性和方法了:

>>> d = Dummy()

>>> d.my_attr
<__main__.Dummy object at 0x10d007160>

>>> d.my_method()
<__main__.Dummy object at 0x10d007160>

>>> d.my_attr.my_method()
<__main__.Dummy object at 0x10d007160>

>>> d.my_attr.my_other_attr
<__main__.Dummy object at 0x10d007160>
10

这个怎么样:

class MayBeCalled(object):
    def __call__(self, *args, **kwargs):
        return None

class Dummy(object):
    def __getattr__(self, attr):
        return MayBeCalled()

    def __setattr__(self, attr, val):
        pass

a = Dummy()

print a.nn
<__main__.MayBeCalled object at 0x103ca9a90>

print a.nn()
None

a.nn = 23
print a.nn()

None

一个“虚拟”对象在你访问它的任何属性时,什么都不做。

15

你问的是一个什么都不做的对象。object的实例正好就是这样。

def create():
    return object()

另一方面,你可能希望它能做点什么。你可能希望它有一些方法,这些方法执行后返回None。你可以返回这个类的一个实例:

In [1]: class Nop(object):
   ...:     def nop(*args, **kw): pass
   ...:     def __getattr__(self, _): return self.nop
   ...:     

In [2]: n=Nop()

In [3]: n.foo
Out[3]: <bound method Nop.nop of <__main__.Nop object at 0x7f9fec034650>>

In [4]: n.foo()

In [5]: n.foo(1,2,3)

撰写回答