소개
이 랩에서는 Python 에서 클래스를 생성하는 데 관련된 저수준 단계를 배우게 됩니다. type() 함수를 사용하여 클래스가 어떻게 구성되는지 이해하면 Python 의 객체 지향 기능을 더 깊이 이해할 수 있습니다.
또한 사용자 정의 클래스 생성 기술을 구현할 것입니다. 이 랩에서는 validate.py 및 structure.py 파일을 수정하여 새로운 지식을 실제 환경에 적용할 수 있습니다.
이 랩에서는 Python 에서 클래스를 생성하는 데 관련된 저수준 단계를 배우게 됩니다. type() 함수를 사용하여 클래스가 어떻게 구성되는지 이해하면 Python 의 객체 지향 기능을 더 깊이 이해할 수 있습니다.
또한 사용자 정의 클래스 생성 기술을 구현할 것입니다. 이 랩에서는 validate.py 및 structure.py 파일을 수정하여 새로운 지식을 실제 환경에 적용할 수 있습니다.
Python 프로그래밍에서 클래스는 데이터와 함수를 함께 그룹화할 수 있는 기본적인 개념입니다. 일반적으로 표준 Python 구문을 사용하여 클래스를 정의합니다. 예를 들어, 간단한 Stock 클래스가 있습니다. 이 클래스는 name, shares, price와 같은 속성을 가진 주식을 나타내며, 비용을 계산하고 주식을 판매하는 메서드를 가지고 있습니다.
class Stock:
def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
def cost(self):
return self.shares * self.price
def sell(self, nshares):
self.shares -= nshares
하지만 Python 이 실제로 어떻게 클래스를 내부적으로 생성하는지 궁금했던 적이 있습니까? 표준 클래스 구문을 사용하지 않고 이 클래스를 생성하려면 어떻게 해야 할까요? 이 섹션에서는 Python 클래스가 더 낮은 수준에서 어떻게 구성되는지 살펴보겠습니다.
수동 클래스 생성을 실험하려면 Python 대화형 셸을 열어야 합니다. 이 셸을 사용하면 Python 코드를 한 줄씩 실행할 수 있으며, 이는 학습 및 테스트에 매우 유용합니다.
WebIDE 에서 터미널을 열고 다음 명령을 입력하여 Python 대화형 셸을 시작합니다. 첫 번째 명령 cd ~/project는 현재 디렉토리를 프로젝트 디렉토리로 변경하고, 두 번째 명령 python3는 Python 3 대화형 셸을 시작합니다.
cd ~/project
python3
클래스를 수동으로 생성하기 전에 클래스의 일부가 될 메서드를 정의해야 합니다. Python 에서 메서드는 클래스와 관련된 함수일 뿐입니다. 따라서 클래스에 원하는 메서드를 일반 Python 함수로 정의해 보겠습니다.
def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
def cost(self):
return self.shares * self.price
def sell(self, nshares):
self.shares -= nshares
여기서 __init__ 함수는 Python 클래스의 특수 메서드입니다. 생성자 (constructor) 라고 하며, 클래스의 인스턴스가 생성될 때 객체의 속성을 초기화하는 데 사용됩니다. cost 메서드는 주식의 총 비용을 계산하고, sell 메서드는 주식 수를 줄입니다.
이제 메서드를 일반 함수로 정의했으므로 클래스를 생성할 때 Python 이 이해할 수 있도록 메서드를 구성해야 합니다. 클래스의 모든 메서드를 포함하는 딕셔너리를 생성하여 이 작업을 수행합니다.
methods = {
'__init__': __init__,
'cost': cost,
'sell': sell
}
이 딕셔너리에서 키는 클래스에서 사용될 메서드의 이름이고, 값은 앞에서 정의한 실제 함수 객체입니다.
type() 생성자를 사용하여 클래스 생성Python 에서 type() 함수는 더 낮은 수준에서 클래스를 생성하는 데 사용할 수 있는 내장 함수입니다. type() 함수는 세 개의 인수를 사용합니다.
(object,)를 사용하는데, 이는 클래스가 Python 의 모든 클래스의 기본 클래스인 기본 object 클래스에서 상속받는다는 의미입니다.Stock = type('Stock', (object,), methods)
이 코드 줄은 type() 함수를 사용하여 Stock이라는 새 클래스를 생성합니다. 이 클래스는 object 클래스에서 상속받고 methods 딕셔너리에 정의된 메서드를 갖습니다.
이제 클래스를 수동으로 생성했으므로 예상대로 작동하는지 테스트해 보겠습니다. 새 클래스의 인스턴스를 생성하고 해당 메서드를 호출합니다.
s = Stock('GOOG', 100, 490.10)
print(s.name)
print(s.cost())
s.sell(25)
print(s.shares)
첫 번째 줄에서 GOOG라는 이름, 100 주, 490.10 의 가격으로 Stock 클래스의 인스턴스를 생성합니다. 그런 다음 주식의 이름을 출력하고, 비용을 계산하여 출력하고, 25 주를 판매하고, 마지막으로 남은 주식 수를 출력합니다.
다음과 같은 출력이 표시됩니다.
GOOG
49010.0
75
이 출력은 수동으로 생성된 클래스가 표준 Python 구문을 사용하여 생성된 클래스와 마찬가지로 작동함을 보여줍니다. 이는 클래스가 기본적으로 이름, 기본 클래스 튜플, 메서드 및 속성의 딕셔너리임을 보여줍니다. type() 함수는 이러한 구성 요소에서 클래스 객체를 생성합니다.
작업이 완료되면 Python 셸을 종료합니다.
exit()
이 단계에서는 더 실용적인 예제를 구축할 것입니다. 형식 유효성 검사를 사용하여 클래스를 생성하는 함수를 구현합니다. 형식 유효성 검사는 클래스 속성에 할당된 데이터가 특정 기준 (예: 특정 데이터 형식 또는 특정 범위 내) 을 충족하는지 확인하므로 매우 중요합니다. 이를 통해 오류를 조기에 포착하고 코드를 더욱 강력하게 만들 수 있습니다.
먼저 WebIDE 편집기에서 structure.py 파일을 열어야 합니다. 이 파일에는 기본 Structure 클래스가 포함되어 있습니다. 이 클래스는 구조화된 객체를 초기화하고 나타내는 기본적인 기능을 제공합니다. 초기화는 제공된 데이터로 객체를 설정하는 것을 의미하고, 표현은 객체를 인쇄할 때 객체가 표시되는 방식에 관한 것입니다.
파일을 열려면 터미널에서 다음 명령을 사용합니다.
cd ~/project
이 명령을 실행하면 structure.py 파일이 있는 올바른 디렉토리에 있게 됩니다. 파일을 열면 기본 Structure 클래스를 볼 수 있습니다. 우리의 목표는 이 클래스를 확장하여 형식 유효성 검사를 지원하는 것입니다.
이제 typed_structure 함수를 structure.py 파일에 추가해 보겠습니다. 이 함수는 Structure 클래스에서 상속하고 지정된 유효성 검사기를 포함하는 새 클래스를 생성합니다. 상속은 새 클래스가 Structure 클래스의 모든 기능을 갖게 되며 자체 기능을 추가할 수도 있음을 의미합니다. 유효성 검사기는 클래스 속성에 할당된 값이 유효한지 확인하는 데 사용됩니다.
다음은 typed_structure 함수의 코드입니다.
def typed_structure(clsname, **validators):
"""
Create a Structure class with type validation.
Parameters:
- clsname: Name of the class to create
- validators: Keyword arguments mapping attribute names to validator objects
Returns:
- A new class with the specified name and validators
"""
cls = type(clsname, (Structure,), validators)
return cls
clsname 매개변수는 새 클래스에 지정하려는 이름입니다. validators 매개변수는 키가 속성 이름이고 값이 유효성 검사기 객체인 딕셔너리입니다. type() 함수는 새 클래스를 동적으로 생성하는 데 사용됩니다. 클래스 이름, 기본 클래스 튜플 (이 경우 Structure 클래스만 해당) 및 클래스 속성 (유효성 검사기) 의 딕셔너리, 이렇게 세 개의 인수를 사용합니다.
이 함수를 추가한 후 structure.py 파일은 다음과 같이 표시됩니다.
## Structure class definition
class Structure:
_fields = ()
def __init__(self, *args, **kwargs):
if len(args) > len(self._fields):
raise TypeError(f'Expected {len(self._fields)} arguments')
## Set all of the positional arguments
for name, value in zip(self._fields, args):
setattr(self, name, value)
## Set the remaining keyword arguments
for name, value in kwargs.items():
setattr(self, name, value)
def __repr__(self):
attrs = ', '.join(f'{name}={getattr(self, name)!r}' for name in self._fields)
return f'{type(self).__name__}({attrs})'
def typed_structure(clsname, **validators):
"""
Create a Structure class with type validation.
Parameters:
- clsname: Name of the class to create
- validators: Keyword arguments mapping attribute names to validator objects
Returns:
- A new class with the specified name and validators
"""
cls = type(clsname, (Structure,), validators)
return cls
validate.py 파일의 유효성 검사기를 사용하여 typed_structure 함수를 테스트해 보겠습니다. 이러한 유효성 검사기는 클래스 속성에 할당된 값이 올바른 형식인지 확인하고 다른 기준을 충족하는지 확인하는 데 사용됩니다.
먼저 Python 대화형 셸을 엽니다. 터미널에서 다음 명령을 사용합니다.
cd ~/project
python3
첫 번째 명령은 올바른 디렉토리로 이동하고, 두 번째 명령은 Python 대화형 셸을 시작합니다.
이제 필요한 구성 요소를 가져와서 형식화된 구조를 생성합니다.
from validate import String, PositiveInteger, PositiveFloat
from structure import typed_structure
## Create a Stock class with type validation
Stock = typed_structure('Stock', name=String(), shares=PositiveInteger(), price=PositiveFloat())
## Create a stock instance
s = Stock('GOOG', 100, 490.1)
## Test the instance
print(s.name)
print(s)
## Test validation
try:
invalid_stock = Stock('AAPL', -10, 150.25) ## Should raise an error
except ValueError as e:
print(f"Validation error: {e}")
validate.py 파일에서 String, PositiveInteger, PositiveFloat 유효성 검사기를 가져옵니다. 그런 다음 typed_structure 함수를 사용하여 형식 유효성 검사가 있는 Stock 클래스를 생성합니다. Stock 클래스의 인스턴스를 생성하고 속성을 인쇄하여 테스트합니다. 마지막으로 유효성 검사를 테스트하기 위해 잘못된 주식 인스턴스를 생성하려고 합니다.
다음과 유사한 출력이 표시됩니다.
GOOG
Stock('GOOG', 100, 490.1)
Validation error: Expected a positive value
테스트를 마쳤으면 Python 셸을 종료합니다.
exit()
이 예제는 type() 함수를 사용하여 특정 유효성 검사 규칙이 있는 사용자 정의 클래스를 생성하는 방법을 보여줍니다. 이 접근 방식은 클래스를 프로그래밍 방식으로 생성할 수 있으므로 매우 강력하며, 이는 많은 시간을 절약하고 코드를 더 유연하게 만들 수 있습니다.
이제 type() 함수를 사용하여 클래스를 생성하는 방법을 이해했으므로, 여러 개의 유사한 클래스를 생성하는 더 효율적인 방법을 살펴보겠습니다. 이 방법은 시간을 절약하고 코드 중복을 줄여 프로그래밍 프로세스를 더욱 원활하게 만듭니다.
먼저 WebIDE 에서 validate.py 파일을 열어야 합니다. 이 파일에는 이미 여러 유효성 검사기 클래스가 포함되어 있으며, 이는 값이 특정 조건을 충족하는지 확인하는 데 사용됩니다. 이러한 클래스에는 Validator, Positive, PositiveInteger, PositiveFloat가 포함됩니다. 이 파일에 Typed 기본 클래스와 여러 형식 관련 유효성 검사기를 추가할 것입니다.
파일을 열려면 터미널에서 다음 명령을 실행합니다.
cd ~/project
Typed 유효성 검사기 클래스를 추가하는 것으로 시작해 보겠습니다. 이 클래스는 값이 예상되는 형식인지 확인하는 데 사용됩니다.
class Typed(Validator):
expected_type = object ## Default, will be overridden in subclasses
@classmethod
def check(cls, value):
if not isinstance(value, cls.expected_type):
raise TypeError(f'Expected {cls.expected_type}')
super().check(value)
이 코드에서 expected_type은 기본적으로 object로 설정됩니다. 하위 클래스는 이를 확인하려는 특정 형식으로 재정의합니다. check 메서드는 isinstance 함수를 사용하여 값이 예상되는 형식인지 확인합니다. 그렇지 않은 경우 TypeError를 발생시킵니다.
기존에는 다음과 같이 형식 관련 유효성 검사기를 생성했습니다.
class Integer(Typed):
expected_type = int
class Float(Typed):
expected_type = float
class String(Typed):
expected_type = str
그러나 이 접근 방식은 반복적입니다. type() 생성자를 사용하여 이러한 클래스를 동적으로 생성하여 더 나은 결과를 얻을 수 있습니다.
개별 클래스 정의를 보다 효율적인 접근 방식으로 대체합니다.
_typed_classes = [
('Integer', int),
('Float', float),
('String', str)
]
globals().update((name, type(name, (Typed,), {'expected_type': ty}))
for name, ty in _typed_classes)
이 코드가 수행하는 작업은 다음과 같습니다.
type() 함수와 함께 제너레이터 표현식을 사용하여 각 클래스를 생성합니다. type() 함수는 클래스 이름, 기본 클래스 튜플 및 클래스 속성의 딕셔너리, 이렇게 세 개의 인수를 사용합니다.globals().update()를 사용하여 새로 생성된 클래스를 전역 네임스페이스에 추가합니다. 이렇게 하면 모듈 전체에서 클래스에 액세스할 수 있습니다.완성된 validate.py 파일은 다음과 같이 표시됩니다.
## Basic validator classes
class Validator:
def __set_name__(self, owner, name):
self.name = name
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__[self.name]
def __set__(self, instance, value):
self.check(value)
instance.__dict__[self.name] = value
@classmethod
def check(cls, value):
pass
class Positive(Validator):
@classmethod
def check(cls, value):
if value <= 0:
raise ValueError('Expected a positive value')
super().check(value)
class PositiveInteger(Positive):
@classmethod
def check(cls, value):
if not isinstance(value, int):
raise TypeError('Expected an integer')
super().check(value)
class PositiveFloat(Positive):
@classmethod
def check(cls, value):
if not isinstance(value, float):
raise TypeError('Expected a float')
super().check(value)
class Typed(Validator):
expected_type = object ## Default, will be overridden in subclasses
@classmethod
def check(cls, value):
if not isinstance(value, cls.expected_type):
raise TypeError(f'Expected {cls.expected_type}')
super().check(value)
## Generate type validators dynamically
_typed_classes = [
('Integer', int),
('Float', float),
('String', str)
]
globals().update((name, type(name, (Typed,), {'expected_type': ty}))
for name, ty in _typed_classes)
이제 동적으로 생성된 유효성 검사기 클래스를 테스트해 보겠습니다. 먼저 Python 대화형 셸을 엽니다.
cd ~/project
python3
Python 셸에 들어가면 유효성 검사기를 가져와 테스트합니다.
from validate import Integer, Float, String
## Test the Integer validator
i = Integer()
i.__set_name__(None, 'test_int')
try:
i.check("not an integer")
print("Error: Check passed when it should have failed")
except TypeError as e:
print(f"Integer validation: {e}")
## Test the String validator
s = String()
s.__set_name__(None, 'test_str')
try:
s.check(123)
print("Error: Check passed when it should have failed")
except TypeError as e:
print(f"String validation: {e}")
## Add a new validator class to the list
import sys
print("Current validator classes:", [cls for cls in dir() if cls in ['Integer', 'Float', 'String']])
형식 유효성 검사 오류를 보여주는 출력이 표시됩니다. 이는 동적으로 생성된 클래스가 올바르게 작동하고 있음을 나타냅니다.
테스트를 마쳤으면 Python 셸을 종료합니다.
exit()
더 많은 형식 유효성 검사기를 추가하려면 validate.py에서 _typed_classes 목록을 업데이트하면 됩니다.
_typed_classes = [
('Integer', int),
('Float', float),
('String', str),
('List', list),
('Dict', dict),
('Bool', bool)
]
이 접근 방식은 반복적인 코드를 작성하지 않고 여러 개의 유사한 클래스를 생성하는 강력하고 효율적인 방법을 제공합니다. 요구 사항이 증가함에 따라 애플리케이션을 쉽게 확장할 수 있습니다.
이 Lab 에서는 Python 에서 클래스를 생성하는 하위 수준 메커니즘에 대해 배웠습니다. 먼저, 클래스 이름, 기본 클래스 튜플 및 메서드 딕셔너리가 필요한 type() 생성자를 사용하여 클래스를 수동으로 생성하는 방법을 익혔습니다. 둘째, 유효성 검사 기능을 갖춘 클래스를 동적으로 생성하기 위해 typed_structure 함수를 구현했습니다.
또한, type() 생성자를 globals().update()와 함께 사용하여 여러 개의 유사한 클래스를 효율적으로 생성하여 반복적인 코드를 피했습니다. 이러한 기술은 프레임워크, 라이브러리 및 메타 프로그래밍에서 유용한 클래스를 프로그래밍 방식으로 생성하고 사용자 정의하는 강력한 방법을 제공합니다. 이러한 기본 메커니즘을 이해하면 Python 의 객체 지향 기능에 대한 통찰력이 깊어지고 더 고급 프로그래밍이 가능해집니다.