전반적인 개념의 다른 구현 또는 전문화 인 클래스 패밀리를 작성하려고한다고 가정하십시오. 일부 파생 속성에 대해 적절한 기본 구현이 있다고 가정합니다. 이것을 기본 클래스에 넣고 싶습니다
class Math_Set_Base:
    @property
    def size(self):
        return len(self.elements)따라서 서브 클래스는이 어리석은 예제에서 자동으로 요소를 계산할 수 있습니다.
class Concrete_Math_Set(Math_Set_Base):
    def __init__(self,*elements):
        self.elements = elements
Concrete_Math_Set(1,2,3).size
# 3그러나 서브 클래스가이 기본값을 사용하지 않으려면 어떻게해야합니까? 작동하지 않습니다.
import math
class Square_Integers_Below(Math_Set_Base):
    def __init__(self,cap):
        self.size = int(math.sqrt(cap))
Square_Integers_Below(7)
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
#   File "<stdin>", line 3, in __init__
# AttributeError: can't set attribute속성을 속성으로 재정의하는 방법이 있지만이를 피하고 싶습니다. 기본 클래스의 목적은 사용자가 가능한 한 쉽게 생활을 할 수 있도록하기 위해, 서브 클래스의 좁은 관점에서 복잡하고 불필요한 액세스 방법을 강요하여 부풀림을 추가하지 않는 것입니다.
할 수 있습니까? 그렇지 않다면 차선책은 무엇입니까?
답변
속성은 이름이 같은 인스턴스 속성보다 우선하는 데이터 설명자입니다. 고유 한 __get__()방법 으로 비 데이터 디스크립터를 정의 할 수 있습니다 . 인스턴스 속성이 동일한 이름 의 비 데이터 디스크립터 보다 우선 합니다 (docs 참조) . 여기서 문제는 non_data_property아래 정의 된 것은 계산 목적으로 만 (세터 또는 삭제자를 정의 할 수 없음) 있지만 귀하의 예에서는 그렇습니다.
import math
class non_data_property:
    def __init__(self, fget):
        self.__doc__ = fget.__doc__
        self.fget = fget
    def __get__(self, obj, cls):
        if obj is None:
            return self
        return self.fget(obj)
class Math_Set_Base:
    @non_data_property
    def size(self, *elements):
        return len(self.elements)
class Concrete_Math_Set(Math_Set_Base):
    def __init__(self, *elements):
        self.elements = elements
class Square_Integers_Below(Math_Set_Base):
    def __init__(self, cap):
        self.size = int(math.sqrt(cap))
print(Concrete_Math_Set(1, 2, 3).size) # 3
print(Square_Integers_Below(1).size) # 1
print(Square_Integers_Below(4).size) # 2
print(Square_Integers_Below(9).size) # 3그러나이 변경을 수행하려면 기본 클래스에 액세스 할 수 있다고 가정합니다.
답변
이것은 무료로만 제공 될 수있는 긴 바람의 대답이 될 것입니다 …
궁극적으로이 답변이 실제 문제에 도움이되지 않을 수 있습니다. 사실, 내 결론은-나는 이것을 전혀하지 않을 것입니다. 말했듯이,이 결론의 배경은 자세한 내용을 찾고 있기 때문에 약간 재미있을 수 있습니다.
약간의 오해를 해결
대부분의 경우 첫 번째 대답은 정확하지만 항상 그런 것은 아닙니다 . 예를 들어 다음 클래스를 고려하십시오.
class Foo:
    def __init__(self):
        self.name = 'Foo!'
        @property
        def inst_prop():
            return f'Retrieving {self.name}'
        self.inst_prop = inst_propinst_prop은 (는 property)이지만 돌이킬 수없는 인스턴스 속성입니다.
>>> Foo.inst_prop
Traceback (most recent call last):
  File "<pyshell#60>", line 1, in <module>
    Foo.inst_prop
