На самом деле я не знаю, как конкретно MagicMock
работает (я никогда не использовал его, но слышал хорошие вещи), но эта часть поведения может быть воспроизведена (наряду с, вероятно, множеством других возможных решений)угоня __getattr__
таким образом, что он возвращает вызываемый объект, который создает новый экземпляр фиктивного файла при вызове:
class MM:
def __init__(self, name=None):
# store a name, TODO: random id, etc.
self.name = name
def __repr__(self):
# make it pretty
if self.name:
r = f'<MM name={self.name}>'
else:
r = f'<MM>'
return r
def __getattr__(self, attrname):
# we want a factory for a mock instance with a name corresponding to attrname
def magicattr():
return MM(name=f"'mock.{attrname}()'")
return magicattr
При выполнении мы видим следующее:
>>> MM()
<MM>
>>> MM().hello()
<MM name='mock.hello()'>
Я не сделалНе стоит забывать об определении id
и еще чего-нибудь, но основной трюк можно увидеть на приведенном выше урезанном примере.
Способ, с которым работает выше, заключается в том, что доступ к .hello
или любому другому атрибуту идетчерез наш пользовательский __getattr__
, который дает нам возможность на лету генерировать фальшивый (насмешливый) метод с любыми свойствами, которые мы хотим.Как я понимаю, одно из многих преимуществ MagicMock
заключается именно в том, что нам не нужно беспокоиться о том, что AttributeError
s выдается по умолчанию, просто работает .