Могут ли модули иметь свойства так же, как объекты? - PullRequest
83 голосов
/ 19 мая 2009

Со свойствами Python я могу сделать так, чтобы

obj.y 

вызывает функцию, а не просто возвращает значение.

Есть ли способ сделать это с модулями? У меня есть случай, когда я хочу

module.y 

для вызова функции, а не просто для возврата хранящегося там значения.

Ответы [ 3 ]

55 голосов
/ 19 мая 2009

Только экземпляры классов нового стиля могут иметь свойства. Вы можете заставить Python поверить, что такой экземпляр является модулем, сохранив его в sys.modules[thename] = theinstance. Так, например, ваш файл модуля m.py может быть:

import sys

class _M(object):
    def __init__(self):
        self.c = 0
    def afunction(self):
        self.c += 1
        return self.c
    y = property(afunction)

sys.modules[__name__] = _M()
49 голосов
/ 19 мая 2009

Я бы сделал это для того, чтобы правильно наследовать все атрибуты модуля и быть правильно идентифицирован isinstance ()

import types

class MyModule(types.ModuleType):
    @property
    def y(self):
        return 5


>>> a=MyModule("test")
>>> a
<module 'test' (built-in)>
>>> a.y
5

И затем вы можете вставить это в sys.modules:

sys.modules[__name__] = MyModule(__name__)  # remember to instantiate the class
5 голосов
/ 16 января 2016

Типичный вариант использования: обогащение (огромного) существующего модуля некоторыми (несколькими) динамическими атрибутами - без превращения всех компонентов модуля в макет класса. К сожалению, самый простой патч класса модуля, такой как sys.modules[__name__].__class__ = MyPropertyModule, не работает с TypeError: __class__ assignment: only for heap types. Поэтому создание модуля необходимо перемонтировать.

Этот подход делает это без хитов импорта Python, просто имея некоторый пролог поверх кода модуля:

# propertymodule.py
""" Module property example """

if '__orgmod__' not in globals():

    # constant prolog for having module properties / supports reload()

    print "PropertyModule stub execution", __name__
    import sys, types
    class PropertyModule(types.ModuleType):
        def __str__(self):
            return "<PropertyModule %r from %r>" % (self.__name__, self.__file__)
    modnew = PropertyModule(__name__, __doc__)
    modnew.__modclass__ = PropertyModule        
    modnew.__file__ = __file__
    modnew.__orgmod__ = sys.modules[__name__]
    sys.modules[__name__] = modnew
    exec sys._getframe().f_code in modnew.__dict__

else:

    # normal module code (usually vast) ..

    print "regular module execution"
    a = 7

    def get_dynval(module):
        return "property function returns %s in module %r" % (a * 4, module.__name__)    
    __modclass__.dynval = property(get_dynval)

Использование:

>>> import propertymodule
PropertyModule stub execution propertymodule
regular module execution
>>> propertymodule.dynval
"property function returns 28 in module 'propertymodule'"
>>> reload(propertymodule)   # AFTER EDITS
regular module execution
<module 'propertymodule' from 'propertymodule.pyc'>
>>> propertymodule.dynval
"property function returns 36 in module 'propertymodule'"

Примечание: что-то вроде from propertymodule import dynval, конечно, даст замороженную копию - соответствует dynval = someobject.dynval

...