Вызываемые модули - PullRequest
47 голосов
/ 30 июня 2009

Почему 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

Ответы [ 4 ]

80 голосов
/ 30 июня 2009

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

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

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

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

34 голосов
/ 30 июня 2009

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

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

8 голосов
/ 04 января 2018

Как говорит Майлз, вам нужно определить вызов на уровне класса. Поэтому альтернативой сообщению Алекса является изменение класса 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.

1 голос
/ 14 марта 2019

Ответ Кристофа Боддекера кажется наилучшим способом создания вызываемого модуля, но, как говорит комментарий , он работает только в Python 3.5 и выше.

Преимущество заключается в том, что вы можете написать свой модуль как обычно и просто добавить переназначение класса в самом конце, т.е.

# coolmodule.py
import stuff

var = 33
class MyClass:
   ...
def function(x, y):
   ...

class CoolModule(types.ModuleType):
    def __call__(self):
        return 42
sys.modules[__name__].__class__ = CoolModule

и все работает, включая все ожидаемые атрибуты модуля, такие как __file__, которые определены. (Это потому, что вы на самом деле вообще не изменяете объект модуля, полученный в результате импорта, просто «приводите» его к подклассу с помощью метода __call__, что в точности соответствует нашему желанию.)

Чтобы это работало аналогично в версиях Python ниже 3.5, вы можете приспособить ответ Алекса Мартелли , чтобы сделать ваш новый класс подклассом ModuleType, и скопировать все атрибуты модуля в ваш новый экземпляр модуля:

#(all your module stuff here)

class CoolModule(types.ModuleType):
    def __init__(self):
       types.ModuleType.__init__(self, __name__)
        # or super().__init__(__name__) for Python 3
        self.__dict__.update(sys.modules[__name__].__dict__)
    def __call__(self):
       return 42

sys.modules[__name__] = CoolModule()

Теперь определены __file__, __name__ и другие атрибуты модуля (которые отсутствуют, если просто следуют за ответом Алекса), и ваш импортированный объект модуля все еще «является» модулем.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...