4 ms·
Ummm, maybe? There seem to be libraries for everything, but I do it manually. The constructors become "non-trivial", but not overly-complicated, and it pays o
by dbcurtis 9y ago
Ummm, maybe? There seem to be libraries for everything, but I do it manually. The constructors become "non-trivial", but not overly-complicated, and it pays off in simpler, more robust code elsewhere.
So my code would be something like:
class Foo:
def __init__(self, x, y=None):
if y is None:
x_ = x.x
y = x.y
self.x = int(x_)
if self.x < 0 or self.x > 99:
raise ValueError('x must be in range 0..99')
if len(y) < 2:
raise ValueError('y must be iterable, length > 2')
self.y = y
Soo...
You can say:
f = Foo(1,['a','b'])
f = Foo(1,('a','b', 3))
f = Foo("42", "bar")
f2 = Foo(f)
Calling int(x_) will take anything that implements __int__(), or raise ValueError. len() will take anything that implements the iterator protocol and has length >= 2, or raise. Calling Foo() on an instance of Foo will cause some excess copies, but it does have the effect of making sure you passed in something Foo-ish. Where "Foo-ish" means "has an x attribute that can be made into an integer between 0 and 99, and a y attribute that is an iterable of length >= 2". The above code lets some exceptions bubble up, but they could be wrapped in try..except to turn them into ValueError with specific messages. Note that this code accepts anything Foo-ish without calling isinstance(). Abstract base cases with isinstance() are a more suitable choice for complex cases of checking protocol conformance.
So whether or not the performance impact and extra code are worth it probably varies from project to project.