AttributeError: type object 'Foo' has no attribute 'inst_prop'
>>> Foo().inst_prop
<property object at 0x032B93F0>
>>> Foo().inst_prop.fget()
'Retrieving Foo!'그것은 모두 당신 의 처음 정의 위치에 달려 있습니다 property. @property클래스가 “scope”클래스 (또는 실제로 namespace) 내에 정의되어 있으면 클래스 속성이됩니다. 내 예에서 클래스 자체는 inst_prop인스턴스화 될 때까지 아무것도 알지 못합니다 . 물론 여기에서는 속성으로 그리 유용하지 않습니다.
그러나 먼저 상속 해결에 대한 귀하의 의견을 다루겠습니다 …
그렇다면 상속이 어떻게 정확하게이 문제에 영향을 미칩니 까? 이 기사에서는이 주제에 대해 조금 살펴 보았고, Method Resolution Order 는 다소 깊이가 아니라 상속의 폭을 주로 다루지 만 다소 관련이 있습니다.
아래 설정에서 우리의 발견과 결합 :
@property
def some_prop(self):
    return "Family property"
class Grandparent:
    culture = some_prop
    world_view = some_prop
class Parent(Grandparent):
    world_view = "Parent's new world_view"
class Child(Parent):
    def __init__(self):
        try:
            self.world_view = "Child's new world_view"
            self.culture = "Child's new culture"
        except AttributeError as exc:
            print(exc)
            self.__dict__['culture'] = "Child's desired new culture"이 줄이 실행될 때 어떤 일이 발생하는지 상상해보십시오.
