[Tutor] question re type()

Kent Johnson kent37 at tds.net
Wed Oct 31 12:48:25 CET 2007


Aditya Lal wrote:
> On 10/29/07, *Kent Johnson* <kent37 at tds.net <mailto:kent37 at tds.net>> wrote:

>     - Common Python practice is to prefer the least restrictive type check
>     possible.

> I completely agree that the check " type(n) == int " is very intuitive 
> and simple. Its just that there are many more types that the basic ones 
> and 'types' module provide a "consistent" way for checking for types.

Yes, for some types it is easiest to import types and use its 
definitions. Whether to use types.IntegerType or int is a matter of 
preference, I suppose.
> As 
> an example - consider a function :
> 
> def execMyFun( f ) :
>    if type(f) == types.GeneratorType :
>       return f.next()
>    elif type(f) == types.FunctionType :
>       return f()
>    else :
>       raise Exception("Invalid type for f : " + type(f) )
> 
> Here types module came to the rescue which otherwise I would have 
> written using exception handling. /Well ! if you have a cleaner solution 
> do let me know./

You probably should write this using exception handling. The least 
restrictive type check is to check for the specific operations you need, 
rather that checking for a type that supports the operation. This can be 
done with preflight checks - known as Look Before You Leap - or by 
trying an operation and catching exceptions in case of failure, known as 
Easier to Ask Forgiveness than Permission.

In this case, it seems that if f implements the iterator protocol then 
you want the next item and if f is callable then you want to just call 
it. In both cases checking for specific operations or trying the 
operation and catching any exception will allow a wider range of 
parameters. For example, next() is meaningful for generators, iterators 
on built-in types, user-defined iterators (defined with classes).
In [129]: import types
In [130]: i=iter([1])
In [131]: type(i)
Out[131]: <type 'listiterator'>
In [132]: type(i)==types.GeneratorType
Out[132]: False
In [133]: isinstance(i, types.GeneratorType)
Out[133]: False
In [134]: i.next
Out[134]: <method-wrapper 'next' of listiterator object at 0x20d0050>

Function call is valid for functions, bound methods, and instances of 
any class that defines a __call__() method.

Here is a less restrictive LBYL implementation of your function:
def execMyFun( f ) :
    if hasattr(f, 'next') and callable(f.next):
       return f.next()
    elif callable(f):
       return f()
    else :
       raise Exception("Invalid type for f : " + type(f) )

Here in an EAFP implementation:
def execMyFun( f ) :
    try:
       return f.next()
    except AttributeError:
       pass

    try:
       return f()
    except TypeError:
       pass
    raise Exception("Invalid type for f : " + type(f) )

I think I prefer the LBYL version here, it allows the same values for f 
and it won't hide AttributeErrors and TypeErrors raised by calling 
f.next() or f().

Finally you should probably raise TypeError which is "raised when an 
operation or function is applied to an object of inappropriate type."

Kent



More information about the Tutor mailing list