仅将关键字参数传递给__new __()并且永远不会将其传递给__init __()?

Par*_*ham 6 python mocking

第1部分

我有一个设置,我有一组我想要模拟的类,我的想法是,在我想要这样做的情况下,我将一个mock关键字参数传递给构造函数并__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
Run Code Online (Sandbox Code Playgroud)

然后我像这样调用构造函数:

ro = RealObject(mock = bool)
Run Code Online (Sandbox Code Playgroud)

我这里的问题是,当我得到下面的错误boolFalse:

TypeError: __init__() got an unexpected keyword argument 'mock'

如果我mock作为关键字参数添加__init__但是我要问的是否可以避免这种情况.我甚至mock从中弹出kwargs dict.

也是关于设计的问题.有一个更好的方法吗?(当然!)我想尝试这样做,不使用工厂或超类或任何东西.但是,我还应该使用其他关键字吗?__call__

第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
Run Code Online (Sandbox Code Playgroud)

我在一个单独的模块中定义了类RealObjectMockRealObject.我现在有两个问题:

  1. 如果MockableMetaclassMockable不在同一个模块中,如果我提供的话RealObject,eval将提出一个.NameErrormock = True
  2. 如果mock = False代码将进入无休止的递归,最终以令人印象深刻的方式结束RuntimeError: maximum recursion depth exceeded while calling a Python objec.我猜这是因为RealObject超级课程不再是object,而是Mockable.

我该如何解决这些问题?我的方法不正确吗?我应该Mockable作为装饰者吗?我尝试过,但这似乎没有用,因为__new__一个实例似乎只是只读.

jsb*_*eno 9

这是一份工作__metaclass__ :-)

负责调用这两者__new__以及__init__实例化Python新样式对象的代码位于__call__类元类的方法中.(或在语义上等同于).

换句话说 - 当你这样做时:

RealObject()- 真正称之为RealObject.__class__.__call__方法.由于没有声明显式元类,元类是type,它type.__call__被调用.

大多数处理元类的__new__方法都涉及对方法进行子类化- 在创建类时自动执行操作.但是,重写__call__我们可以在实例化类时执行操作.

在这种情况下,所需的只是在调用之前删除"mock"关键字参数(如果有)__init__:

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
    ...
Run Code Online (Sandbox Code Playgroud)