С помощью свойств Python я могу сделать так, чтобы
obj.y
вызывает функцию, а не просто возвращает значение.
Есть ли способ сделать это с помощью модулей? У меня есть случай где я хочу
module.y
для вызова функции, а не просто для возврата значения, хранящегося там.
python
properties
python-module
Джош Гибсон
источник
источник
__getattr__
Модуль для более современного решения.Ответы:
Только экземпляры классов нового стиля могут иметь свойства. Вы можете заставить Python поверить, что такой экземпляр является модулем, спрятав его в
sys.modules[thename] = theinstance
. Так, например, ваш файл модуля m.py может быть:источник
types.ModuleType
что показано в очень похожем ответе @ Unknown?builtins.module
, которые сами по себе являются экземпляромtype
(что является определением класса нового стиля). Проблема заключается в том, что свойства должны быть в классе, а не например: если вы это сделаетеf = Foo()
,f.some_property = property(...)
он будет не в состоянии точно так же , как если бы вы наивный поставить его в модуле. Решение состоит в том, чтобы поместить его в класс, но поскольку вы не хотите, чтобы все модули обладали этим свойством, вы создаете подкласс (см. Ответ Неизвестного).globals()
(сохранение ключей без изменений, но сброс значенийNone
) при повторной привязке имени является проблемойsys.modules
Python 2 - Python 3.4 работает по назначению. Если вам нужен доступ к объекту класса в Py2, добавьте, например,_M._cls = _M
сразу послеclass
оператора (или сохраните его эквивалентным образом в каком-либо другом пространстве имен) и получите доступ к нему, какself._cls
в методах, требующих этого (type(self)
может быть в порядке, но не если вы также выполняете какое-либо подклассирование_M
) .Я бы сделал это, чтобы правильно унаследовать все атрибуты модуля и правильно идентифицировать isinstance ()
А затем вы можете вставить это в sys.modules:
источник
__file__
которые должны быть определены вручную, (2) импорт, сделанный в модуле, содержащем класс, не будет «видимым» во время выполнения и т. Д.types.ModuleType
, любой (новый стиль) класс будет делать. Какие именно атрибуты специального модуля вы надеетесь унаследовать?__init__
создании экземпляра, и вы получите правильное поведение при использованииisinstance
.Поскольку PEP 562 был реализован в Python> = 3.7, теперь мы можем это сделать
файл: module.py
использование:
Обратите внимание: если вы опустите
raise AttributeError
в__getattr__
функции, это означает, что функция заканчивается наreturn None
, тогдаmodule.nosuch
будет получено значениеNone
.источник
На основе ответа Джона Линя :
Использование (обратите внимание на подчеркивание в начале) в
the_module.py
:Затем:
Верхнее подчеркивание необходимо, чтобы отличать функцию с указанием свойств от исходной функции. Я не мог придумать способ переназначить идентификатор, так как во время выполнения декоратора он еще не был назначен.
Обратите внимание, что IDE не будут знать, что свойство существует, и будут отображать красные волны.
источник
@property def x(self): return self._x
я думаю, чтоdef thing()
без подчеркивания более условно. И можете ли вы создать в своем ответе декоратор «средство задания свойств модуля»?def thing()
предложение. Проблема в том, что вызывается__getattr__
только для отсутствующих атрибутов . Но после того, как@module_property def thing(): …
работает,the_module.thing
определяется, так GetAttr никогда не будет называться. Нам нужно как-то зарегистрироватьсяthing
в декораторе, а затем удалить его из пространства имен модуля. Я пробовал вернутьсяNone
из декоратора, но потомthing
определяется какNone
. Можно было бы сделать,@module_property def thing(): … del thing
но я нахожу это хуже, чем использованиеthing()
в качестве функции__getattribute__
». Спасибо.Типичный вариант использования: обогащение (огромного) существующего модуля некоторыми (небольшими) динамическими атрибутами - без превращения всего модуля в макет класса. К сожалению , самый простой модуль класса патч , как
sys.modules[__name__].__class__ = MyPropertyModule
терпит неудачу сTypeError: __class__ assignment: only for heap types
. Так что создание модуля нужно переделать.Этот подход делает это без перехватчиков импорта Python, просто имея некоторый пролог поверх кода модуля:
Использование:
Примечание: что-то вроде
from propertymodule import dynval
, конечно, создаст замороженную копию - соответствующуюdynval = someobject.dynval
источник
Короткий ответ: используйте
proxy_tools
proxy_tools
Пакет пытается обеспечить@module_property
функциональные возможности .Он устанавливается с
Используя небольшую модификацию примера @Marein,
the_module.py
мы помещаемТеперь из другого сценария я могу сделать
Неожиданное поведение
Это решение не лишено недостатков. А именно
the_module.thing
это не строка ! Этоproxy_tools.Proxy
объект, специальные методы которого были переопределены, чтобы имитировать строку. Вот несколько основных тестов, которые иллюстрируют это:Внутри исходная функция сохраняется в
the_module.thing._Proxy__local
:Дальнейшие мысли
Честно говоря, я сбит с толку, почему в модули не встроена эта функциональность. Я думаю, что суть вопроса в том, что
the_module
это экземплярtypes.ModuleType
класса. Установка «свойства модуля» сводится к установке свойства на экземпляре этого класса, а не на самомtypes.ModuleType
классе. Подробнее см. В этом ответе .Фактически мы можем реализовать свойства
types.ModuleType
следующим образом, хотя результаты невелики. Мы не можем напрямую изменять встроенные типы, но можем проклинать их:Это дает нам свойство, которое существует для всех модулей. Это немного громоздко, так как мы нарушаем поведение настроек для всех модулей:
источник
@module_property
примеру, декоратора. Вообще говоря, встроенный@property
декоратор используется, когда класс определен, а не после создания его экземпляра, поэтому я предполагаю, что то же самое будет верно для свойства модуля, и это с ответом Алекса - напомним, этот вопрос задает «Могут ли модули иметь свойства так же, как объекты?». Однако это можно добавить их после этого и я изменил мой предыдущий фрагмент кода , чтобы проиллюстрировать способ , который может быть сделано.cached_module_property
, тот факт, что__getattr__()
он больше не будет вызываться, если атрибут будет определен, полезен. (аналогично тому, что происходитfunctools.cached_property
).