基本的に静的変数を取得および設定するための2つのクラスメソッド(classmethod()関数を使用)を持つクラスがあります。これらでproperty()関数を使用しようとしましたが、エラーになります。インタプリタで次のエラーを再現できました。
class Foo(object):
_var = 5
@classmethod
def getvar(cls):
return cls._var
@classmethod
def setvar(cls, value):
cls._var = value
var = property(getvar, setvar)
クラスメソッドをデモンストレーションできますが、プロパティとして機能しません。
>>> f = Foo()
>>> f.getvar()
5
>>> f.setvar(4)
>>> f.getvar()
4
>>> f.var
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: 'classmethod' object is not callable
>>> f.var=5
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: 'classmethod' object is not callable
Classmethod装飾関数でproperty()関数を使用することは可能ですか?
プロパティはクラスで作成されますが、インスタンスに影響します。したがって、classmethodプロパティが必要な場合は、メタクラスでプロパティを作成します。
>>> class foo(object):
... _var = 5
... class __metaclass__(type): # Python 2 syntax for metaclasses
... pass
... @classmethod
... def getvar(cls):
... return cls._var
... @classmethod
... def setvar(cls, value):
... cls._var = value
...
>>> foo.__metaclass__.var = property(foo.getvar.im_func, foo.setvar.im_func)
>>> foo.var
5
>>> foo.var = 3
>>> foo.var
3
しかし、とにかくメタクラスを使用しているため、クラスメソッドをそこに移動するだけで読みやすくなります。
>>> class foo(object):
... _var = 5
... class __metaclass__(type): # Python 2 syntax for metaclasses
... @property
... def var(cls):
... return cls._var
... @var.setter
... def var(cls, value):
... cls._var = value
...
>>> foo.var
5
>>> foo.var = 3
>>> foo.var
3
または、Python 3のmetaclass=...
構文、およびfoo
クラス本体の外部で定義されたメタクラス、および_var
の初期値を設定するメタクラスを使用して:
>>> class foo_meta(type):
... def __init__(cls, *args, **kwargs):
... cls._var = 5
... @property
... def var(cls):
... return cls._var
... @var.setter
... def var(cls, value):
... cls._var = value
...
>>> class foo(metaclass=foo_meta):
... pass
...
>>> foo.var
5
>>> foo.var = 3
>>> foo.var
3
Python 2.2リリース ノートを読むと、次のことがわかります。
プロパティがインスタンス属性(C()。x)ではなくクラス属性(C.x)としてアクセスされる場合、[プロパティの] getメソッドは呼び出されません。クラス属性として使用するときにプロパティの__get__操作をオーバーライドする場合は、プロパティをサブクラス化できます-それは新しいスタイルの型である-その__get__メソッドを拡張するか、新しい型を作成して記述子型を最初から定義できます__get __、__ set__、および__delete__メソッドを定義する-styleクラス。
注:以下のメソッドは、実際にはセッターでは機能せず、ゲッターでのみ機能します。
したがって、規定の解決策は、プロパティのサブクラスとしてClassPropertyを作成することだと思います。
class ClassProperty(property):
def __get__(self, cls, owner):
return self.fget.__get__(None, owner)()
class foo(object):
_var=5
def getvar(cls):
return cls._var
getvar=classmethod(getvar)
def setvar(cls,value):
cls._var=value
setvar=classmethod(setvar)
var=ClassProperty(getvar,setvar)
assert foo.getvar() == 5
foo.setvar(4)
assert foo.getvar() == 4
assert foo.var == 4
foo.var = 3
assert foo.var == 3
ただし、セッターは実際には機能しません。
foo.var = 4
assert foo.var == foo._var # raises AssertionError
foo._var
は変更されず、プロパティを新しい値で上書きしただけです。
ClassProperty
をデコレーターとして使用することもできます。
class foo(object):
_var = 5
@ClassProperty
@classmethod
def var(cls):
return cls._var
@var.setter
@classmethod
def var(cls, value):
cls._var = value
assert foo.var == 5
この非常にシンプルな読み取り専用@classproperty
デコレータは、クラスプロパティを探している人を助けるでしょう。
class classproperty(object):
def __init__(self, fget):
self.fget = fget
def __get__(self, owner_self, owner_cls):
return self.fget(owner_cls)
class C(object):
@classproperty
def x(cls):
return 1
assert C.x == 1
assert C().x == 1
Classmethod修飾された関数でproperty()関数を使用することは可能ですか?
番号。
ただし、classmethodは、そのクラスのインスタンスからアクセス可能なクラスの単なるバインドメソッド(部分関数)です。
インスタンスはクラスの関数であり、インスタンスからクラスを派生できるため、property
を使用して、クラスプロパティから必要な動作を取得できます。
class Example(object):
_class_property = None
@property
def class_property(self):
return self._class_property
@class_property.setter
def class_property(self, value):
type(self)._class_property = value
@class_property.deleter
def class_property(self):
del type(self)._class_property
このコードはテストに使用できます。エラーを発生させることなくパスする必要があります。
ex1 = Example()
ex2 = Example()
ex1.class_property = None
ex2.class_property = 'Example'
assert ex1.class_property is ex2.class_property
del ex2.class_property
assert not hasattr(ex1, 'class_property')
そして、メタクラスはまったく必要なかったことに注意してください-とにかく、そのクラスのインスタンスを介してメタクラスに直接アクセスすることはありません。
@classproperty
デコレータを書くclassproperty
をサブクラス化することにより、ほんの数行のコードでproperty
デコレーターを実際に作成できます(Cで実装されていますが、同等のPython- ここ ):
class classproperty(property):
def __get__(self, obj, objtype=None):
return super(classproperty, self).__get__(objtype)
def __set__(self, obj, value):
super(classproperty, self).__set__(type(obj), value)
def __delete__(self, obj):
super(classproperty, self).__delete__(type(obj))
次に、デコレータを、プロパティと組み合わせたクラスメソッドであるかのように扱います。
class Foo(object):
_bar = 5
@classproperty
def bar(cls):
"""this is the bar attribute - each subclass of Foo gets its own.
Lookups should follow the method resolution order.
"""
return cls._bar
@bar.setter
def bar(cls, value):
cls._bar = value
@bar.deleter
def bar(cls):
del cls._bar
そして、このコードはエラーなく動作するはずです:
def main():
f = Foo()
print(f.bar)
f.bar = 4
print(f.bar)
del f.bar
try:
f.bar
except AttributeError:
pass
else:
raise RuntimeError('f.bar must have worked - inconceivable!')
help(f) # includes the Foo.bar help.
f.bar = 5
class Bar(Foo):
"a subclass of Foo, nothing more"
help(Bar) # includes the Foo.bar help!
b = Bar()
b.bar = 'baz'
print(b.bar) # prints baz
del b.bar
print(b.bar) # prints 5 - looked up from Foo!
if __== '__main__':
main()
しかし、これがどれだけ賢明なのかはわかりません。古いメーリングリスト 記事 は、機能しないことを示唆しています。
上記の欠点は、クラス__dict__
からのデータ記述子を単純に上書きするため、クラスから「クラスプロパティ」にアクセスできないことです。
ただし、メタクラス__dict__
で定義されたプロパティでこれをオーバーライドできます。例えば:
class MetaWithFooClassProperty(type):
@property
def foo(cls):
"""The foo property is a function of the class -
in this case, the trivial case of the identity function.
"""
return cls
そして、メタクラスのクラスインスタンスは、前のセクションですでに示した原則を使用して、クラスのプロパティにアクセスするプロパティを持つことができます。
class FooClassProperty(metaclass=MetaWithFooClassProperty):
@property
def foo(self):
"""access the class's property"""
return type(self).foo
そして今、私たちは両方のインスタンスを見る
>>> FooClassProperty().foo
<class '__main__.FooClassProperty'>
そしてクラス
>>> FooClassProperty.foo
<class '__main__.FooClassProperty'>
クラスプロパティにアクセスできます。
古い質問、多くのビュー、1つだけのPython 3つの方法が必要です。
幸いなことに、metaclass
kwargを使用すると簡単です。
class FooProperties(type):
@property
def var(cls):
return cls._var
class Foo(object, metaclass=FooProperties):
_var = 'FOO!'
次に、>>> Foo.var
「FOO!」
この「クラスプロパティ」システムをPythonで動作させる合理的な方法はありません。
これを機能させる不合理な方法の1つを次に示します。メタクラスの魔法の量を増やすことで、確実にシームレスにすることができます。
class ClassProperty(object):
def __init__(self, getter, setter):
self.getter = getter
self.setter = setter
def __get__(self, cls, owner):
return getattr(cls, self.getter)()
def __set__(self, cls, value):
getattr(cls, self.setter)(value)
class MetaFoo(type):
var = ClassProperty('getvar', 'setvar')
class Foo(object):
__metaclass__ = MetaFoo
_var = 5
@classmethod
def getvar(cls):
print "Getting var =", cls._var
return cls._var
@classmethod
def setvar(cls, value):
print "Setting var =", value
cls._var = value
x = Foo.var
print "Foo.var = ", x
Foo.var = 42
x = Foo.var
print "Foo.var = ", x
問題の結び目は、プロパティがPython「ディスクリプタ」と呼ばれるものであるということです。この種のメタプログラミングがどのように機能するかを説明するための短く簡単な方法はありません。 descriptor howto 。
かなり高度なフレームワークを実装している場合にのみ、この種のことを理解する必要があります。透過的なオブジェクトの永続性やRPCシステム、またはドメイン固有の言語のようなもの。
ただし、前の回答へのコメントでは、あなたは
クラスのすべてのインスタンスから見えるような方法で、これらのクラスメソッドが呼び出されるスコープ内で、クラスのすべてのインスタンスへの参照を持たない属性を変更する必要があります。
本当に欲しいのは、 Observer デザインパターンです。
インスタンス化されたオブジェクトを介してクラスプロパティにアクセスする場合、メタクラスでのみ設定しても役に立ちません。この場合、オブジェクトにも通常のプロパティをインストールする必要があります(クラスプロパティにディスパッチします)。次のことはもう少し明確だと思います:
#!/usr/bin/python
class classproperty(property):
def __get__(self, obj, type_):
return self.fget.__get__(None, type_)()
def __set__(self, obj, value):
cls = type(obj)
return self.fset.__get__(None, cls)(value)
class A (object):
_foo = 1
@classproperty
@classmethod
def foo(cls):
return cls._foo
@foo.setter
@classmethod
def foo(cls, value):
cls.foo = value
a = A()
print a.foo
b = A()
print b.foo
b.foo = 5
print a.foo
A.foo = 10
print b.foo
print A.foo
半分の解決策、クラスの__set__はまだ機能しません。ソリューションは、プロパティとstaticmethodの両方を実装するカスタムプロパティクラスです
class ClassProperty(object):
def __init__(self, fget, fset):
self.fget = fget
self.fset = fset
def __get__(self, instance, owner):
return self.fget()
def __set__(self, instance, value):
self.fset(value)
class Foo(object):
_bar = 1
def get_bar():
print 'getting'
return Foo._bar
def set_bar(value):
print 'setting'
Foo._bar = value
bar = ClassProperty(get_bar, set_bar)
f = Foo()
#__get__ works
f.bar
Foo.bar
f.bar = 2
Foo.bar = 3 #__set__ does not
クラスのすべてのインスタンスから見えるような方法で、またこれらのクラスメソッドが呼び出されるスコープ内で、クラスのすべてのインスタンスへの参照を持たない属性を変更する必要があるからです。
クラスの少なくとも1つのインスタンスにアクセスできますか?私はそれを行う方法を考えることができます:
class MyClass (object):
__var = None
def _set_var (self, value):
type (self).__var = value
def _get_var (self):
return self.__var
var = property (_get_var, _set_var)
a = MyClass ()
b = MyClass ()
a.var = "foo"
print b.var
これを試してみると、多くの既存のコードを変更/追加することなく仕事が完了します。
>>> class foo(object):
... _var = 5
... def getvar(cls):
... return cls._var
... getvar = classmethod(getvar)
... def setvar(cls, value):
... cls._var = value
... setvar = classmethod(setvar)
... var = property(lambda self: self.getvar(), lambda self, val: self.setvar(val))
...
>>> f = foo()
>>> f.var
5
>>> f.var = 3
>>> f.var
3
property
関数には、2つのcallable
引数が必要です。ラムダラッパー(インスタンスを最初の引数として渡す)を与えると、すべてうまくいきます。
これは、クラスを介したアクセスとメタクラスを使用するインスタンスを介したアクセスの両方で機能するソリューションです。
In [1]: class ClassPropertyMeta(type):
...: @property
...: def prop(cls):
...: return cls._prop
...: def __new__(cls, name, parents, dct):
...: # This makes overriding __getattr__ and __setattr__ in the class impossible, but should be fixable
...: dct['__getattr__'] = classmethod(lambda cls, attr: getattr(cls, attr))
...: dct['__setattr__'] = classmethod(lambda cls, attr, val: setattr(cls, attr, val))
...: return super(ClassPropertyMeta, cls).__new__(cls, name, parents, dct)
...:
In [2]: class ClassProperty(object):
...: __metaclass__ = ClassPropertyMeta
...: _prop = 42
...: def __getattr__(self, attr):
...: raise Exception('Never gets called')
...:
In [3]: ClassProperty.prop
Out[3]: 42
In [4]: ClassProperty.prop = 1
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-4-e2e8b423818a> in <module>()
----> 1 ClassProperty.prop = 1
AttributeError: can't set attribute
In [5]: cp = ClassProperty()
In [6]: cp.prop
Out[6]: 42
In [7]: cp.prop = 1
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-7-e8284a3ee950> in <module>()
----> 1 cp.prop = 1
<ipython-input-1-16b7c320d521> in <lambda>(cls, attr, val)
6 # This makes overriding __getattr__ and __setattr__ in the class impossible, but should be fixable
7 dct['__getattr__'] = classmethod(lambda cls, attr: getattr(cls, attr))
----> 8 dct['__setattr__'] = classmethod(lambda cls, attr, val: setattr(cls, attr, val))
9 return super(ClassPropertyMeta, cls).__new__(cls, name, parents, dct)
AttributeError: can't set attribute
これは、メタクラスで定義されたセッターでも機能します。
さまざまな場所を検索した後、Python 2および3で有効なクラスプロパティを定義するメソッドを見つけました。
from future.utils import with_metaclass
class BuilderMetaClass(type):
@property
def load_namespaces(self):
return (self.__sourcepath__)
class BuilderMixin(with_metaclass(BuilderMetaClass, object)):
__sourcepath__ = 'sp'
print(BuilderMixin.load_namespaces)
これが誰かを助けることを願っています:)