PEP new assert idiom
Fábio Mendes
niels_bohr at uol.com.br
Sun Nov 7 03:22:33 EST 2004
Thanks for clarification :-)
Fabio
Em Dom, 2004-11-07 às 07:43 +0000, Bengt Richter escreveu:
> On Sun, 07 Nov 2004 03:50:43 GMT, "Raymond Hettinger" <vze4rx4y at verizon.net> wrote:
>
> >[Fbio Mendes]
> >> This is very similar to what I'm proposing, with the only inconvinience
> >> that is uglier to type:
> >>
> >> assert \
> >> exp1 and \
> >> exp2 and \
> >> ...
> >> expn,
> >> 'ErrorMsg'
> >>
> >> Instead of:
> >>
> >> assert \
> >> exp1,
> >> exp2,
> >> ...
> >> expn,
> >> 'Errormsg'
> >>
> >> Well, I realize I didn't expressed my thoughts very clearly and that
> >> it's indeed a very minor change in python's syntax. It won't let anyone
> >> does anything new, IFAIK, but it makes a common pattern of code a little
> >> more beautiful, (and why not? more expressive). If one consider the fact
> >> that it won't break old code (only in one very unlikely case) I don't
> >> see it as a completely unreasonable suggestion. Other people may think
> >> differently though.
> >
> >The odds of Guido accepting this proposal are about zero. As you say, it
> >doesn't do anything new, but it does require altering the grammar. Besides,
> >TOOWTDI.
> >
> >Also, Guido tends to not be persuaded by arguments about "too much typing".
> >This is doubly true is you're talking about replacing an "and" with a comma (big
> >whoop).
> >
> >Also, one of the existing alternatives is quite readable:
> >
> >err = 'Errormsg'
> >assert exp1, err
> >assert exp2, err
> >assert exp3, err
> >
> >The alternative has the advantage that a failure will point to the exact
> >expression that failed. The disadvantage is the repetition of the error
> >message; however, I disagree that your use case is common. Instead, it is
> >likely more advantageous to have different error messages for each expression.
> >For example, the following comes from the post condition checks in QR matrix
> >decomposition:
> >
> >assert Q.tr().mmul(Q)==eye(min(m,n)), "Q is not orthonormal"
> >assert isinstance(R,UpperTri), "R is not upper triangular"
> >assert R.size==(m,n), "R is does not match the original dimensions"
> >assert Q.mmul(R)==self, "Q*R does not reproduce the original matrix"
> >
> >One could link all of these by an "and" or the proposed comma, but then you
> >end-up with a single, less informative error message, "The QR decomposition
> >bombed, but I won't tell you why ;-) ".
> >
> Besides, if you want the single message with comma-delimited expressions,
> you can already write:
>
> >>> assert False not in map(bool, (
> ... True,
> ... 1,
> ... 2==2
> ... )), 'Error message'
>
> Or to show what happens
> >>> assert False not in map(bool, (
> ... True,
> ... 1,
> ... 2==3
> ... )), 'Error message'
> Traceback (most recent call last):
> File "<stdin>", line 1, in ?
> AssertionError: Error message
>
> Or with a little helper, and then two boilerplate lines for the assert,
> you can have individual messages:
>
> >>> def fff(*xm):
> ... """find first false xpr in seq xpr ,msg, xpr, msg and yield pair"""
> ... it = iter(xm)
> ... for x in it:
> ... m = it.next()
> ... if not x: yield x, m; break
> ...
> >>> def test(x):
> ... assert not [t for t in fff(
> ...
> ... True, 'true msg',
> ... 1, 'one msg',
> ... 2==x, '2 eq x msg',
> ... 'xx', 'xx msg'
> ...
> ... )], 'Error: expr == %r, msg = %r'%t
> ...
> >>> test(2)
> >>> test(3)
> Traceback (most recent call last):
> File "<stdin>", line 1, in ?
> File "<stdin>", line 7, in test
> AssertionError: Error: expr == False, msg = '2 eq x msg'
>
> Actually, why not just make a utility function "asserts" to do it:
>
> >>> def asserts(*expr_msg_seq):
> ... """find first false expression value and assert it with paired message"""
> ... it = iter(expr_msg_seq)
> ... for x in it:
> ... m = it.next()
> ... if not x:
> ... assert x, '%r -> %r'%(x, m)
> ...
> >>> def test(x):
> ... asserts(
> ... True, 'true msg',
> ... 1, 'one msg',
> ... x, 'bool(x) is not True',
> ... x==2, '2 eq x msg',
> ... 'xx', 'xx msg'
> ... )
> ...
> >>> test(2)
> >>> test(3)
> Traceback (most recent call last):
> File "<stdin>", line 1, in ?
> File "<stdin>", line 7, in test
> File "<stdin>", line 7, in asserts
> AssertionError: False -> '2 eq x msg'
> >>> test(0)
> Traceback (most recent call last):
> File "<stdin>", line 1, in ?
> File "<stdin>", line 7, in test
> File "<stdin>", line 7, in asserts
> AssertionError: 0 -> 'bool(x) is not True'
> >>> test(())
> Traceback (most recent call last):
> File "<stdin>", line 1, in ?
> File "<stdin>", line 7, in test
> File "<stdin>", line 7, in asserts
> AssertionError: () -> 'bool(x) is not True'
> >>> test(type('foo',(),{'__nonzero__':lambda self:0})())
> Traceback (most recent call last):
> File "<stdin>", line 1, in ?
> File "<stdin>", line 7, in test
> File "<stdin>", line 7, in asserts
> AssertionError: <__main__.foo object at 0x02EF17AC> -> 'bool(x) is not True'
> >>> test(type('foo',(),{'__nonzero__':lambda self:1})())
> Traceback (most recent call last):
> File "<stdin>", line 1, in ?
> File "<stdin>", line 7, in test
> File "<stdin>", line 7, in asserts
> AssertionError: False -> '2 eq x msg'
> >>> test(type('foo',(),{'__nonzero__':lambda self:1, '__cmp__':lambda s,o:0})())
>
> I doesn't shortcut, so you could get an exception in preparing the arg list for
> a sequence like
> asserts(
> den !=0, 'denom must be zero',
> num/den>5, 'better write it as assert num>5*den'
> )
> which would be safer as
> assert den !=0, 'denom must be zero'
> assert num/den>5 'better write it as assert num>5*den'
>
> Not to mention side effects, but you shouldn't have those in asserts anyway.
>
> Silliness ;-)
>
> Regards,
> Bengt Richter
More information about the Python-list
mailing list