__get__, __set__ 및 Python 기술자에 대해
Python의 디스크립터가 무엇이고 어디에 유용한지 이해하려고 합니다.어떻게 작동하는지는 이해하지만, 여기 제 의심이 있습니다.다음 코드를 고려합니다.
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()
디스크립터 클래스가 필요한 이유는 무엇입니까?
죠?
instance
★★★★★★★★★★★★★★★★★」owner
__get__
). 들파 파파 ?적 적? ?? ???이 예를 어떻게 부르거나 사용할 수 있을까요?
의 Python Python의 입니다.property
타이핑하다는 간단히 합니다.__get__
,__set__
(의 [ Temperature ])래스에서 했던 것처럼) 정의에서 다른 클래스에 추가됩니다.예를 들어 다음과 같습니다.
temp=Temperature()
temp.celsius #calls celsius.__get__
에 액세스 (「」).celsius
위의 예에서)는 적절한 디스크립터 메서드를 호출합니다.
instance
__get__
「이러한 인스턴스」로 되어 있습니다).__get__
수 있다temp
와 동시에 , 「」를 참조해 주세요.owner
, 「Descriptor」가 ).Temperature
를 참조해 주세요.
디스크립터 클래스를 사용하여 디스크립터 클래스를 지원하는 로직을 캡슐화해야 합니다.이와 같이 기술자가 고가의 작업(예를 들어)을 캐시하기 위해 사용되는 경우 해당 클래스가 아닌 그 자체에 값을 저장할 수 있습니다.
기술자에 대한 기사는 여기에서 찾을 수 있습니다.
한 바와 같이, ": jchl"을 하면 됩니다.Temperature.celsius
,instance
되다None
.
디스크립터 클래스가 필요한 이유는 무엇입니까?
속성 작동 방식을 더욱 제어할 수 있습니다.예를 들어 Java에서 getters와 setters에 익숙하다면 Python의 방식입니다.한 가지 장점은 사용자에게 속성처럼 보인다는 것입니다(구문은 변경되지 않음).우선 일반 속성부터 시작하여 고급스러운 작업을 수행해야 할 경우 설명자로 전환할 수 있습니다.
Atribute는 가변값일 뿐입니다.디스크립터를 사용하면 값을 읽거나 설정(또는 삭제)할 때 임의 코드를 실행할 수 있습니다.따라서 데이터베이스 내의 필드(ORM의 일종)에 Atribute를 매핑하기 위해 사용할 수 있습니다.
는 '하다, 하다, 하다, 하다'에 를 두고 입니다.__set__
– 사실상 읽기 전용이 됩니다.
죠?
instance
★★★★★★★★★★★★★★★★★」owner
__get__
). 이들 파라미터의 목적은 무엇입니까?
이것은 매우 미묘합니다(그리고 제가 여기에 새로운 답을 쓰는 이유는 같은 것을 궁금해하다가 이 질문을 발견했는데, 기존의 답변이 그렇게 훌륭하다고 생각하지 않았습니다).
설명자는 클래스에서 정의되지만 일반적으로 인스턴스에서 호출됩니다.기술자는 클래스에 정의되지만 일반적으로 인스턴스에서 호출됩니다. When it's called from an instance both 인스턴스에서 호출된 경우 둘 다instance
and 그리고.owner
are set (and you can work out (그리고 운동도 할 수 있다)owner
부에서instance
그것은 무의미한 것 같다.의미가 없는 것 같아요.) But when called from a class, only 하지만 수업에서 전화를 걸었을 때만owner
그게 바로 여기 있습니다.설정되어 있기 때문에, 거기에 있습니다.
This is only needed for 이것은 다음 경우에만 필요합니다.__get__
왜냐하면 수업시간에 전화를 할 수 있는 유일한 유일한 사람이니까요.수업시간에 호출할 수 있는 유일한 방법이기 때문입니다.설명을 설정한 클래스 값을 설정합니다.클래스 값을 설정할 경우 기술자 자체를 설정합니다.삭제도 마찬가지입니다. '이러니'가owner
그럴 필요가 없어요.
이 예를 어떻게 부르거나 사용할 수 있을까요?
자, 여기 비슷한 수업을 사용하는 멋진 요령이 있습니다.
class Celsius:
def __get__(self, instance, owner):
return 5 * (instance.fahrenheit - 32) / 9
def __set__(self, instance, value):
instance.fahrenheit = 32 + 9 * value / 5
class Temperature:
celsius = Celsius()
def __init__(self, initial_f):
self.fahrenheit = initial_f
t = Temperature(212)
print(t.celsius)
t.celsius = 0
print(t.fahrenheit)
3을 , 분할이 ( python 3 Python 3 을을을을을을을을을을 ( ( ( ( ( (을을을을을 ( ( ( ( ( ( ( ()가 되어 있는지 확인해야 합니다Python 2 python python python 의의 python pythonた 、 Python 2 。/ 5.0
★★★★★★★★★★★★★★★★★」/ 9.0
는 다음과 같은 것을 말합니다.
100.0
32.0
이제 비단뱀에서도 같은 효과를 얻을 수 있는 다른, 거의 틀림없이 더 나은 방법이 있다(예를 들어 섭씨가 특성이라면, 그것은 같은 기본 메커니즘이지만 모든 소스를 온도 클래스 안에 배치한다). 하지만 그것은 무엇을 할 수 있는지를 보여준다...
Python의 디스크립터가 무엇인지, 어떤 용도로 유용하게 쓰일 수 있는지 이해하려고 합니다.
디스크립터는 인스턴스 속성(슬롯, 속성 또는 메서드 등)을 관리하는 클래스 네임스페이스 내의 객체입니다.예를 들어 다음과 같습니다.
class HasDescriptors:
__slots__ = 'a_slot' # creates a descriptor
def a_method(self): # creates a descriptor
"a regular method"
@staticmethod # creates a descriptor
def a_static_method():
"a static method"
@classmethod # creates a descriptor
def a_class_method(cls):
"a class method"
@property # creates a descriptor
def a_property(self):
"a property"
# even a regular function:
def a_function(some_obj_or_self): # creates a descriptor
"create a function suitable for monkey patching"
HasDescriptors.a_function = a_function # (but we usually don't do this)
일반적으로 디스크립터는 다음과 같은 특수한 메서드를 가진 오브젝트입니다.이러한 메서드는 "descriptor 메서드립터 메서드라고 불립니다.
__get__
의 메서드)__set__
슬롯의 등)__delete__
: 메서드 슬롯에서 ): "Data descriptor 메서드
이러한 기술자 개체는 다른 개체 클래스 네임스페이스의 속성입니다., 들, 음, 음, 음, 음, the, that, that, that, that, that, that, that, that, that, that, that, that.__dict__
클래스 오브젝트의
검색 방식으로 합니다( 도트 검색 결과:foo.descriptor
('나'나 '나'나 '나'나 '나'나 '나'처럼)
방식, "/", "/",property
,classmethod
, , , , 입니다.staticmethod
모두 이러한 특수한 방법을 사용하여 점 조회를 통해 액세스하는 방법을 제어합니다.
데이터 기술자, 예를 들어 있습니다.property
는 오브젝트 상태에 따라 Atribute를 느리게 평가할 수 있기 때문에 인스턴스는 각 Atribute를 사전에 계산했을 때보다 메모리를 적게 사용할 수 있습니다.
또 다른 데이터 기술자 amember_descriptor
에 의해 작성된 것으로, 보다 유연하지만 공간 소모가 큰 것이 아니라, 클래스가 데이터를 변환 가능한 태플과 같은 데이터 구조에 격납하는 것으로, 메모리 절약(및 고속 검색)이 가능하게 됩니다.__dict__
.
기술자, 및 인 첫 인수은 「」, 「」라고 하는 이름)를 취득합니다.self
★★★★★★★★★★★★★★★★★」cls
방식인 '__get__
이것이 스태틱 메서드가 암묵적인 첫 번째 인수를 갖지 않는 방법입니다.
Python의 대부분의 사용자는 디스크립터의 높은 사용법만 배우면 되고 디스크립터의 구현을 더 배우거나 이해할 필요가 없습니다.
그러나 기술자가 어떻게 작동하는지 이해하면 Python에 대한 숙달에 더 큰 자신감을 얻을 수 있다.
상세: 디스크립터란?
는 다음 중의 방법기술자)을 입니다.__get__
,__set__
, 「」__delete__
는, 인스턴스의 일반적인 어트리뷰트인 것처럼, 닷 첨부 문자를 사용해 사용하는 것을 목적으로 하고 있습니다. " " "obj_instance
, 를합니다.descriptor
★★★★★★★★★★★★★★★★★★:
obj_instance.descriptor
descriptor.__get__(self, obj_instance, owner_class)
, 반환value
및 「」는 되어 있습니다.get
동산사사사사사종종종obj_instance.descriptor = value
descriptor.__set__(self, obj_instance, value)
(Return)None
이렇게 '이렇게'가 됩니다.setter
★★★★★★★★★★★★★★★★★★del obj_instance.descriptor
descriptor.__delete__(self, obj_instance)
(Return)None
이렇게 '이렇게'가 됩니다.deleter
★★★★★★★★★★★★★★★★★★
obj_instance
는 클래스에 기술자 객체의 인스턴스가 포함된 인스턴스입니다. self
디스크립터의 인스턴스입니다(클래스에 1개만 포함).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
'CHANGE: 'CHANGE: 'CHANGE: 'CHANGE:
>>> is_descriptor(classmethod), is_data_descriptor(classmethod)
(True, False)
>>> is_descriptor(staticmethod), is_data_descriptor(staticmethod)
(True, False)
다 둘 have밖에 __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)
기술자, "Data Descriptorproperty
★★★★★★★★★★★★★★.property
입니다.
>>> is_data_descriptor(property)
True
>>> has_descriptor_attrs(property)
set(['__set__', '__get__', '__delete__'])
닷이 있는 조회 순서
이것들은 닷이 있는 룩업의 룩업순서에 영향을 미치기 때문에 중요한 차이입니다.
obj_instance.attribute
- 먼저 위의 내용은 속성이 인스턴스의 클래스에 있는 Data-Descriptor인지 확인합니다.
- 않으면 이 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()
- 디스크립터 클래스가 필요한 이유는 무엇입니까?
하면 이 인 "Descriptor"에 할 수 .Temperature
, 사용 del
Attribute at at at :
>>> t1 = Temperature()
>>> del t1.celsius
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: __delete__
그렇지 않으면 디스크립터는 오너 클래스 및 오너 인스턴스를 무시하고 스테이트를 디스크립터에 저장합니다.간단한 클래스 속성으로 모든 인스턴스에서 상태를 쉽게 공유할 수 있습니다(클래스로 항상 플로트로 설정하고 삭제하지 않거나 코드 사용자가 쉽게 삭제할 수 있는 경우).
class Temperature(object):
celsius = 0.0
하게 같은 질문 ). ( 「피톤스」, 「피톤스」가 사용되고 있습니다). 의, 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 가 이 은 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
결론
디스크립터를 정의하는 속성, 데이터 디스크립터와 비데이터 디스크립터의 차이, 디스크립터를 사용하는 빌트인 오브젝트, 사용에 관한 특정 질문에 대해 설명했습니다.
그럼 다시 질문의 예를 어떻게 사용하시겠습니까?그러지 않길 바래.첫 번째 제안(간단한 클래스 속성)에서 시작하여 필요하다고 생각되면 두 번째 제안(속성 장식가)으로 넘어가시기 바랍니다.
디스크립터의 상세를 설명하기 전에, Python의 속성 룩업이 어떻게 동작하는지를 아는 것이 중요합니다.인 """를 으로 가정합니다.__getattribute__
(양쪽 모두 동작을 「조정」하기 위해서 사용할 수 있습니다).
이 경우 속성 룩업(Python 3.x 또는 Python 2.x의 새로운 스타일의 클래스)의 가장 좋은 그림은 Python 메타클래스 이해(ionel의 코드 로그)에서 볼 수 있습니다.이미지 사용:
커스터마이즈할 수 없는 속성 검색 대신 사용됩니다.
은 Atribute의 .foobar
instance
Class
:
여기서는 다음 두 가지 조건이 중요합니다.
- 의
instance
name은 Atribute name으로 되어 있습니다.__get__
★★★★★★★★★★★★★★★★★」__set__
. - 경우,
instance
Atribute name 엔트리는 없지만 클래스에는 Atribute name 엔트리가 있습니다.__get__
.
여기서 설명자가 사용됩니다.
- 둘 다 있는 데이터 기술자
__get__
★★★★★★★★★★★★★★★★★」__set__
. - 데이터 기술자 이외의 기술자는
__get__
.
모두 은 '귀환'을 .__get__
인스턴스를 첫 번째 인수로, 클래스를 두 번째 인수로 호출합니다.
클래스 어트리뷰트 룩업은 더욱 복잡합니다(예를 들어 (상기 블로그의) 클래스 어트리뷰트 룩업을 참조).
구체적인 질문으로 넘어가겠습니다.
디스크립터 클래스가 필요한 이유는 무엇입니까?
대부분의 경우 기술자 클래스를 작성할 필요가 없습니다.하지만 당신은 아마도 매우 일반적인 최종 사용자일 것입니다.예를 들어 기능.는 이렇게와 같은 할 수 .self
첫 번째 인수로 암묵적으로 전달됩니다.
def test_function(self):
return self
class TestClass(object):
def test_method(self):
...
test_method
인스턴스에서는 "바운드 메서드"가 반환됩니다.
>>> instance = TestClass()
>>> instance.test_method
<bound method TestClass.test_method of <__main__.TestClass object at ...>>
도 할 수 .__get__
수동 방법(설명 목적으로만 권장되지 않음):
>>> test_function.__get__(instance, TestClass)
<bound method test_function of <__main__.TestClass object at ...>>
이 「셀프 바인드 방식」이라고 부를 수도 있습니다.
>>> test_function.__get__(instance, TestClass)()
<__main__.TestClass at ...>
인수를 제공하지 않았고 함수는 바인딩한 인스턴스를 반환했습니다.
함수는 데이터 설명자가 아닙니다!
로는 이이음음 、 음 、 음 、 음 、 음 、 음 、 음 、 음 음 。property
한시를 getter
,setter
, , , , 입니다.deleter
property
Descriptor는 다음과 같습니다(Descriptor HowTo Guide "Properties"
class Property(object):
def __init__(self, fget=None, fset=None, fdel=None, doc=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
if doc is None and fget is not None:
doc = fget.__doc__
self.__doc__ = doc
def __get__(self, obj, objtype=None):
if obj is None:
return self
if self.fget is None:
raise AttributeError("unreadable attribute")
return self.fget(obj)
def __set__(self, obj, value):
if self.fset is None:
raise AttributeError("can't set attribute")
self.fset(obj, value)
def __delete__(self, obj):
if self.fdel is None:
raise AttributeError("can't delete attribute")
self.fdel(obj)
됩니다.property
그리고 그것은 단순히 기능들을 위임한다.@property
,@name.setter
, , , , 입니다.@name.deleter
(일부러)
라이브러리에는 가 몇 개 . , '아까부터'입니다.staticmethod
,classmethod
.
디스크립터의 포인트는 간단합니다(거의 필요 없습니다만).속성 액세스를 위한 추상 공통 코드입니다. property
입니다.function
메서드의 입니다.staticmethod
없는 및 "인스턴스 액세스"를 입니다.classmethod
는 인스턴스 액세스가 아닌 클래스 액세스가 필요한 메서드에 대한 추상화를 제공합니다(이는 조금 단순합니다).
다른 예로는 클래스 속성을 들 수 있습니다.
예(「」를 사용)__set_name__
또한 Python 3.6)은 특정 유형만 허용하는 속성일 수도 있습니다.
class TypedProperty(object):
__slots__ = ('_name', '_type')
def __init__(self, typ):
self._type = typ
def __get__(self, instance, klass=None):
if instance is None:
return self
return instance.__dict__[self._name]
def __set__(self, instance, value):
if not isinstance(value, self._type):
raise TypeError(f"Expected class {self._type}, got {type(value)}")
instance.__dict__[self._name] = value
def __delete__(self, instance):
del instance.__dict__[self._name]
def __set_name__(self, klass, name):
self._name = name
그런 다음 클래스에서 설명자를 사용할 수 있습니다.
class Test(object):
int_prop = TypedProperty(int)
그리고 그걸 가지고 놀다 보면:
>>> t = Test()
>>> t.int_prop = 10
>>> t.int_prop
10
>>> t.int_prop = 20.0
TypeError: Expected class <class 'int'>, got <class 'float'>
또는 "게으른 재산":
class LazyProperty(object):
__slots__ = ('_fget', '_name')
def __init__(self, fget):
self._fget = fget
def __get__(self, instance, klass=None):
if instance is None:
return self
try:
return instance.__dict__[self._name]
except KeyError:
value = self._fget(instance)
instance.__dict__[self._name] = value
return value
def __set_name__(self, klass, name):
self._name = name
class Test(object):
@LazyProperty
def lazy(self):
print('calculating')
return 10
>>> t = Test()
>>> t.lazy
calculating
10
>>> t.lazy
10
예를 들어 로직을 공통 디스크립터로 이동하는 것이 타당할 수 있지만, 다른 방법으로 해결할 수도 있습니다(일부 코드를 반복하는 것으로 해결할 수도 있습니다.
죠?
instance
★★★★★★★★★★★★★★★★★」owner
__get__
). 들파 파파 ?적 적? ?? ???
속성을 검색하는 방법에 따라 다릅니다.인스턴스에서 Atribute를 검색하면 다음과 같이 됩니다.
- 두 번째 인수는 속성을 검색하는 인스턴스입니다.
- 세 번째 인수는 인스턴스의 클래스입니다.
클래스에서 Atribute를 조회하는 경우(Descriptor가 클래스에 정의되어 있다고 가정):
- 는 ""입니다.
None
- 세 번째 인수는 속성을 검색하는 클래스입니다.
룩업을 할 때의 을 커스터마이즈하는 세 이 인수는 클래스 레벨 룩업을 실행할 때 합니다).instance
None
를 참조해 주세요.
이 예를 어떻게 부르거나 사용할 수 있을까요?
예시는 입니다.float
클래스의 모든 인스턴스 간에 공유됩니다(클래스에서는 "읽기" 액세스만 사용할 수 있지만 그렇지 않으면 디스크립터 인스턴스를 대체합니다).
>>> t1 = Temperature()
>>> t2 = Temperature()
>>> t1.celsius = 20 # setting it on one instance
>>> t2.celsius # looking it up on another instance
20.0
>>> Temperature.celsius # looking it up on the class
20.0
두 번째 인수인수)를 사용합니다.instance
값을 저장하여 공유하지 않도록 합니다.그러나 인스턴스 간에 값을 공유하는 것이 바람직한 경우가 있습니다(현재로서는 시나리오가 생각나지 않습니다).학문적인 일 수 밖에 없다.순수하게 학문적인 운동일 수도 있어요
디스크립터 클래스가 필요한 이유는 무엇입니까?
Buciano Ramalho의 Fluent Python에서 영감을 받았습니다.
이런 수업이 있을 거라고 상상하면서
class LineItem:
price = 10.9
weight = 2.1
def __init__(self, name, price, weight):
self.name = name
self.price = price
self.weight = weight
item = LineItem("apple", 2.9, 2.1)
item.price = -0.9 # it's price is negative, you need to refund to your customer even you delivered the apple :(
item.weight = -0.8 # negative weight, it doesn't make sense
무게와 가격을 확인하여 음수를 할당하지 않도록 해야 합니다. 이렇게 설명자를 프록시로 사용하면 코드를 적게 쓸 수 있습니다.
class Quantity(object):
__index = 0
def __init__(self):
self.__index = self.__class__.__index
self._storage_name = "quantity#{}".format(self.__index)
self.__class__.__index += 1
def __set__(self, instance, value):
if value > 0:
setattr(instance, self._storage_name, value)
else:
raise ValueError('value should >0')
def __get__(self, instance, owner):
return getattr(instance, self._storage_name)
LineItem 클래스를 다음과 같이 정의합니다.
class LineItem(object):
weight = Quantity()
price = Quantity()
def __init__(self, name, weight, price):
self.name = name
self.weight = weight
self.price = price
Quantity 클래스를 확장하여 보다 일반적인 검증을 수행할 수 있습니다.
diagnosted ( (소화가 용이함)__get__ & __set__ & __call__
'무엇을'이라고 , '학번'이란 인가?Owner, Instance
다이빙을 하기 전에 머그업(mug up)을 해야 할 사항도 있습니다.
__get__ __set__
는 클래스의 디스크립터라고 불리며, 그 내부 어트리뷰트를 동작/저장합니다.즉, 다음과 같습니다.__name__
클래스 ), - (클래스/오너 클래스 이름), 변수 - (클래스/오너 클래스 이름)__dict__
소유자가 무엇인지 나중에 설명하겠습니다.- 디스크립터는 디자인 패터러에서 더 일반적으로 사용됩니다. 예를 들어 (물건을 추상화하기 위해) 데코레이터와 함께 사용합니다.소프트웨어 아키텍처 설계에서 용장성이 떨어지고 읽기 쉽도록 하는 데 더 자주 사용되는 것을 고려할 수 있습니다(비논리적으로 보입니다).따라서 솔리드 및 드라이 원칙을 준수합니다.
- SOLID와 DRY의 원칙에 준거하는 소프트웨어를 설계하고 있지 않은 경우는, 필요 없을지도 모르지만, 항상 그것들을 이해하는 것이 현명합니다.
1. 다음 코드를 고려하십시오.
class Method:
def __init__(self, name):
self.name = name
def __call__(self, instance, arg1, arg2):
print(f"{self.name}: {instance} called with {arg1} and {arg2}")
class MyClass:
method = Method("Internal call")
instance = MyClass()
instance.method("first", "second")
# Prints:TypeError: __call__() missing 1 required positional argument: 'arg2'
요?instance.method("first", "second")
라고 합니다.__call__
콜할 수 ).가 호출될 마다 호출할 수 있습니다.__call__
및가 할당됩니다.instance: "first", arg1: "second"
arg2는 arg2의 합니다.TypeError: __call__() missing 1 required positional argument: 'arg2'
2. 어떻게 해결할 것인가?
★★
__call__
이 걸리다instance
인수(first 인수,로 지정되지만, "first ", arg1, arg2)로 지정됩니다.instance
엇은어??? ??Instance
는 디스크립터 클래스(메서드)를 호출하는 메인클래스(MyClass)의 인스턴스입니다.so,는,instance = MyClass()
는 는 입니다.instance
''는 누구일까요?owner
- ?는 클래스입니다.MyClass
인 ,, 술, in, in, in, in, in, in, in, in, in, in, in, in, in, in, in, in, in in, ,(Method)
을 로로로 인식하다instance
이 바로 로 하는 것입니다__get__
다음를 한 번 .을 사용하다
from types import MethodType
class Method:
def __init__(self, name):
self.name = name
def __call__(self, instance, arg1, arg2):
print(f"{self.name}: {instance} called with {arg1} and {arg2}")
def __set__(self, instance, value):
self.value = value
instance.__dict__["method"] = value
def __get__(self, instance, owner):
if instance is None:
return self
print (instance, owner)
return MethodType(self, instance)
class MyClass:
method = Method("Internal call")
instance = MyClass()
instance.method("first", "second")
# Prints: Internal call: <__main__.MyClass object at 0x7fb7dd989690> called with first and second
docs에 따르면 현재 세트는 잊으십시오.
__get__
"오너 클래스(클래스 속성 액세스) 또는 해당 클래스의 인스턴스(인스턴스 속성 액세스)의 속성을 가져오기 위해 호출됩니다.
경우: " " " 입니다.instance.method.__get__(instance)
Prints:<__main__.MyClass object at 0x7fb7dd9eab90> <class '__main__.MyClass'>
, "의 오브젝트, ""의 오브젝트: ""의MyClass
, 「」입니다.instance
★★★★★★★★★★★★★★★★★」Owner
MyClass
자체
3. __set__
★★★★
__set__
값 됩니다.__dict__
object(명령줄을 사용한다고 가정합니다).set 내부값을 설정하는 명령어는 다음과 같습니다.instance.descriptor = 'value'
는 # " " " " 입니다.method
)
instance.__dict__["method"] = value
에서는 「를 실시합니다.__dict__
★★★★★★★★★★★★★★★★★★★★★★★★★」하다, 하다, 하다, 하다.
instance.method = 'value'
, 그럼 이제 .value = 'value'
설정에는 이 있습니다.__set__
할 수__dict__
'Descriptor'method
: .실행:instance.method.__dict__
★★★★★{'_name': 'Internal call', 'value': 'value'}
□□□을 하실 수 있습니다.
__dict__
를 사용한 값vars(instance.method)
★★★★★{'name': 'Internal call', 'value': 'value'}
됐으면
https://docs.python.org/3/howto/descriptor.html#properties 가 표시됩니다.
class Property(object):
"Emulate PyProperty_Type() in Objects/descrobject.c"
def __init__(self, fget=None, fset=None, fdel=None, doc=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
if doc is None and fget is not None:
doc = fget.__doc__
self.__doc__ = doc
def __get__(self, obj, objtype=None):
if obj is None:
return self
if self.fget is None:
raise AttributeError("unreadable attribute")
return self.fget(obj)
def __set__(self, obj, value):
if self.fset is None:
raise AttributeError("can't set attribute")
self.fset(obj, value)
def __delete__(self, obj):
if self.fdel is None:
raise AttributeError("can't delete attribute")
self.fdel(obj)
def getter(self, fget):
return type(self)(fget, self.fset, self.fdel, self.__doc__)
def setter(self, fset):
return type(self)(self.fget, fset, self.fdel, self.__doc__)
def deleter(self, fdel):
return type(self)(self.fget, self.fset, fdel, self.__doc__)
Andrew Cooke의 답변에서 (제안대로 사소한 변경을 가하여) 코드를 시험해 보았습니다.(저는 python 2.7을 실행하고 있습니다.)
코드:
#!/usr/bin/env python
class Celsius:
def __get__(self, instance, owner): return 9 * (instance.fahrenheit + 32) / 5.0
def __set__(self, instance, value): instance.fahrenheit = 32 + 5 * value / 9.0
class Temperature:
def __init__(self, initial_f): self.fahrenheit = initial_f
celsius = Celsius()
if __name__ == "__main__":
t = Temperature(212)
print(t.celsius)
t.celsius = 0
print(t.fahrenheit)
그 결과:
C:\Users\gkuhn\Desktop>python test2.py
<__main__.Celsius instance at 0x02E95A80>
212
Python 3보다 이전 버전에서는 get magic이 오래된 스타일의 클래스에서는 동작하지 않기 때문에 디스크립터를 올바르게 동작시키는 오브젝트에서 서브클래스를 실행해 주세요.
언급URL : https://stackoverflow.com/questions/3798835/understanding-get-and-set-and-python-descriptors
'source' 카테고리의 다른 글
--all-databases 덤프에서 단일 데이터베이스 가져오기 (0) | 2022.11.18 |
---|---|
Java에서 디렉터리 내용을 삭제하는 방법 (0) | 2022.11.18 |
오류와 예외의 차이점은 무엇입니까? (0) | 2022.11.18 |
캔 원 앵귤러JS 컨트롤러가 다른 컨트롤러를 호출합니까? (0) | 2022.11.18 |
PHP로 작성된 괜찮은 PHP 파서는 없습니까? (0) | 2022.11.18 |