Why is there no instancemethod builtin?

J

John Reese

Why hello there ha ha.

I have got in the habit of testing the types of variables with
isinstance and the builtin type names instead of using the types
module, as was the style back around Python 2.1. That is, rather than

if type(x) == types.ListType:

I now do:

if isinstance(x, list):

It is my understanding that this is what people do nowadays. One
problem I have, though, is that not all type names are available as
builtins. Just looking through the types declared in types, the
following are builtins:

bool, buffer, complex, dict, file, float, list, long,
object, slice, str, tuple, type, unicode, xrange, NoneType,
NotImplementedType

And the following are not:

dictproxy, ellipsis, frame, function, instancemethod, module,
traceback, instancemethod, NoneType, NotImplementedType

So for any in the latter batch, I have to import types after all. I
assume the dividing line is whether the name is useful as a
constructor. Still, I feel there's some inconsistencies in the
usefulness of the new type system. Why do str and unicode derive from
basestring, while list and tuple are independent types? list and
tuple support most of the same operations... it seems like either they
should also have an abstract base class or str and unicode shouldn't,
because duck typing doesn't really require that.

It also seems like types may be on the way out, because I don't see
constants for set or frozenset.

I'm not sure I have a question here, just pointing out what I see as
some flaws in the new type system.
 
J

John Machin

John said:
Why hello there ha ha.
[snip]
Just looking through the types declared in types, the
following are builtins:
[snip]
... NoneType,
NotImplementedType

And the following are not:
[snip]
... NoneType, NotImplementedType

So for any in the latter batch, I have to import types after all.

Plonk.
 
S

Steven Bethard

John said:
I now do:

if isinstance(x, list):

It is my understanding that this is what people do nowadays.

I wouldn't go that far. I don't have an isinstance check for lists
anywhere in my entire codebase. Why do you think you need to check to
see if something is of type list? Why don't you just use it as needed,
and find out, e.g.:

try:
itr = iter(x)
except TypeError:
# do whatever you need to do if it's not iterable
else:
# do whatever you need to do if it *is* iterable

STeVe
 
J

John Reese

I wouldn't go that far. I don't have an isinstance check for lists
anywhere in my entire codebase. Why do you think you need to check to
see if something is of type list? Why don't you just use it as needed,
and find out, e.g.:

try:
itr = iter(x)
except TypeError:
# do whatever you need to do if it's not iterable
else:
# do whatever you need to do if it *is* iterable

STeVe

I'm not saying I do it a lot, but sometimes it's useful to write
methods with interfaces like, well, isinstance's, whose second argument
can be a single type object or a sequence of class objects. The
standard conditional vs. exception tradeoffs exist here... if it's
likely that x isn't iterable, I shouldn't have to incur the time and
heap churn penalty of filling in sys.exc_info and its obsolete
cousins, potentially rolling back the stack, etc.
 
S

Steven Bethard

John said:
I now do:
if isinstance(x, list): [snip]

I'm not saying I do it a lot, but sometimes it's useful to write
methods with interfaces like, well, isinstance's, whose second argument
can be a single type object or a sequence of class objects.

Personally, I'd just write these functions with a *args instead, e.g.:

def my_isinstance(obj, *types):
return isinstance(obj, types)

Sure, it's not an option if you need multiple type lists, but how often
does that happen?

STeVe
 
J

John Roth

John Reese said:
Why hello there ha ha.

I have got in the habit of testing the types of variables with
isinstance and the builtin type names instead of using the types
module, as was the style back around Python 2.1. That is, rather than

if type(x) == types.ListType:

I now do:

if isinstance(x, list):

you need _both_ isinstance and the types module to do a correct
check for any string type: isinstance(fubar, types.StringTypes).
That's because both string and unicode are subtypes of one base.
It is my understanding that this is what people do nowadays. One
problem I have, though, is that not all type names are available as
builtins. Just looking through the types declared in types, the
following are builtins:

bool, buffer, complex, dict, file, float, list, long,
object, slice, str, tuple, type, unicode, xrange, NoneType,
NotImplementedType

And the following are not:

dictproxy, ellipsis, frame, function, instancemethod, module,
traceback, instancemethod, NoneType, NotImplementedType

You need to do your homework better. You have, for example
NoneType and NotImplementedType in both lists.

The basic answer to your question is that the types in builtins
are there because they have uses other than type checks.
Being useful for type checks is not the criterion for being in
builtins. That's the function of the types module.

The other point to be made here is that, in most cases,
type checks are a design smell. That's not always true, but
it's the first thing to check when you see one.

John Roth
 
G

George Sakkis

Steven Bethard said:
I wouldn't go that far. I don't have an isinstance check for lists
anywhere in my entire codebase. Why do you think you need to check to
see if something is of type list? Why don't you just use it as needed,
and find out, e.g.:

try:
itr = iter(x)
except TypeError:
# do whatever you need to do if it's not iterable
else:
# do whatever you need to do if it *is* iterable

A class of cases I've found necessary to do explicit type checking is
when a different action is taken for different types, but where duck
typing can't distinguish between them. For example, a function that
returns a string representation of an S-expression, represented as a
list of nested lists:

def s_expr(obj):
if isinstance(obj, list):
return "(%s)" % ' '.join(map(s_expr,obj))
else:
return str(obj)
s_expr([1, [2,3], [4,5], "foo"])
'(1 (2 3) (4 5) foo)'

Here duck typing doesn't help because the function should take the if
branch only for lists and not other iterables (e.g. strings).

