Are assert checks unpythonic? (was: Passing objects to a function)

Holger Türk htx1 at gmx.de
Tue May 11 09:03:35 EDT 2004


bruno modulix wrote:
> Holger Türk a écrit :
>> bruno modulix wrote:
>>> Holger Türk a écrit :
>>>> This could be supported by an assert statement:
>>>>
>>>> def BlackJackValue(hand):
>>>>     """BlackJackValue(Hand_instance) -> int\n\nReturn an integer, which
>>>> is the value of the Hand according to the rules of BlackJack."""
>>>>     assert isinstance (hand, Hand)
>>>>     [...]
>>>> The method will throw an AssertionError if hand is not
>>>> an instance of Hand. Assertions can be turned off later for
>>>> performance reasons; with -O I guess.
>>>
>>> Note that this is also highly unpythonic since you won't be able to pass 
>>
>> Unpythonic? That's a matter of taste. So: maybe.
> 
> We can discuss the point... But to summarize what I have in mind : 
> testing for a *specific* type is fighting against the very dynamic 
> nature of Python instead of taking advantage of it (IMHO etc...)

The dynamic nature is a very powerful tool and it may be applied
when needed. But one does not have to.

>>> objects that implements the Hand interface but do not subclass Hand. 
>>> This may not be a problem in this specific case, but this probably 
>>> should not be recommanded in the general case unless there is a very 
>>> compelling reason to do so...
>>
>> The assert statement, as it is used in the example above, describes
>> the assumptions the programmer had in mind when writing the function
>> it is written in. 
> 
> Not being psychic, I can't say what the programmer actually had in mind 
> when writing this specific piece of code !-) But whatever, the fact is 
> that the code needs a 'Hand-like' object, not a 'Hand' object. The 
> important word here is 'like'.

I understand. It's like the omnipresent file-like object, an object
which is not actually a file, but an object which supports read() and
write(). Or was it write() and close()? Or even read(), tell() and
seek ()? Seems like it depends on the client, even within the
standard library.
   But instead of an AssertionError, telling me that my file-like
object isn't file-like enough, an AttributeError is raised deep inside
the module. In some cases, even TypeErrors may occur.
   That's my problem with foo-like objects implementing varying
interfaces that aren't checked. Why isn't there a superclass "FileLike"
with the standard methods that all raise NotImplementedError, "method
bar"? So when NotImplementedError is raised, it's absolutely clear
what's missing. I can't tell that from an AttributeError without
deeper investigation.
   In this case: Hand could specialize HandLike and the assert could
be "assert isinstance (hand, HandLike)". But that looks like a lot of
useless work.

>> Programs are not written into stone. When the need arises, the assert
>> statement can be modified, e.g. to check an interface
>> (hasattr (hand, containsCard), or something else), or even removed.
> 
> But this require source-code modification.

What's wrong with it? A function doesn't become poisonous once it's
finished.
   A second person using the code may be unwilling or unable to change it.
OK. But when you offer your code to someone else, you should be especially
careful with the effects of your program/library.

>> Because documentation regularly turns out to be neglected and assert
>> statements are easily written,
>> it's recommended to use them unless
>> there is a good reason to drop them.
> 
> 
> it's as easy to write :
> def BlackJackValue(hand):
>    """expects a Hand-like object"""
>   [...]
> 
> Because assert (or any other type-specific check) prevents from using 
> the same code with object of other types supporting the same protocol, 
> it's recommended to *not* use'em that way unless there is a *compelling* 
> reason to do so.

Most of this discussion is based on the difference of our perspectives
of what objects are: I focus on what something *is*, and you focus on
what something *does* and how its "doing" is accessed. So I clearly
understand your point of view. Still I do not understand why there
should not be any explicit check of this interface, or why this interface
should not be made explicit by extracting it into a class?
   Of course this doesn't make much sense when one wants to write a
function like str(), which works on a large variety of objects. But for
a specific problem domain, any means of making the program more explicit
should be welcome.
   Any implicit interface, like the one you advocate, has to be properly
documented, or else it will be forgotten, even by the author. And
documentation, as I pointed out, regularly tends to be neglected.

>> In this special case, Thomas clearly wanted to express that the
>> parameter hand should be an instance of Hand.
> 
> Whatever he 'clearly' wanted to express, what he *needs* to express is 
> that the parameter 'hand' should be an instance of any object responding 

First of all: He has to decide on his own what he wants and needs.
He wrote: "I'd like to write the parameter list of a method or
function in a way that makes the class of each parameter crystal
clear, [...]" This undoubtedly is a demand for a strong
restriction of the parameter type, i.e. he wanted to be sure that
hand is a Hand, as opposed to hand being handlike. This can be
enforced with assert, though it can't be documented with it.
   I don't see any point in saying: You can do it with foo, but don't
do it with foo, because it does not match the spirit of the language
(of which foo is a part), and because of that, you don't want and
need it anyway.
   Maybe he likes assert and it makes him confident when he reasons
out his program. Maybe it doesn't. At least it works for me.

Greetings,

Holger

> to the messages that will be send to it. And the good news is that there 
> is no need to explicitely check for this, as if the object fails to do 
> so, an AttributeError will be raised.
> 
>> So why shouldn't it
>> be documented and checked this way?




More information about the Python-list mailing list