다음과 같은 간단한 Python 클래스가 있습니다.
class Spam(object):
__init__(self, description, value):
self.description = description
self.value = value
다음 제약 조건을 확인하고 싶습니다.
- “설명은 비워 둘 수 없습니다.”
- “값은 0보다 커야합니다.”
1. 스팸 개체를 만들기 전에 데이터의 유효성을 검사 해야합니까 ?
2. __init__
방법 에 대한 데이터를 확인 ?
3. is_valid
Spam 클래스에 메서드를 만들고 spam.isValid ()로 호출합니까?
4. is_valid
Spam 클래스에 정적 메서드를 만들고 Spam.isValid (description, value)?
5. setters 선언에 대한 데이터를 확인합니까?
6. 등
잘 디자인 된 / 파이 토닉 / 장황하지 않은 (많은 속성을 가진 수업에서) / 우아한 접근 방식을 추천 해 주시겠습니까?
답변
Python 속성 을 사용 하여 각 필드에 개별적으로 규칙을 명확하게 적용하고 클라이언트 코드가 필드를 변경하려고 할 때에도 적용 할 수 있습니다.
class Spam(object):
def __init__(self, description, value):
self.description = description
self.value = value
@property
def description(self):
return self._description
@description.setter
def description(self, d):
if not d: raise Exception("description cannot be empty")
self._description = d
@property
def value(self):
return self._value
@value.setter
def value(self, v):
if not (v > 0): raise Exception("value must be greater than zero")
self._value = v
__init__
함수 내에서도 규칙을 위반하려는 모든 시도에서 예외가 발생하며이 경우 객체 생성이 실패합니다.
업데이트 : 2010 년과 지금 사이에 operator.attrgetter
다음 사항에 대해 배웠습니다 .
import operator
class Spam(object):
def __init__(self, description, value):
self.description = description
self.value = value
description = property(operator.attrgetter('_description'))
@description.setter
def description(self, d):
if not d: raise Exception("description cannot be empty")
self._description = d
value = property(operator.attrgetter('_value'))
@value.setter
def value(self, v):
if not (v > 0): raise Exception("value must be greater than zero")
self._value = v
답변
개체가 생성 될 때만 값의 유효성을 검사하고 잘못된 값을 전달하는 것이 프로그래밍 오류로 간주되면 어설 션을 사용합니다.
class Spam(object):
def __init__(self, description, value):
assert description != ""
assert value > 0
self.description = description
self.value = value
이것은 당신이 얻는 것만 큼 간결하며 이것이 객체를 만들기위한 전제 조건임을 명확하게 문서화합니다.
답변
직접 롤링하지 않는 한 formencode 를 사용할 수 있습니다 . 많은 속성과 스키마 (하위 클래스 스키마 만)로 정말 빛나고 유용한 유효성 검사기가 많이 내장되어 있습니다. 보시다시피 이것은 “스팸 개체를 만들기 전에 데이터 유효성 검사”접근 방식입니다.
from formencode import Schema, validators
class SpamSchema(Schema):
description = validators.String(not_empty=True)
value = validators.Int(min=0)
class Spam(object):
def __init__(self, description, value):
self.description = description
self.value = value
## how you actually validate depends on your application
def validate_input( cls, schema, **input):
data = schema.to_python(input) # validate `input` dict with the schema
return cls(**data) # it validated here, else there was an exception
# returns a Spam object
validate_input( Spam, SpamSchema, description='this works', value=5)
# raises an exception with all the invalid fields
validate_input( Spam, SpamSchema, description='', value=-1)
당신은 또한 검사를 할 수 있지만 __init__
(그리고 descriptors | decorators | metaclass로 완전히 투명하게 만들 수 있습니다),하지만 저는 그 팬이 아닙니다. 저는 사용자 입력과 내부 개체 사이의 깨끗한 장벽을 좋아합니다.
답변
생성자에 전달 된 값만 검증하려면 다음을 수행 할 수 있습니다.
class Spam(object):
def __init__(self, description, value):
if not description or value <=0:
raise ValueError
self.description = description
self.value = value
물론 이것은 누군가가 다음과 같은 일을하는 것을 막을 수 없습니다.
>>> s = Spam('s', 5)
>>> s.value = 0
>>> s.value
0
따라서 올바른 접근 방식은 수행하려는 작업에 따라 다릅니다.
답변
시도해 볼 수 있습니다 pyfields
.
from pyfields import field
class Spam(object):
description = field(validators={"description can not be empty": lambda s: len(s) > 0})
value = field(validators={"value must be greater than zero": lambda x: x > 0})
s = Spam()
s.description = "hello"
s.description = "" # <-- raises error, see below
그것은 산출
ValidationError[ValueError]: Error validating [<...>.Spam.description=''].
InvalidValue: description can not be empty.
Function [<lambda>] returned [False] for value ''.
python 2 및 3.5 (와 반대 pydantic
) 와 호환되며 값이 변경 될 때마다 유효성 검사가 발생합니다 (이 아닌 첫 번째뿐만 아니라 attrs
). 생성자를 생성 할 수 있지만 기본적으로 위와 같이 생성하지는 않습니다.
mini-lambda
오류 메시지를 훨씬 더 간단하게 표시하려면 일반 이전 람다 함수 대신 선택적으로 사용할 수 있습니다 (실패한 표현식이 표시됨).
자세한 내용은 pyfields
문서 를 참조하십시오 (저는 저자입니다;))