print("Instantiating Child class...")
c = Child()
print(f'c.__dict__ is: {c.__dict__}')
print(f'Child.__dict__ is: {Child.__dict__}')
print(f'c.world_view is: {c.world_view}')
print(f'Child.world_view is: {Child.world_view}')
print(f'c.culture is: {c.culture}')
print(f'Child.culture is: {Child.culture}')결과는 다음과 같습니다.
Instantiating Child class... can't set attribute c.__dict__ is: {'world_view': "Child's new world_view", 'culture': "Child's desired new culture"} Child.__dict__ is: {'__module__': '__main__', '__init__': <function Child.__init__ at 0x0068ECD8>, '__doc__': None} c.world_view is: Child's new world_view Child.world_view is: Parent's new world_view c.culture is: Family property Child.culture is: <property object at 0x00694C00>
방법에 주목하십시오 :
- self.world_view- self.culture실패 하면서 적용 할 수있었습니다
- culture에 존재하지 않습니다- Child.__dict__(- mappingproxy클래스와 인스턴스와 혼동하지 마십시오- __dict__)
- 에 culture존재 하더라도c.__dict__참조되지 않습니다.
왜 클래스에 world_view의해 Parent속성이 아닌 것으로 덮어 쓰여 졌는지 짐작할 Child수있을 것입니다. 때문에 한편, culture상속, 그것은 단지 내에 존재 mappingproxy의Grandparent :
Grandparent.__dict__ is: {
    '__module__': '__main__',
    'culture': <property object at 0x00694C00>,
    'world_view': <property object at 0x00694C00>,
    ...
}실제로 제거하려고하면 Parent.culture:
>>> del Parent.culture Traceback (most recent call last): File "<pyshell#67>", line 1, in <module> del Parent.culture AttributeError: culture
당신은 심지어 존재하지 않는 것을 알 수 있습니다 Parent. 객체가 직접을 참조하기 때문 Grandparent.culture입니다.
그렇다면 결의 명령은 어떻습니까?
따라서 실제 해결 명령을 준수하는 데 관심이 있습니다. Parent.world_view대신 제거해 보겠습니다 .
del Parent.world_view
print(f'c.world_view is: {c.world_view}')
print(f'Child.world_view is: {Child.world_view}')결과가 궁금하십니까?
c.world_view is: Family property Child.world_view is: <property object at 0x00694C00>
world_view property비록 우리가 이전을 성공적으로 할당했지만, 그것은 조부모의 것으로 되돌아갔습니다 self.world_view! 그러나 world_view다른 대답과 같이 수업 수준에서 강하게 변화하면 어떻게 될까요? 삭제하면 어떻게 되나요? 현재 클래스 속성을 속성으로 할당하면 어떻게됩니까?
Child.world_view = "Child's independent world_view"
print(f'c.world_view is: {c.world_view}')
print(f'Child.world_view is: {Child.world_view}')
del c.world_view
print(f'c.world_view is: {c.world_view}')
print(f'Child.world_view is: {Child.world_view}')
Child.world_view = property(lambda self: "Child's own property")
print(f'c.world_view is: {c.world_view}')
print(f'Child.world_view is: {Child.world_view}')결과는 다음과 같습니다.
# Creating Child's own world view c.world_view is: Child's new world_view Child.world_view is: Child's independent world_view # Deleting Child instance's world view c.world_view is: Child's independent world_view Child.world_view is: Child's independent world_view # Changing Child's world view to the property c.world_view is: Child's own property Child.world_view is: <property object at 0x020071B0>
c.world_view인스턴스 속성으로 복원되었으므로 Child.world_view할당 한 속성 이 흥미 롭습니다 . 인스턴스 속성을 제거한 후에는 클래스 속성으로 돌아갑니다. 그리고 Child.world_view속성에 속성 을 다시 할당 하면 인스턴스 속성에 즉시 액세스 할 수 없게됩니다.
따라서 다음 해결 순서를 추측 할 수 있습니다 .
- 클래스 속성이 존재하는 경우 와 그것이이다 property, 그것의를 통해 값을 검색getter하거나fget(나중에에 더). 현재 클래스는 기본 클래스부터 마지막 클래스까지입니다.
- 그렇지 않으면 인스턴스 속성이 존재하면 인스턴스 속성 값을 검색하십시오.
- 그렇지 않으면, 비 property클래스 속성을 검색하십시오 . 현재 클래스는 기본 클래스부터 마지막 클래스까지입니다.
이 경우 루트를 제거하십시오 property.
del Grandparent.culture
print(f'c.culture is: {c.culture}')
print(f'Child.culture is: {Child.culture}')다음을 제공합니다.
c.culture is: Child's desired new culture Traceback (most recent call last): File "<pyshell#74>", line 1, in <module> print(f'Child.culture is: {Child.culture}') AttributeError: type object 'Child' has no attribute 'culture'
타다!  에에 대한 강력한 삽입을 기반으로하는 Child고유 culture한 기능이 c.__dict__있습니다.  Child.culture이 정의되지 않았기 때문에, 물론, 존재하지 않는 Parent또는 Child클래스 속성과 Grandparent의 제거했다.
이것이 내 문제의 근본 원인입니까?
실제로는 없습니다 . 할당 할 때 여전히 관찰되는 오류 self.culture는 완전히 다릅니다 . 그러나 상속 순서는 배경을 답으로 설정합니다 property.
이전에 언급 한 getter방법 외에도 property소매에 깔끔한 트릭이 있습니다. 이 경우 가장 관련이 있는 것은 setter또는 fset메소드 self.culture = ...입니다. 이 메소드는 라인 별로 트리거됩니다 . 당신 property은 어떤 setter또는 fget함수를 구현하지 않았기 때문에 파이썬은 무엇을 해야할지 모르고 AttributeError대신 ( can't set attribute)을 던집니다 .
그러나 setter메소드 를 구현 한 경우 :
@property
def some_prop(self):
    return "Family property"
@some_prop.setter
def some_prop(self, val):
    print(f"property setter is called!")
    # do something else...Child클래스를 인스턴스화하면 다음을 얻을 수 있습니다.
Instantiating Child class... property setter is called!
를받는 대신 AttributeError실제로 some_prop.setter메소드를 호출하고 있습니다. 이전의 결과를 통해 객체에 대한 제어력이 향상되었으므로 속성에 도달 하기 전에 클래스 속성을 덮어 써야한다는 것을 알고 있습니다. 이것은 기본 클래스 내에서 트리거로 구현 될 수 있습니다. 다음은 신선한 예입니다.
class Grandparent:
    @property
    def culture(self):
        return "Family property"
    # add a setter method
    @culture.setter
    def culture(self, val):
        print('Fine, have your own culture')
        # overwrite the child class attribute
        type(self).culture = None
        self.culture = val
class Parent(Grandparent):
    pass
class Child(Parent):
    def __init__(self):
        self.culture = "I'm a millennial!"
c = Child()
print(c.culture)결과 :
Fine, have your own culture I'm a millennial!
TA-DAH! 상속 된 속성을 통해 자신의 인스턴스 속성을 덮어 쓸 수 있습니다!
문제가 해결 되었습니까?
… 실제로. 이 방법의 문제점은 이제 적절한 setter방법 을 사용할 수 없다는 것 입니다. 에 값을 설정하려는 경우가 있습니다 property. 그러나 이제는 설정 self.culture = ...할 때마다 항상 정의 된 기능을 덮어 씁니다 getter(이 경우 실제로 @property포장 된 부분 일뿐 입니다. 더 미묘한 측정을 추가 할 수 는 있지만 한 가지 방법은 항상 단순한 것 이상을 포함 self.culture = ...합니다. 예 :
class Grandparent:
    # ...
    @culture.setter
    def culture(self, val):
        if isinstance(val, tuple):
            if val[1]:
                print('Fine, have your own culture')
                type(self).culture = None
                self.culture = val[0]
        else:
            raise AttributeError("Oh no you don't")
# ...
class Child(Parent):
    def __init__(self):
        try:
            # Usual setter
            self.culture = "I'm a Gen X!"
        except AttributeError:
            # Trigger the overwrite condition
            self.culture = "I'm a Boomer!", True그것은 것 절대로 안돼요 이상 다른 대답보다 복잡한 size = None클래스 수준.
and 또는 추가 메소드 를 처리하기 위해 자체 설명자를 작성하는 것도 고려할 수 있습니다 . 그러나 하루가 끝나면 참조 될 때 항상 먼저 트리거되고 참조 될 때 항상 먼저 트리거됩니다. 내가 시도한 한 주변을 돌아 다니지 않습니다.__get____set__self.culture__get__self.culture = ...__set__
문제의 핵심, IMO
내가 여기서 보는 문제는-당신은 당신의 케이크를 가지고 그것을 먹을 수 없습니다.  property유사한 의미 설명 과 같은 방법에서 편리하게 액세스 할 수있는 getattr나 setattr. 이 방법들이 다른 목적을 달성하기를 원한다면 문제를 묻는 것입니다. 아마도 접근 방식을 다시 생각할 것입니다.
- 나는 property이것을 위해 정말로 필요 합니까?
- 방법이 다르게 제공 될 수 있습니까?
- 내가 필요한 경우 property덮어 쓸 이유가 있습니까?
- 서브 클래스 property가 적용되지 않으면 실제로 같은 패밀리에 속합니까?
- /을 덮어 써야 할 경우 property재 할당이 실수로propertys를 무효화 할 수 있기 때문에 별도의 방법이 단순히 재 할당하는 것보다 나을까요 ?
포인트 5의 경우 내 접근 방식은 overwrite_prop()기본 클래스에 현재 클래스 속성을 덮어 쓰는 메소드가 있으므로 property더 이상 트리거되지 않습니다.
class Grandparent:
    # ...
    def overwrite_props(self):
        # reassign class attributes
        type(self).size = None
        type(self).len = None
        # other properties, if necessary
# ...
# Usage
class Child(Parent):
    def __init__(self):
        self.overwrite_props()
        self.size = 5
        self.len = 10보시다시피 여전히 약간의 생각은 있지만 적어도 cryptic보다 명시 적 size = None입니다. 즉, 궁극적으로 속성을 덮어 쓰지 않고 루트에서 내 디자인을 다시 생각할 것입니다.
당신이 이것을 지금까지 만들었다면-나와 함께이 여행을 걸어 주셔서 감사합니다. 재미있는 작은 운동이었다.
답변
A @property는 클래스 수준에서 정의됩니다. 문서는 어떻게 작동하는지에 대한 철저한 내용이수록되어 있지만, 그 말을 충분 설정 하거나 점점 특정 메소드를 호출에 속성 결의를. 그러나이 property프로세스를 관리 하는 객체는 클래스 자체 정의로 정의됩니다. 즉, 클래스 변수로 정의되지만 인스턴스 변수처럼 동작합니다.
이것의 한 가지 결과 는 클래스 레벨에서 자유롭게 재 할당 할 수 있다는 것입니다 .
print(Math_Set_Base.size)
# <property object at 0x10776d6d0>
Math_Set_Base.size = 4
print(Math_Set_Base.size)
# 4다른 클래스 수준 이름 (예 : 메서드)과 마찬가지로 명시 적으로 다르게 정의하여 하위 클래스에서이를 재정의 할 수 있습니다.
class Square_Integers_Below(Math_Set_Base):
    # explicitly define size at the class level to be literally anything other than a @property
    size = None
    def __init__(self,cap):
        self.size = int(math.sqrt(cap))
print(Square_Integers_Below(4).size)  # 2
print(Square_Integers_Below.size)     # None실제 인스턴스를 만들 때 인스턴스 변수는 단순히 같은 이름의 클래스 변수를 음영 처리합니다. property객체는 일반적으로이 과정 (즉, 적용 getter 및 setter)하지만 클래스 수준의 이름을 속성으로 정의되어 있지 않은 경우, 아무것도 특별한 일이 발생을 조작하는 몇 가지 헛소리를 사용하고, 당신이 다른 변수의 예상대로 그래서이 역할을합니다.
답변
과제 ( size) 가 전혀 필요하지 않습니다 . size기본 클래스의 속성이므로 자식 클래스에서 해당 속성을 재정의 할 수 있습니다.
class Math_Set_Base:
    @property
    def size(self):
        return len(self.elements)
    # size = property(lambda self: self.elements)
class Square_Integers_Below(Math_Set_Base):
    def __init__(self, cap):
        self._cap = cap
    @property
    def size(self):
        return int(math.sqrt(self._cap))
    # size = property(lambda self: int(math.sqrt(self._cap)))제곱근을 미리 계산하여 이것을 (마이크로) 최적화 할 수 있습니다.
class Square_Integers_Below(Math_Set_Base):
    def __init__(self, cap):
        self._size = int(math.sqrt(self._cap))
    @property
    def size(self):
        return self._size답변
size수업에서 정의하려는 것처럼 보입니다 .
class Square_Integers_Below(Math_Set_Base):
    size = None
    def __init__(self, cap):
        self.size = int(math.sqrt(cap))다른 옵션은 cap클래스 에 저장 size하고 속성 으로 정의 하여 계산하는 것입니다 (기본 클래스의 속성을 재정의 함 size).
답변
다음과 같이 세터를 추가하는 것이 좋습니다.
class Math_Set_Base:
    @property
    def size(self):
        try:
            return self._size
        except:
            return len(self.elements)
    @size.setter
    def size(self, value):
        self._size = value이렇게하면 다음 .size과 같이 기본 속성을 재정의 할 수 있습니다 .
class Concrete_Math_Set(Math_Set_Base):
    def __init__(self,*elements):
        self.elements = elements
class Square_Integers_Below(Math_Set_Base):
    def __init__(self,cap):
        self.size = int(math.sqrt(cap))
print(Concrete_Math_Set(1,2,3).size) # 3
print(Square_Integers_Below(7).size) # 2답변
또한 다음을 할 수 있습니다
class Math_Set_Base:
    _size = None
    def _size_call(self):
       return len(self.elements)
    @property
    def size(self):
        return  self._size if self._size is not None else self._size_call()
class Concrete_Math_Set(Math_Set_Base):
    def __init__(self, *elements):
        self.elements = elements
class Square_Integers_Below(Math_Set_Base):
    def __init__(self, cap):
        self._size = int(math.sqrt(cap))