George
 
M

Michael Hoffman

John said:
you need _both_ isinstance and the types module to do a correct
check for any string type: isinstance(fubar, types.StringTypes).
That's because both string and unicode are subtypes of one base.

But basestring, their base class is a built-in.
 
J

John Roth

Michael Hoffman said:
But basestring, their base class is a built-in.

True, but that wasn't the question.

However, to add a bit of sanity to this thread, please consider
that PEP 294 is still open - it survived the recent spate of PEP
rejections and closings. It suggests extending the types module
to include all types, including those in the new module (which the
OP seems to have overlooked,) and then depreciating the new
module.

John Roth

 
M

Michele Simionato

In this case I have used hasattr(obj, "__iter__") instead of
isinstance(obj, list)
(strings are iterable but do not have __iter__ method). I think hasattr
is much better
since it works for tuples and custom iterables too.

Michele Simionato
 
G

George Sakkis

Michele Simionato said:
In this case I have used hasattr(obj, "__iter__") instead of
isinstance(obj, list)
(strings are iterable but do not have __iter__ method). I think hasattr
is much better
since it works for tuples and custom iterables too.

The fact that strings don't have __iter__ is an implementation detail
(I can't think of any reason other than historic and perhaps backwards
compatibility for this; iterables should IMHO by definition be exactly
the objects with __iter__). Also, what if tuples and any other
iterables except for lists are considered atomic ? An implementation of
s-expressions would use a single data structure for the nesting (e.g. a
builtin list or a custom linked list) and objects of all other types
should be considered atoms, even if they happen to be iterable.

George
 
M

Michele Simionato

I think strings do not have __iter__ on purpose, exactly to distinguish
them
from other iterables, since sometimes it is nice to consider them
atomic,
but I am not sure of this. You should ask the developers. Anyway, the
right definition of iterable is (as I was told) "an object X such that
iter(X)
does not throw an exception". Objects following the __getitem__
protocol
- such as strings -are iterables even if they do not have an __iter__
method.


Michele Simionato
 
G

George Sakkis

Michele said:
I think strings do not have __iter__ on purpose, exactly to distinguish
them from other iterables, since sometimes it is nice to consider them
atomic, but I am not sure of this. You should ask the developers. Anyway, the
right definition of iterable is (as I was told) "an object X such that
iter(X) does not throw an exception".

Hmm.. not a very insightful definition unless someone knows the
implementation of iter().
Objects following the __getitem__ protocol - such as strings - are iterables
even if they do not have an __iter__ method.

It would be more uniform if the default 'type' metaclass added an
__iter__ method to classes that define __getitem__ but not __iter__
with something like:

from itertools import count

def __iter__(self):
for i in count():
try: yield self
except IndexError: raise StopIteration

George
 
J

John Roth

George Sakkis said:
Michele said:
I think strings do not have __iter__ on purpose, exactly to distinguish
them from other iterables, since sometimes it is nice to consider them
atomic, but I am not sure of this. You should ask the developers. Anyway,
the
right definition of iterable is (as I was told) "an object X such that
iter(X) does not throw an exception".

Hmm.. not a very insightful definition unless someone knows the
implementation of iter().
Objects following the __getitem__ protocol - such as strings - are
iterables
even if they do not have an __iter__ method.

It would be more uniform if the default 'type' metaclass added an
__iter__ method to classes that define __getitem__ but not __iter__
with something like:

from itertools import count

def __iter__(self):
for i in count():
try: yield self
except IndexError: raise StopIteration


Unfortunately it doesn't work: getitem can be defined for
a class that acts like a dictionary: that is, the items are
not integers, let alone integers that extend in a strict
sequence from 0.

John Roth
 
G

George Sakkis

John Roth said:
Unfortunately it doesn't work: getitem can be defined for
a class that acts like a dictionary: that is, the items are
not integers, let alone integers that extend in a strict
sequence from 0.

This is true, but that's the current behaviour of iterators for classes
that define __getitem__ without __iter__:

class AnIterable(object):
def __init__(self, it): self._it = it
def __getitem__(self,i): return self._it

KeyError: 0


George
 
R

Raymond Hettinger

[George Sakkis]
The fact that strings don't have __iter__ is an implementation
detail. I can't think of any reason other than historic and perhaps
backwards compatibility for this;
iterables should IMHO by definition be exactly
the objects with __iter__).

There would be no benefit other than satisfying that particular world
view. It is a feature that all sequences are automatically iterable
without having to write a custom __iter__ method. That makes life a
bit easier for writers of sequence-like classes.



[Michele Simionato]
I think strings do not have __iter__ on purpose, exactly to
distinguish them from other iterables, since sometimes it is nice
to consider them atomic, but I am not sure of this. You should
ask the developers.

That is not correct. Since any sequence is automatically iterable
(because of the presence of __getitem__), the inclusion of a separate
__iter__ method is purely optional. The option to include a custom
__iter__ method has been exercised only when it has offered some
performance benefit. IOW, the inclusion of __iter__ for a sequence is
an arbitrary implementation detail -- there is no other significance.

Anyway, the right definition of iterable is
(as I was told) "an object X such that iter(X) does not throw an
exception". Objects following the __getitem__ protocol
- such as strings -are iterables even if they do not have
an __iter__ method.

An object is iterable if and only if it provides either __iter__ or
__getitem__.



Raymond
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,767
Messages
2,569,570
Members
45,045
Latest member
DRCM

Latest Threads

Top