파이썬의 설명자가 무엇이며 유용 할 수있는 것을 이해하려고합니다.
디스크립터는 다음과 같은 특수 메소드가있는 클래스 속성 (예 : 특성 또는 메소드)입니다.
__get__
(비 데이터 디스크립터 방법 (예 : 메소드 / 함수))
__set__
(예 : 속성 인스턴스의 데이터 설명자 방법)
__delete__
(데이터 디스크립터 방법)
이 디스크립터 오브젝트는 다른 오브젝트 클래스 정의의 속성으로 사용될 수 있습니다. (즉, 그들은__dict__
, 클래스 객체 있습니다.)
디스크립터 객체를 사용하여 점선 조회 결과를 프로그래밍 방식으로 관리 할 수 있습니다 (예 : foo.descriptor
정규 표현식, 대입, 심지어 삭제에서 .
기능 / 방법, 결합 방법 property
, classmethod
및staticmethod
모든 사용들은이 점 조회를 통해 액세스하는 방법을 제어하는이 특별한 방법.
ㅏ 데이터 기술자 와 같은property
, 인스턴스가 각 가능한 속성을 미리 계산하는 경우보다 적은 메모리를 사용할 수 있도록 개체의 간단한 상태에 따라 속성의 게으른 평가를 할 수 있습니다.
에 member_descriptor
의해 생성 된 다른 데이터 디스크립터__slots__
는 클래스가보다 유연하지만 공간을 소비하는 대신 가변 터플과 유사한 데이터 구조에 데이터를 저장할 수 있도록하여 메모리를 절약 할 수 __dict__
있습니다.
비 데이터 디스크립터 (일반적으로 인스턴스, 클래스 및 정적 메소드)는 내재 된 첫 번째 인수 (일반적으로 이름이 지정됨)를 가져옵니다. cls
self
)는 비 데이터 디스크립터 메소드 ()에서 각각 및 )를 가져옵니다 __get__
.
대부분의 Python 사용자는 간단한 사용법 만 배울 필요가 있으며 설명 자의 구현을 더 배우거나 이해할 필요가 없습니다.
심도있는 내용 : 설명자는 무엇입니까?
디스크립터는 다음 방법 ( __get__
, __set__
또는 __delete__
) 중 하나를 가진 객체로 , 인스턴스의 일반적인 속성 인 것처럼 점으로 찾아보기를 통해 사용됩니다. 소유자 - 객체의 경우, obj_instance
, A의 descriptor
객체 :
obj_instance.descriptor
를 발동
descriptor.__get__(self, obj_instance, owner_class)
돌아 오는 value
이 방법을 모든 방법이며, get
속성 작업에.
obj_instance.descriptor = value
descriptor.__set__(self, obj_instance, value)
반환을 호출합니다. None
이것은 setter
속성이 작동하는 방식입니다.
del obj_instance.descriptor
descriptor.__delete__(self, obj_instance)
반환을 호출합니다. None
이것은 deleter
속성이 작동하는 방식입니다.
obj_instance
클래스가 디스크립터 객체의 인스턴스를 포함하는 인스턴스입니다. 디스크립터self
의 인스턴스입니다 (아마도 클래스의 클래스 일 것입니다 )obj_instance
코드로 이것을 정의하기 위해 객체의 속성 세트가 필요한 속성과 교차하는 경우 객체는 디스크립터입니다.
def has_descriptor_attrs(obj):
return set(['__get__', '__set__', '__delete__']).intersection(dir(obj))
def is_descriptor(obj):
"""obj can be instance of descriptor or the descriptor class"""
return bool(has_descriptor_attrs(obj))
데이터 설명자 가지고 __set__
및 / 또는 __delete__
. 비 - 데이터 - 기술자는 어느 쪽도 없습니다 아니다 .
__set__
__delete__
def has_data_descriptor_attrs(obj):
return set(['__set__', '__delete__']) & set(dir(obj))
def is_data_descriptor(obj):
return bool(has_data_descriptor_attrs(obj))
내장 디스크립터 오브젝트 예제 :
classmethod
staticmethod
property
- 일반적인 기능
비 데이터 디스크립터
우리는 볼 수 있습니다 classmethod
및 staticmethod
비 - 데이터 - 설명자입니다 :
>>> is_descriptor(classmethod), is_data_descriptor(classmethod)
(True, False)
>>> is_descriptor(staticmethod), is_data_descriptor(staticmethod)
(True, False)
둘 다 __get__
방법이 있습니다.
>>> has_descriptor_attrs(classmethod), has_descriptor_attrs(staticmethod)
(set(['__get__']), set(['__get__']))
모든 함수는 비 데이터 디스크립터이기도합니다.
>>> def foo(): pass
...
>>> is_descriptor(foo), is_data_descriptor(foo)
(True, False)
데이터 설명자, property
그러나 property
데이터 설명자입니다.
>>> is_data_descriptor(property)
True
>>> has_descriptor_attrs(property)
set(['__set__', '__get__', '__delete__'])
점선 조회 순서
이는 점으로 구분 된 조회의 조회 순서에 영향을주기 때문에 중요한 차이점 입니다.
obj_instance.attribute
- 먼저 위의 속성이 인스턴스 클래스의 데이터 디스크립터인지 확인합니다.
- 그렇지 않으면 속성이있는 경우, 그것은에서 볼 수 보이는
obj_instance
'의__dict__
다음,
- 결국 비 데이터 디스크립터로 넘어갑니다.
이 조회 순서의 결과는 함수 / 메소드와 같은 비 데이터 디스크립터 가 인스턴스에 의해 대체 될 수 있다는 것 입니다.
요약 및 다음 단계
우리는 디스크립터의와 오브젝트 것을 배웠습니다 __get__
, __set__
또는이 __delete__
. 이 디스크립터 오브젝트는 다른 오브젝트 클래스 정의의 속성으로 사용될 수 있습니다. 이제 코드를 예로 들어 어떻게 사용되는지 살펴 보겠습니다.
문제의 코드 분석
다음은 코드와 각 질문에 대한 답변입니다.
class Celsius(object):
def __init__(self, value=0.0):
self.value = float(value)
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, value):
self.value = float(value)
class Temperature(object):
celsius = Celsius()
- 디스크립터 클래스가 필요한 이유는 무엇입니까?
디스크립터는이 클래스 속성에 대해 항상 float를 갖도록하고 속성 을 삭제하는 데 Temperature
사용할 수 없도록 del
합니다.
>>> t1 = Temperature()
>>> del t1.celsius
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: __delete__
그렇지 않으면 디스크립터는 소유자 클래스와 소유자 인스턴스를 무시하고 대신 디스크립터에 상태를 저장합니다. 간단한 클래스 속성으로 모든 인스턴스에서 상태를 쉽게 공유 할 수 있습니다 (항상 클래스에 float로 설정하고 삭제하지 않거나 코드 사용자에게 익숙한 경우).
class Temperature(object):
celsius = 0.0
이렇게하면 예제와 정확히 동일한 동작을 얻을 수 있지만 (아래 질문 3에 대한 응답 참조) Pythons 내장 ( property
)을 사용하며 더 관용적 인 것으로 간주됩니다.
class Temperature(object):
_celsius = 0.0
@property
def celsius(self):
return type(self)._celsius
@celsius.setter
def celsius(self, value):
type(self)._celsius = float(value)
- 여기서 인스턴스와 소유자는 무엇입니까? ( 얻을 ). 이 매개 변수의 목적은 무엇입니까?
instance
디스크립터를 호출하는 소유자의 인스턴스입니다. 소유자는 설명자 객체를 사용하여 데이터 포인트에 대한 액세스를 관리하는 클래스입니다. 보다 자세한 변수 이름은이 답변의 첫 번째 단락 옆에 설명자를 정의하는 특수 메소드에 대한 설명을 참조하십시오.
- 이 예제를 어떻게 호출 / 사용합니까?
데모는 다음과 같습니다.
>>> t1 = Temperature()
>>> t1.celsius
0.0
>>> t1.celsius = 1
>>>
>>> t1.celsius
1.0
>>> t2 = Temperature()
>>> t2.celsius
1.0
속성을 삭제할 수 없습니다 :
>>> del t2.celsius
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: __delete__
그리고 부동으로 변환 할 수없는 변수를 할당 할 수 없습니다 :
>>> t1.celsius = '0x02'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in __set__
ValueError: invalid literal for float(): 0x02
그렇지 않으면 여기에있는 것은 모든 인스턴스에 대한 글로벌 상태이며, 이는 모든 인스턴스에 지정하여 관리됩니다.
대부분의 숙련 된 Python 프로그래머가이 결과를 달성 할 것으로 예상되는 방식은 property
데코레이터 를 사용하는 것입니다. 데코레이터는 동일한 설명자를 사용하지만 위에서 정의한대로 소유자 클래스의 구현으로 동작을 가져옵니다.
class Temperature(object):
_celsius = 0.0
@property
def celsius(self):
return type(self)._celsius
@celsius.setter
def celsius(self, value):
type(self)._celsius = float(value)
다음은 원래 코드 조각과 정확히 동일한 동작을합니다.
>>> t1 = Temperature()
>>> t2 = Temperature()
>>> t1.celsius
0.0
>>> t1.celsius = 1.0
>>> t2.celsius
1.0
>>> del t1.celsius
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't delete attribute
>>> t1.celsius = '0x02'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 8, in celsius
ValueError: invalid literal for float(): 0x02
결론
디스크립터를 정의하는 속성, 데이터 디스크립터와 비 데이터 디스크립터의 차이점,이를 사용하는 내장 객체 및 사용에 대한 특정 질문을 다뤘습니다.
다시 질문의 예를 어떻게 사용 하시겠습니까? 나는 당신이하지 않기를 바랍니다. 첫 번째 제안 (간단한 클래스 특성)으로 시작하고 필요하다고 생각되면 두 번째 제안 (속성 데코레이터)으로 넘어 가기를 바랍니다.
self
과instance
?