IT story

클래스 메소드에서 property () 사용

hot-time 2020. 6. 11. 08:21
반응형

클래스 메소드에서 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

반응형