Вызываемые модули

Почему Python не позволяет модулям иметь __call__? (Помимо очевидного, что его было бы непросто импортировать напрямую.) В частности, почему синтаксис a(b) не использует атрибут __call__, как это делает для функций, классов и объектов? (Является ли поиск просто несовместимым для модулей?)

>>> print open("mod_call.py").read()
def __call__():
    return 42

>>> import mod_call
>>> mod_call()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'module' object is not callable
>>> mod_call.__call__()
42

Ответ 1

Специальные методы гарантируют, что они будут неявно называться неявно, если они определены в типе, а не в экземпляре. (__call__ - это атрибут экземпляра модуля mod_call, а не <type 'module'>.) Вы не можете добавлять методы к встроенным типам.

http://docs.python.org/reference/datamodel.html#special-method-lookup-for-new-style-classes

Ответ 2

Python не позволяет модулю переопределять или добавлять какой-либо магический метод, потому что сохранение объектов модуля простым, регулярным и легким просто слишком выгодно, учитывая, как редко появляются сильные примеры использования, где вы можете использовать магические методы.

Когда такие примеры использования появляются, решение состоит в том, чтобы сделать экземпляр класса masquerade как модуль. В частности, введите код mod_call.py следующим образом:

import sys
class mod_call(object):
  def __call__(self):
    return 42
sys.modules[__name__] = mod_call()

Теперь ваш код импорта и вызова mod_call работает нормально.

Ответ 3

Как говорит Майлз, вам нужно определить вызов на уровне класса. Поэтому альтернативой сообщению Alex является изменение класса sys.modules[__name__] на подкласс типа sys.modules[__name__] (это должны быть types.ModuleType).

Это имеет то преимущество, что модуль можно вызывать, сохраняя все остальные свойства модуля (например, доступ к функциям, переменным,...).

import sys

class MyModule(sys.modules[__name__].__class__):
    def __call__(self):  # module callable
        return 42

sys.modules[__name__].__class__ = MyModule

Примечание. Протестировано с помощью python3.6.