[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