2009-05-19 5 views
69

С свойствами питона, я могу сделать это таким образом, чтоМогут ли модули иметь свойства так же, как объекты могут?

obj.y 

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

Есть ли способ сделать это с помощью модулей? У меня есть случай, когда я хочу, чтобы вызвать функцию, а не просто вернуть сохраненное там значение.

+2

Что не так с вызовом функции? –

+8

хочу сделать это менее типичным, чтобы делать определенные вещи, которые делают много. –

ответ

50

Только экземпляры классов нового стиля могут иметь свойства. Вы можете заставить 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() 

Отредактировано: убрана неявная зависимость от глобал (не имеет ничего общего с точкой примера, но сделали все запутать, делая исходный код не работает!).

+0

Я бы никогда не подумал об этом. Я признаю, что вряд ли буду использовать его, но вы никогда не знаете. Всегда рады узнать о неисследованных проспектах в Python ... спасибо. –

+1

Пробовал ли кто-нибудь еще? Когда я помещаю этот код в один файл x.py и импортирую его из другого, то вызов xy приводит к атрибуту AttributeError: объект «NoneType» не имеет атрибута «c», поскольку _M каким-то образом имеет значение None ... – Stephan202

+0

Вам должно быть что-то неправильно , Проверьте интерактивную сессию: $ питона ... >>> импорта SYS >>> класс _m (объект): ... с = 0 ... четкость прекращения функции (сама): ... _M .c + = 1 ... возвращение _M.c ... y = свойство (afunction) ... >>> sys.Модули [ 'гав'] = _M() >>> импорт гав >>> bowwow.y >>> bowwow.y >>> ... точно так, как и следовало ожидать, конечно. Итак, что изменилось в том, что вы точно делаете? –

44

Я хотел бы сделать это для того, чтобы должным образом наследовать все атрибуты модуля, и быть правильно определены 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 
+0

Это, похоже, работает только в самых простых случаях. Возможные проблемы: (1) некоторые импортные помощники могут также ожидать, что другие атрибуты, такие как '__file__', которые должны быть определены вручную, (2) импорт, сделанный в модуле, содержащем класс, не будет« видимым »во время выполнения и т. Д. – tutuDajuju

+1

Нет необходимости выводить подкласс класса 'types.ModuleType', _any_ (new-style). Точно, какие специальные атрибуты модуля вы надеетесь наследовать? – martineau

+0

Что делать, если исходный модуль является пакетом, и я хочу получить доступ к модулям под исходным модулем? –

1

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