클래스 메소드에서 property () 사용
본질적으로 정적 변수를 가져오고 설정하기위한 두 가지 클래스 메소드 (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
클래스 메소드 장식 함수와 함께 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)이 아닌 클래스 속성 (Cx)으로 액세스되면 속성의 get 메서드가 호출되지 않습니다. 클래스 속성으로 사용될 때 속성에 대한 __get__ 작업을 재정의하려는 경우 __get__ 메서드를 확장하기 위해 속성 (하위 스타일 유형 자체)을 하위 클래스로 만들거나 새로 작성하여 설명자 유형을 처음부터 정의 할 수 있습니다 __get__, __set__ 및 __delete__ 메소드를 정의하는 스타일 클래스.
참고 : 아래 방법은 실제로 세터에서만 작동하지 않으며 게터에서만 작동합니다.
따라서 처방 된 솔루션은 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
클래스 메소드 장식 함수와 함께 property () 함수를 사용할 수 있습니까?
아니.
그러나 클래스 메소드는 단순히 해당 클래스의 인스턴스에서 액세스 할 수있는 클래스의 바인딩 된 메소드 (일부 함수)입니다.
인스턴스는 클래스의 함수이므로 인스턴스에서 클래스를 파생시킬 수 있으므로 다음을 사용하여 클래스 속성에서 원하는 동작을 얻을 수 있습니다 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 __name__ == '__main__':
main()
그러나 이것이 얼마나 잘 충고되었는지는 확실하지 않습니다. 오래된 메일 링리스트 기사에 따르면 작동하지 않아야합니다.
강의실에서 숙소를 이용하기 :
위의 단점은 "클래스 속성"은 클래스에서 액세스 할 수 없다는 것 __dict__입니다. 클래스에서 데이터 디스크립터를 단순히 덮어 쓰기 때문 입니다.
그러나 metaclass에 정의 된 속성으로이를 재정의 할 수 있습니다 __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'>
클래스 속성에 액세스 할 수 있습니다.
파이썬 3!
오래된 질문, 많은 견해, 진실한 파이썬 3 방법이 절실히 필요합니다.
운 좋게도 metaclasskwarg를 사용하면 쉽습니다.
class FooProperties(type):
@property
def var(cls):
return cls._var
class Foo(object, metaclass=FooProperties):
_var = 'FOO!'
그때, >>> Foo.var
'FOO!'
이 "클래스 속성"시스템을 파이썬에서 작동시키는 합리적인 방법은 없습니다.
그것이 작동하게하는 불합리한 방법은 다음과 같습니다. 메타 클래스 마법의 양을 늘리면 더 매끄럽게 만들 수 있습니다.
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
문제의 핵심은 속성이 파이썬이 "설명자"라고 부르는 것입니다. 이런 종류의 메타 프로그래밍이 어떻게 작동하는지 설명 할 수있는 짧고 쉬운 방법은 없으므로 설명자 howto 를 가리켜 야합니다 .
상당히 고급 프레임 워크를 구현하는 경우 이러한 종류의 것들만 이해하면됩니다. 투명한 객체 지속성 또는 RPC 시스템 또는 일종의 도메인 별 언어와 같습니다.
그러나 이전 답변에 대한 의견에서 귀하는
클래스의 모든 인스턴스에서 볼 수있는 방식으로 속성을 수정해야하며 이러한 클래스 메소드가 호출되는 범위에 클래스의 모든 인스턴스에 대한 참조가 없습니다.
당신이 정말로 원하는 것은 관찰자 디자인 패턴입니다.
메타 클래스에서만 설정하면 인스턴스화 된 객체를 통해 클래스 속성에 액세스하려는 경우 도움이되지 않습니다.이 경우 객체에 일반 속성을 설치해야합니다 (클래스 속성으로 디스패치). 다음은 조금 더 분명하다고 생각합니다.
#!/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__은 여전히 작동하지 않습니다. 솔루션은 속성과 정적 메서드를 모두 구현하는 사용자 지정 속성 클래스입니다.
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
클래스의 모든 인스턴스에서 볼 수있는 방식으로 이러한 클래스 메소드가 호출되는 범위에서 속성을 수정해야하기 때문에 클래스의 모든 인스턴스에 대한 참조가 없습니다.
수업 중 하나 이상의 인스턴스에 액세스 할 수 있습니까? 그때 할 수있는 방법을 생각할 수 있습니다.
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함수에는 두 개의 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
메타 클래스에 정의 된 setter 와도 작동합니다.
다른 장소를 검색 한 후 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)
이것이 누군가를 도울 수 있기를 바랍니다 :)
여기 내 제안이 있습니다. 클래스 메소드를 사용하지 마십시오.
진심으로.
이 경우 클래스 메소드를 사용하는 이유는 무엇입니까? 왜 평범한 수업의 평범한 물건을 가지고 있지 않습니까?
단순히 값을 변경하려면 속성이별로 도움이되지 않습니까? 속성 값을 설정하고 완료하십시오.
감추어 야 할 것이 있다면, 미래 구현에서 변경 될 수있는 것만 사용해야합니다.
어쩌면 귀하의 예가 끝났을 수도 있고, 중단 된 계산이있을 수 있습니다. 그러나 속성이 상당한 가치를 부여하는 것처럼 보이지 않습니다.
Java에 영향을 미치는 "개인 정보 보호"기술 (Python에서 _로 시작하는 속성 이름)은 그다지 도움이되지 않습니다. 누구 한테서? 개인의 요점은 파이썬에서와 같이 소스를 가질 때 조금 성가시다.
Java에 영향을 미치는 EJB 스타일 getter 및 setter (종종 Python의 특성으로 수행됨)는 정적 언어 컴파일러를 사용하여 소집자를 전달할뿐만 아니라 Java의 원시적 인트로 스펙 션을 용이하게합니다. 그 게터와 세터는 모두 파이썬에서 도움이되지 않습니다.
참고 URL : https://stackoverflow.com/questions/128573/using-property-on-classmethods
'IT story' 카테고리의 다른 글
| Java 해시 맵이 실제로 O (1)입니까? (0) | 2020.06.11 |
|---|---|
| objective-c에서 YES / NO, TRUE / FALSE와 true / false 사이에 차이가 있습니까? (0) | 2020.06.11 |
| POM.xml에서 환경 변수를 참조하는 방법은 무엇입니까? (0) | 2020.06.11 |
| jQuery show () 함수에 display : inline-block을 추가하는 방법은 무엇입니까? (0) | 2020.06.11 |
| Android Studio의 여러 인스턴스를 실행하는 방법 (0) | 2020.06.11 |