Posiadanie prostej klasy Pythona, takiej jak ta:
class Spam(object):
__init__(self, description, value):
self.description = description
self.value = value
Chciałbym sprawdzić następujące ograniczenia:
- „opis nie może być pusty”
- „wartość musi być większa od zera”
Czy powinienem:
1. zweryfikować dane przed utworzeniem obiektu spamu?
2. sprawdzić dane dotyczące __init__
metody?
3. utwórz is_valid
metodę w klasie Spam i wywołaj ją za pomocą spam.isValid ()?
4. utworzyć is_valid
statyczną metodę na klasie Spam i wywołać ją za pomocą Spam.isValid (opis, wartość)?
5. sprawdzić dane na deklaracji ustawiających?
6. itp.
Czy mógłbyś polecić dobrze zaprojektowane / Pythonic / nierozpoznane (w klasie z wieloma atrybutami) / eleganckie podejście?
self.description = description
użyć podkreślenia, czyliself._description = description
czy to nie ma znaczenia? Czy jest to konieczne, czy po prostu coś podobnego do wersji „prywatnych” zmiennych w Pythonie?self.description = …
przypisuje przez właściwość, podczas gdyself._description = …
przypisuje bezpośrednio do pola bazowego. Który z nich należy użyć podczas budowy, jest wyborem projektu, ale zwykle bezpieczniej jest zawsze przypisywać go za pośrednictwem nieruchomości. Na przykład powyższy kod zgłosi wyjątek, jeśli zadzwoniszSpam('', 1)
, tak jak powinien.Jeśli chcesz zweryfikować wartości tylko wtedy, gdy obiekt jest tworzony ORAZ przekazywanie nieprawidłowych wartości jest uważane za błąd programowania, użyłbym asercji:
class Spam(object): def __init__(self, description, value): assert description != "" assert value > 0 self.description = description self.value = value
Jest to mniej więcej tak zwięzłe, jak to tylko możliwe, i jasno dokumentuje, że są to warunki wstępne do utworzenia obiektu.
źródło
assert value > 0, "value attribute to Spam must be greater than zero"
. Asercje są tak naprawdę komunikatami dla programisty i nie powinny być przechwytywane przez kod klienta, ponieważ wskazują na błąd programowania. Jeśli chcesz, aby klient wychwycił i obsłużył błąd, jawnie zgłoś wyjątek, taki jak ValueError, jak pokazano w innych odpowiedziach.def
należy wstawić przed__init__
Jeśli nie masz ochoty na własne, możesz po prostu użyć formencode . Naprawdę wyróżnia się wieloma atrybutami i schematami (tylko schematami podklas) i ma wiele wbudowanych przydatnych walidatorów. Jak widać, jest to podejście polegające na „sprawdzaniu danych przed utworzeniem obiektu spamu”.
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)
Możesz też sprawdzić w trakcie
__init__
(i uczynić je całkowicie przezroczystymi za pomocą deskryptorów | dekoratorów | metaklasy), ale nie jestem tego wielkim fanem. Podoba mi się czysta bariera między danymi wprowadzanymi przez użytkownika a obiektami wewnętrznymi.źródło
jeśli chcesz zweryfikować tylko wartości przekazane do konstruktora, możesz zrobić:
class Spam(object): def __init__(self, description, value): if not description or value <=0: raise ValueError self.description = description self.value = value
To oczywiście nie powstrzyma nikogo przed zrobieniem czegoś takiego:
>>> s = Spam('s', 5) >>> s.value = 0 >>> s.value 0
Zatem prawidłowe podejście zależy od tego, co próbujesz osiągnąć.
źródło
Możesz spróbować
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
Poddaje się
ValidationError[ValueError]: Error validating [<...>.Spam.description='']. InvalidValue: description can not be empty. Function [<lambda>] returned [False] for value ''.
Jest zgodny z Pythonem 2 i 3.5 (w przeciwieństwie do
pydantic
), a walidacja ma miejsce za każdym razem, gdy wartość jest zmieniana (nie tylko za pierwszym razem, a nie zaattrs
). Może utworzyć konstruktora dla Ciebie, ale nie robi tego domyślnie, jak pokazano powyżej.Zauważ, że możesz opcjonalnie użyć
mini-lambda
zamiast zwykłych starych funkcji lambda, jeśli chcesz, aby komunikaty o błędach były jeszcze prostsze (wyświetlą one nieprawidłowe wyrażenie).Zobacz
pyfields
dokumentację po szczegóły (swoją drogą jestem autorem;))źródło