回答:
新しいスタイルのクラスのインスタンスのみがプロパティを持つことができます。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()
types.ModuleType
のその他の非常によく似た答えに示されているように、インスタンスのクラスを派生させることには特別な利点がありますか?
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()によって正しく識別されるようにするためです。
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
__file__
、手動で定義する必要があるような他の属性も想定している可能性があります(2)クラスを含むモジュールで行われたインポートは、実行時に「見えない」など...
types.ModuleType
、任意の(新しいスタイル)クラスが行います。継承したい特別なモジュール属性は何ですか?
__init__
インスタンスのときにモジュール名を指定でき、を使用すると正しい動作が得られますisinstance
。
PEP 562として Pythonで実装されています> = 3.7、今、私たちはこれを行うことができます
ファイル:module.py
def __getattr__(name):
if name == 'y':
return 3
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
other = 4
使用法:
>>> import module
>>> module.y
3
>>> module.other
4
>>> module.nosuch
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "module.py", line 4, in __getattr__
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
AttributeError: module 'module' has no attribute 'nosuch'
あなたは省略することに注意raise AttributeError
して__getattr__
機能、それが持つ機能の終了を意味しreturn None
、その後、module.nosuch
の値を取得しますNone
。
ジョン・リンの答えに基づいて:
def module_property(func):
"""Decorator to turn module functions into properties.
Function names must be prefixed with an underscore."""
module = sys.modules[func.__module__]
def base_getattr(name):
raise AttributeError(
f"module '{module.__name__}' has no attribute '{name}'")
old_getattr = getattr(module, '__getattr__', base_getattr)
def new_getattr(name):
if f'_{name}' == func.__name__:
return func()
else:
return old_getattr(name)
module.__getattr__ = new_getattr
return func
使用方法(先頭のアンダースコアに注意)、中the_module.py
:
@module_property
def _thing():
return 'hello'
次に:
import the_module
print(the_module.thing) # prints 'hello'
プロパティ化された関数を元の関数と区別するには、先頭のアンダースコアが必要です。識別子を再割り当てする方法を考えることができませんでした。デコレータの実行時に識別子がまだ割り当てられていないためです。
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インポートフックなしでそれを行います。
# 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
proxy_tools
proxy_tools
提供するために、パッケージの試み@module_property
機能を。
それはとインストールします
pip install proxy_tools
@Mareinの例を少し変更しthe_module.py
て、
from proxy_tools import module_property
@module_property
def thing():
print(". ", end='') # Prints ". " on each invocation
return 'hello'
今、別のスクリプトから、私はすることができます
import the_module
print(the_module.thing)
# . hello
この解決策には警告がないわけではありません。つまり、文字列でthe_module.thing
はありません。これは、proxy_tools.Proxy
文字列を模倣するように特別なメソッドがオーバーライドされたオブジェクトです。ポイントを説明するいくつかの基本的なテストはここにあります:
res = the_module.thing
# [No output!!! Evaluation doesn't occur yet.]
print(type(res))
# <class 'proxy_tools.Proxy'>
print(isinstance(res, str))
# False
print(res)
# . hello
print(res + " there")
# . hello there
print(isinstance(res + "", str))
# . True
print(res.split('e'))
# . ['h', 'llo']
内部的には、元の関数は次の場所に保存され the_module.thing._Proxy__local
ます。
print(res._Proxy__local)
# <function thing at 0x7f729c3bf680>
正直なところ、モジュールにこの機能が組み込まれていない理由に困惑しています。問題の核心はそれthe_module
がtypes.ModuleType
クラスのインスタンスであると思います。「モジュールプロパティ」を設定すると、クラス自体ではなく、このクラスのインスタンスにプロパティを設定することになりますtypes.ModuleType
。詳細については、この回答を参照してください。
types.ModuleType
結果は素晴らしいものではありませんが、実際には次のようにプロパティを実装できます。組み込み型を直接変更することはできませんが、それらを呪うことはできます。
# python -m pip install forbiddenfruit
from forbiddenfruit import curse
from types import ModuleType
# curse has the same signature as setattr.
curse(ModuleType, "thing2", property(lambda module: f'hi from {module.__name__}'))
これにより、すべてのモジュールに存在するプロパティが提供されます。すべてのモジュールで設定動作を壊すので、少し扱いにくいです。
import sys
print(sys.thing2)
# hi from sys
sys.thing2 = 5
# AttributeError: can't set attribute
cached_module_property
事実__getattr__()
は役に立ちます。(functools.cached_property
達成することと同様)。
__getattr__
は、モジュールを参照してください。