출처 :https://www.pythontutorial.net/python-oop/python-descriptors/
Introduction to the Python descriptors
두 개의 인스턴스 속성 ( first_name와 last_name ) 을 가진 Person 클래스가 있다고 가정합니다.
class Person:
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
그리고 first_name 및 last_name 속성이 비어 있지 않은 문자열이 되기를 원합니다. 하지만 일반 속성은 이를 보장할 수 없습니다.
데이터 유효성을 적용하려면 다음과 같이 property 의 getter 및 setter 메서드를 사용하여 구현 할 수 있습니다.
class Person:
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
@property
def first_name(self):
return self._first_name
@first_name.setter
def first_name(self, value):
if not isinstance(value, str):
raise ValueError('The first name must a string')
if len(value) == 0:
raise ValueError('The first name cannot be empty')
self._first_name = value
@property
def last_name(self):
return self._last_name
@last_name.setter
def last_name(self, value):
if not isinstance(value, str):
raise ValueError('The last name must a string')
if len(value) == 0:
raise ValueError('The last name cannot be empty')
self._last_name = value
이 Person 클래스에서 getter는 속성 값을 반환하고 setter는 속성에 할당하기 전에 유효성을 검사합니다.
이 코드는 완벽하게 작동합니다. 그러나 이름과 성을 검증하는 유효성 검사 논리가 동일하여 중복이다.
또한 클래스에 비어 있지 않은 문자열이 필요한 property가 더 많으면 이 논리를 다른 property에 복제해야합니다. 하지만, 이 유효성 검사 논리는 다시 사용할 수 없습니다.
논리 중복을 방지하려면 데이터의 유효성을 검사하고 다른 property에서 이 메서드를 재사용하는 메서드가 있을 수 있습니다. 이 방법을 사용하면 재사용이 가능합니다. 그러나 Python은 descriptor(설명자)를 사용하여 이 문제를 해결하는 더 좋은 방법을 가지고 있습니다.
먼저 세 가지 메서드 __set_name__, __get__ 및 __set__ 구현하는 descriptor 클래스를 정의합니다.
class RequiredString:
def __set_name__(self, owner, name):
self.property_name = name
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__[self.property_name] or None
def __set__(self, instance, value):
if not isinstance(value, str):
raise ValueError(f'The {self.property_name} must be a string')
if len(value) == 0:
raise ValueError(f'The {self.property_name} cannot be empty')
instance.__dict__[self.property_name] = value
Second, use the RequiredString
class in the Person
class:
둘째, Person 클래스에서 필수 문자열 클래스를 사용합니다.
class Person:
first_name = RequiredString()
last_name = RequiredString()
If you assign an empty string or a non-string value to the first_name
or last_name
attribute of the Person
class, you’ll get an error.
For example, the following attempts to assign an empty string to the first_name
attribute:
Person 클래스의 first_name 또는 last_name 속성에 빈 문자열 또는 문자열이 아닌 값을 할당 하면 오류가 발생합니다.
예를 들어 다음은 first_name 속성에 빈 문자열을 할당하려고 시도합니다.
try:
person = Person()
person.first_name = ''
except ValueError as e:
print(e)
Error:
The first_name must be a string
Also, you can use the RequiredString
class in any class with attributes that require a non-empty string value.
Besides the RequiredString
, you can define other descriptors to enforce other data types like age, email, and phone. And this is just a simple application of the descriptors.
Let’s understand how descriptors work.
또한 비어 있지 않은 문자열 값이 필요한 속성이 있는 모든 클래스에서 RequiredString 클래스를 사용할 수 있습니다.
RequiredString 외에도 다른 descriptor를 정의하여 나이, 전자 메일 및 전화와 같은 다른 데이터 형식을 적용할 수 있습니다. 그리고 이것은 descriptor의 간단한 응용 프로그램 일뿐입니다.
descriptor의 작동 방식을 이해합시다.
Descriptor protocol
In Python, the descriptor protocol consists of three methods:
__get__
gets an attribute value__set__
sets an attribute value__delete__
deletes an attribute
Optionally, a descriptor can have the __set_name__
method that sets an attribute on an instance of a class to a new value.
Python에서 descriptor 프로토콜은 세 가지 방법으로 구성됩니다.
· __get__ 속성 값을 가져옵니다.
· __set__ 속성 값을 설정합니다.
· __delete__ 속성을 삭제합니다.
선택적으로 descriptor에는 클래스 인스턴스의 속성을 새 값으로 설정하는 __set_name__ 메서드가 있을 수 있습니다.
What is a descriptor
A descriptor is an object of a class that implements one of the methods specified in the descriptor protocol.
Descriptors have two types: data descriptor and non-data descriptor.
- A data descriptor is an object of a class that implements the
__set__
and/or__delete__
method. - A non-data descriptor is an object that implements the
__get__
method only.
The descriptor type specifies the property lookup resolution that we’ll cover in the next tutorial.
descriptor는descriptor 프로토콜에 지정된 메서드 중 하나를 구현하는 클래스의 obje ct입니다.
descriptor에는 데이터 descriptor와 비데이터 descriptor의 두 가지 유형이 있습니다.
1. 데이터 descriptor는 __set__ 및/또는 __delete__ 메서드를 구현하는 클래스의 개체입니다.
2. n-data descriptor 없음은 __get__ 메서드만 구현하는 개체입니다 .
descriptor 형식은 다음 자습서에서 다룰 속성 조회 해상도를 지정합니다.
How descriptors work
The following modifies the RequiredString
class to include the print
statements that print out the arguments.
폴로 윙은 인수를 출력하는 인쇄 문을 포함하도록 RequiredString 클래스를수정합니다.
class RequiredString:
def __set_name__(self, owner, name):
print(f'__set_name__ was called with owner={owner} and name={name}')
self.property_name = name
def __get__(self, instance, owner):
print(f'__get__ was called with instance={instance} and owner={owner}')
if instance is None:
return self
return instance.__dict__[self.property_name] or None
def __set__(self, instance, value):
print(f'__set__ was called with instance={instance} and value={value}')
if not isinstance(value, str):
raise ValueError(f'The {self.property_name} must a string')
if len(value) == 0:
raise ValueError(f'The {self.property_name} cannot be empty')
instance.__dict__[self.property_name] = value
class Person:
first_name = RequiredString()
last_name = RequiredString()
The __set_name__
method
When you compile the code, you’ll see that Python creates the descriptor objects for first_name
and last_name
and automatically call the __set_name__
method of these objects. Here’s the output:
코드를 컴파일할 때 Python이 first_name 및 last_name에 대한 descriptor 개체를 만들고 이러한 개체의 __set_name__ 메서드를 자동으로 호출 하는 것을 볼 수 있습니다 . 출력은 다음과 같습니다.
__set_name__ was called with owner=<class '__main__.Person'> and name=first_name
__set_name__ was called with owner=<class '__main__.Person'> and name=last_name
In this example, the owner argument of __set_name__
is set to the Person
class in the __main__
module, and the name
argument is set to the first_name
and last_name
attribute accordingly.
It means that Python automatically calls the __set_name__
when the owning class Person
is created. The following statements are equivalent:
이 예제에서 __set_name__의 소유자 인수는 __main__ 모듈의 Person 클래스 로 설정 되고 그에 따라 name 인수는 first_name 및 last_name 속성으로 설정됩니다.
이는 파이썬이 소유 클래스 Person 이 생성 될 때 자동으로 __set_name__ 호출한다는 것을 의미합니다 . 다음 문은 동일합니다.
first_name = RequiredString()
and
first_name.__set_name__(Person, 'first_name')
Inside, the __set_name__
method, we assign the name
argument to the property_name
instance attribute of the descriptor
object so that we can access it later in the __get__
and __set__
method:
__set_name__ 메서드 내에서 descriptor 개체의 property_name 인스턴스 속성에 name 인수 를 할당하여 나중에 __get__ 및 __set__ 메서드에서 액세스할 수 있도록 합니다.
self.property_name = name
The first_name
and last_name
are the class variables of the Person
class. If you look at the Person.__dict__
class attribute, you’ll see two descriptor objects first_name
and last_name
:
first_name와 last_name는 Person 클래스의 클래스 변수입니다 . Person.__dict__ class 속성을 보면 두 개의 descriptor 개체가 first_name되고 last_name 표시됩니다.
from pprint import pprint
pprint(Person.__dict__)
Output:
mappingproxy({'__dict__': <attribute '__dict__' of 'Person' objects>,
'__doc__': None,
'__module__': '__main__',
'__weakref__': <attribute '__weakref__' of 'Person' objects>,
'first_name': <__main__.RequiredString object at 0x0000019D6AB947F0>,
'last_name': <__main__.RequiredString object at 0x0000019D6ACFBE80>})
The __set__
method
Here’s the __set__
method of the RequiredString
class:
다음은 필수 문자열 클래스의 __set__ 메서드입니다.
def __set__(self, instance, value):
print(f'__set__ was called with instance={instance} and value={value}')
if not isinstance(value, str):
raise ValueError(f'The {self.property_name} must be a string')
if len(value) == 0:
raise ValueError(f'The {self.property_name} cannot be empty')
instance.__dict__[self.property_name] = value
When you assign the new value to a descriptor, Python calls __set__
method to set the attribute on an instance of the owner class to the new value. For example:
descriptor에 새 값을 할당하면 Python __set__ 메서드를 호출하여 소유자 클래스 인스턴스의 속성을 새 값으로 설정합니다. 예를 들어:
person = Person()
person.first_name = 'John'
Output:
__set__ was called with instance=<__main__.Person object at 0x000001F85F7167F0> and value=John
In this example, the instance
argument is person
object and the value is the string 'John'
. Inside the __set__
method, we raise a ValueError
if the new value is not a string or if it is an empty string.
Otherwise, we assign the value to the instance attribute first_name
of the person
object:
이 예제에서 인스턴스 인수 는 person 객체이고 값은 문자열 'John'입니다. __set__ 메서드 내에서 새 값이 문자열이 아니거나 빈 문자열인 경우 ValueError를 발생시킵니다.
그렇지 않으면 person 객체의 인스턴스 속성 first_name에 값을 할당 합니다.
instance.__dict__[self.property_name] = value
Note that Python uses instance.__dict__
dictionary to store instance attributes of the instance
object.
Once you set the first_name
and last_name
of an instance of the Person
object, you’ll see the instance attributes with the same names in the instance’s __dict__
. For example:
Python instance.__dict__ 사전을 사용하여 인스턴스 객체 의 인스턴스 속성을 저장합니다 .
Person 객체의 인스턴스 first_name 및 last_name 설정하면 내부 __dict__에 동일한 이름의 인스턴스 속성이 표시됩니다. 예를 들어:
person = Person()
print(person.__dict__) # {}
person.first_name = 'John'
person.last_name = 'Doe'
print(person.__dict__) # {'first_name': 'John', 'last_name': 'Doe'}
Output:
{}
{'first_name': 'John', 'last_name': 'Doe'}
The __get__
method
The following shows the __get__
method of the RequiredString
class:
다음은 필수 문자열 클래스의 __get__ 메서드를 보여 줍니다 .
def __get__(self, instance, owner):
print(f'__get__ was called with instance={instance} and owner={owner}')
if instance is None:
return self
return instance.__dict__[self.property_name] or None/pre>
Python calls the __get__
method of the Person
‘s object when you access the first_name
attribute. For example:
파이썬은 first_name 속성에 액세스할 때 Person 객체의 __get__ 메서드를 호출합니다 . 예를 들어:
person = Person()
person.first_name = 'John'
print(person.first_name)
Output:
__set__ was called with instance=<__main__.Person object at 0x000001F85F7167F0> and value=John
__get__ was called with instance=<__main__.Person object at 0x000001F85F7167F0> and owner=<class '__main__.Person'>
The __get__
method returns the descriptor if the instance
is None
. For example, if you access the first_name
or last_name
from the Person
class, you’ll see the descriptor object:
__get__ 메서드는 인스턴스가 None인 경우 descriptor를 반환합니다. F 또는 예를 들어 Person 클래스 에서 first_name 또는 last_name에 액세스하면 descriptor 개체가 표시됩니다.
print(Person.first_name)
Output:
<__main__.RequiredString object at 0x000001AF1DA147F0>
If the instance
is not None
, the __get__()
method returns the value of the attribute with the name property_name
of the instance
object.
인스턴스가 None이 아닌 경우 __get__() 메서드는 인스턴스 객체의 이름 property_name와 함께 속성 값을 반환합니다.
Summary
- Descriptors are objects of class that implements one of the method in the descriptor protocol including
__set__
,__get__
,__del__
· descriptor는 __set__, __get__ __del__ 포함하는 descriptor 프로토콜의 메서드 중 하나를 구현하는 클래스의 개체입니다.
'Python Object-oriented Programming' 카테고리의 다른 글
enum 기타 기능 (0) | 2023.04.07 |
---|---|
Data vs. Non-data Descriptors (0) | 2023.04.04 |
Multiple inheritance (0) | 2023.04.04 |
Interface Segregation Principle (0) | 2023.04.04 |
Liskov Substitution Principle (0) | 2023.04.04 |
댓글