作者:Evilchrist | 来源:互联网 | 2023-09-14 04:25
第1部分我有一个设置,我有一组我想要模拟的类,我的想法是,在我想要这样做的情况下,我将一个模拟关键字参数传递给构造函数,并在__new__中拦截这个,而是传回一个模拟该对象的版本.
第1部分
我有一个设置,我有一组我想要模拟的类,我的想法是,在我想要这样做的情况下,我将一个模拟关键字参数传递给构造函数,并在__new__中拦截这个,而是传回一个模拟该对象的版本.
它看起来像这样(在@mgilsons建议之后编辑了关键字查找):
class RealObject(object):
def __new__(cls, *args, **kwargs):
if kwargs.pop('mock', None):
return MockRealObject()
return super(RealObect, cls).__new__(cls, *args, **kwargs)
def __init__(self, whatever = None):
'''
Constructor
'''
#stuff happens
然后我像这样调用构造函数:
ro = RealObject(mock = bool)
我在这里遇到的问题是当bool为False时出现以下错误:
TypeError:__ init __()得到一个意外的关键字参数’mock’
如果我将mock作为关键字参数添加到__init__,但是我要问的是否可以避免这种情况.我甚至从kwargs dict中弹出模拟.
这也是关于设计的问题.有一个更好的方法吗? (当然!)我想尝试这样做,不使用工厂或超类或任何东西.但是,我还应该使用其他关键字吗? __呼叫__?
第2部分基于jsbueno的回答
所以我想将元类和__new__函数提取到一个单独的模块中.我这样做了:
class Mockable(object):
def __new__(cls, *args, **kwargs):
if kwargs.pop('mock', None):
mock_cls = eval('{0}{1}'.format('Mock',cls.__name__))
return super(mock_cls, mock_cls).__new__(mock_cls)
return super(cls, cls).__new__(cls,*args, **kwargs)
class MockableMetaclass(type):
def __call__(self, *args, **kwargs):
obj = self.__new__(self, *args, **kwargs)
if "mock" in kwargs:
del kwargs["mock"]
obj.__init__(*args, **kwargs)
return obj
我在一个单独的模块中定义了类RealObject和MockRealObject.
我现在有两个问题:
>如果MockableMetaclass和Mockable与RealObject类不在同一个模块中,如果我提供mock = True,eval将引发NameError.
>如果mock = False,代码将进入无休止的递归,以令人印象深刻的RuntimeError结束:调用Python对象时超出了最大递归深度.我猜这是因为RealObject的超类不再是对象而是Mockable.
我该如何解决这些问题?我的方法不正确吗?我应该将Mockable作为装饰者吗?我试过了,但这似乎没有用,因为实例的__new__只是只读的.
解决方法:
这是__metaclass__的工作:-)
在实例化Python新式对象时负责调用__new__和__init__的代码位于类元类的__call__方法中. (或在语义上等同于).
换句话说 – 当你这样做时:
RealObject() – 真正称之为RealObject .__ class __.__ call__方法.
由于没有声明显式元类,元类是类型,因此调用类型.__ call__.
大多数处理元类的方法都涉及子类化__new__method – 创建类时的自动化操作.但是重写__call__,我们可以在实例化类时执行操作.
在这种情况下,只需要在调用__init__之前删除“mock”关键字参数(如果有):
class MetaMock(type):
def __call__(cls, *args, **kw):
obj = cls.__new__(cls, *args, **kw)
if "mock" in kw:
del kw["mock"]
obj.__init__(*args, **kw)
return obj
class RealObject(object):
__metaclass__ = MetaMock
...