classification
Title: No way to find out if an object is an instance of a namedtuple
Type: enhancement Stage: needs patch
Components: Library (Lib) Versions: Python 3.3
process
Status: closed Resolution: rejected
Dependencies: Superseder:
Assigned To: rhettinger Nosy List: abarnert, amaury.forgeotdarc, daniel.urban, eric.araujo, eric.snow, pitrou, pwaller, python-dev, rhettinger, santoso.wijaya, thead, zuo
Priority: low Keywords:

Created on 2010-01-27 16:11 by pwaller, last changed 2014-01-13 01:15 by abarnert. This issue is now closed.

Messages (22)
msg98437 - (view) Author: Peter Waller (pwaller) Date: 2010-01-27 16:11
Apologies if there is a way of doing this, but I haven't been able to find anything.

I need to be able to do the following:

my_tuple = namedtuple("my_tuple", "a b c")
obj = my_tuple(1,2,3)

if isinstance(obj, namedtuple):
    .. do stuff ..

The best I could come up with for the moment is:

if isinstance(obj, tuple) and type(obj) is not tuple:
    .. do stuff ..
msg98439 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2010-01-27 16:14
In this case, what is wrong with:

if isinstance(obj, my_tuple): ...

or do you want to catch all namedtuples? And if so, why?

(I suppose it would be easy to make all namedtuples inherit from a common base class, though)
msg98440 - (view) Author: Éric Araujo (eric.araujo) * (Python committer) Date: 2010-01-27 16:16
Hello

namedtuple is a factory function, not a class (as hinted by the naming in lower case, see PEP 8), so there are no instances of namedtuple.

You can workaround that with issubclass, or checking for special namedtuples attributes (_asdict, _replace, but this is fragile), or just not use class cheking, often unneeded in Python code.

Kind regards
msg98441 - (view) Author: Peter Waller (pwaller) Date: 2010-01-27 16:18
Hi Antoine and Eric, 

Thanks for your responses.

I need to be able to catch all named tuples, I don't know in advance what instance of a namedtuple it might be, but I want my function to only accept named tuples. Is this unreasonable?

(I am actually writing a C interface which does conversion from namedtuples to a form of "RecordSpecification" to do with databases. The namedtuple is arbitrarily created by the user.)
msg98442 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2010-01-27 16:21
> I need to be able to catch all named tuples, I don't know in advance
> what instance of a namedtuple it might be, but I want my function to
> only accept named tuples. Is this unreasonable?

I don't know. Why exactly don't you want to accept something else?
Perhaps the user has their own namedtuple-like class. Or perhaps they're
fine with plain tuples.
msg98443 - (view) Author: Peter Waller (pwaller) Date: 2010-01-27 16:23
In this case, I need to have names for each object. It is also desirable to use the namedtuple because of their lightweight nature.

If they were to subclass the namedtuple I would be happy to accept that, but I really do want them to pass some form of namedtuple to me, to have some sort of consistency. The named tuple gives an obvious simple interface for determining which fields are available and the order they are in.
msg98444 - (view) Author: Amaury Forgeot d'Arc (amaury.forgeotdarc) * (Python committer) Date: 2010-01-27 16:36
It seems a perfect case for "duck typing" style of programming:
All namedtuple classes:
- inherit from tuple
- have a "_fields" class attribute
These two properties could be the "duck test" for namedtuples, regardless of the actual implementation.
msg98453 - (view) Author: Peter Waller (pwaller) Date: 2010-01-28 00:01
Hi Amaury,

Thanks. I had heard of but never bothered to read about duck-typing before now; though I have used it passively before. I think it does make sense in this case. I can't imagine any case where checking for the _fields attribute would fail and isinstance(x, namedtuple) would not.

Besides which, for my current project I am forced to implement such a "workaround" anyway, so it doesn't affect me as such.

The only reason that remains why I would want it is that I often use isinstance(x, Y) to deal with different Ys, and that was the thing I intuitively wanted to use in this case as a python programmer for quite a few years now. This is probably a pretty weak reason, so I am happy to close this issue if the consensus points to duck typing.
msg98555 - (view) Author: Sridhar Ratnakumar (srid) Date: 2010-01-30 07:37
For more discussion on this, see http://stackoverflow.com/questions/2166818
msg99869 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2010-02-22 23:23
Discussed this with GvR.
Here's a recap:

For 2.6 and 3.1 which are already released, check for the _fields attribute.  This is a guaranteed part of the API is not fragile.  For the C structures, check for the n_fields attribute.

In the future, the C API needs to grow to match the namedtuple() API and we should add an abstract base class describing the common API.  Then you'll be able to write: 
    if isinstance(obj, abc_namedtuple).
msg115347 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2010-09-02 00:30
Am deferring this to Py3.3.  There is a workaround available just using duck-typing and I would like to wait until more more has been done on StructSeq before setting committing to an new namedtuple Abstract Base Class (one released, it would be very hard to change).  Also, I would like to see how pprint() evolves (because it may also want to have special handling for named tuples).
msg115371 - (view) Author: Éric Araujo (eric.araujo) * (Python committer) Date: 2010-09-02 13:37
To make sure I understand: StructSeq is the C-pendent of named tuples, and a NamedTuple ABC would have to work with StructSeqs too?
msg132363 - (view) Author: Jan Kaliszewski (zuo) Date: 2011-03-27 20:40
On python-ideas I have proposed an ABC being also a kind of a mix-in, potentially making namedtuple subclassing (with custom methods etc.) more convenient, e.g.:

    class MyRecord(namedtuple.abc):
        _fields = 'x y z'
        def _my_custom_method(self):
            return list(self._asdict().items())

or

    class MyAbstractRecord(namedtuple.abc):
        def _my_custom_method(self):
            return list(self._asdict().items())

    class AnotherAbstractRecord(MyAbstractRecord):
        def __str__(self):
            return '<<<{}>>>'.format(super().__str__())
 
    class MyRecord2(MyAbstractRecord):
        _fields = 'a, b'
 
    class MyRecord3(AnotherAbstractRecord):
        _fields = 'p', 'q', 'r'

Here is an experimental monkey-patcher adding the 'abc' attribute to namedtuple:

http://dpaste.org/T9w6/

I am not sure if it is worth preparing an actual patch based on it. If you think it is I could prepare one.
msg132365 - (view) Author: Jan Kaliszewski (zuo) Date: 2011-03-27 21:34
PS. Newer, shorter version: http://dpaste.org/2aiQ/
msg132377 - (view) Author: Éric Araujo (eric.araujo) * (Python committer) Date: 2011-03-27 22:34
Thanks for working on this.  I have some remarks:

1) Please post diff files here instead of using external sites.  See http://docs.python.org/devguide/patch#preparation

2) The license you chose doesn’t allow the PSF to include it into Python, see http://www.python.org/psf/contrib/contrib-form/

3) abc looks like a module name, here something like NamedTupleABC or simply NamedTuple would be better.  Note that this bug is only about type checking, the alternate form of defining named tuples thanks to this ABC is an additional feature; Raymond may reject it.
msg132384 - (view) Author: Jan Kaliszewski (zuo) Date: 2011-03-28 07:19
Thank you. Raymond is against the idea so I don't know if it makes sense to create the real patch for now (it would patch the collections module and, I suppose, addming tests, docs etc.). Anyway, if somebody would be interested in the idea, the newest version of the draft is here: http://dpaste.org/qyKv/.
msg132713 - (view) Author: Jan Kaliszewski (zuo) Date: 2011-03-31 22:07
PS. For the record: the final recipe is here: http://code.activestate.com/recipes/577629-namedtupleabc-abstract-base-class-mix-in-for-named/
msg132716 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2011-03-31 22:46
New changeset 7aa3f1f7ac94 by Raymond Hettinger in branch '3.2':
Issue #7796: Add link to Jan Kaliszewski's alternate constructor and ABC for named tuples.
http://hg.python.org/cpython/rev/7aa3f1f7ac94

New changeset 330d3482cad8 by Raymond Hettinger in branch 'default':
Issue #7796: Add link to Jan Kaliszewski's alternate constructor and ABC for named tuples.
http://hg.python.org/cpython/rev/330d3482cad8
msg134186 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2011-04-20 22:17
Detecting _fields is the simplest thing we can do right now.  There are too many structseq API differences to warrant bringing them under a single ABC umbrella.
msg190816 - (view) Author: Eric Snow (eric.snow) * (Python committer) Date: 2013-06-08 18:29
It may not enough, but the use of namedtuples (vs. plain tuples) with functools.singledispatch() would be messier without a NamedTuple ABC (or other base type).  Of course, when would you want to dispatch specifically on namedtuple?  I can think of a few relatively weak uses, but not enough to justify the cost of establishing a common base class for namedtuple.
msg191870 - (view) Author: Eric Snow (eric.snow) * (Python committer) Date: 2013-06-25 17:31
A NamedTuple ABC doesn't have to define any API (so that part could wait?)[1].  I see it as most useful for isinstance checks.  Here's a solution along those lines:


class NamedTuple(Sequence):
    @classmethod
    def __subclasshook__(cls, C):
        if cls is NamedTuple:
            if any("_fields" in B.__dict__ for B in C.__mro__):
                return True
        return NotImplemented

def namedtuple(...):
    ...
    NamedTuple.register(result)
    return result

(or include NamedTuple in the bases in the template)

For structseq support:

class NamedTuple(Sequence):
    @classmethod
    def __subclasshook__(cls, C):
        if cls is NamedTuple:
            if any("_fields" in B.__dict__ or  # for namedtuple
                   "n_fields" in B.__dict__  # for structseq
                   for B in C.__mro__):
                return True
        return NotImplemented


[1] I agree there is still a problem if we define an ABC here with no API and then add some later.  Then classes that inherit from NamedTuple would break later when we add an API (just `_fields`?).  Not sure how much that is a concern though.  Then again we could just close out issue1820 and actually establish the API.
msg207992 - (view) Author: Andrew Barnert (abarnert) * Date: 2014-01-13 01:15
I believe the structseq issues are a lot easier to solve than the appear. See #20230, which adds a _fields member to every structseq type. Having done that, a NamedTuple ABC works on them just fine. And of course duck typing them without an ABC does too.
History
Date User Action Args
2014-01-13 01:15:13abarnertsetnosy: + abarnert
messages: + msg207992
2013-06-25 17:31:10eric.snowsetmessages: + msg191870
2013-06-08 18:29:13eric.snowsetmessages: + msg190816
2013-06-08 14:04:34eric.snowsetnosy: + eric.snow
2011-04-20 22:17:48rhettingersetstatus: open -> closed
resolution: later -> rejected
dependencies: - Enhance Object/structseq.c to match namedtuple and tuple api
messages: + msg134186
2011-03-31 22:46:55python-devsetnosy: + python-dev
messages: + msg132716
2011-03-31 22:07:16zuosetmessages: + msg132713
2011-03-28 20:13:35daniel.urbansetnosy: + daniel.urban
2011-03-28 18:08:19sridsetnosy: - srid
2011-03-28 18:05:06santoso.wijayasetnosy: + santoso.wijaya
2011-03-28 07:19:26zuosetmessages: + msg132384
2011-03-27 22:34:38eric.araujosetmessages: + msg132377
2011-03-27 21:34:42zuosetmessages: + msg132365
2011-03-27 20:40:00zuosetnosy: + zuo
messages: + msg132363
2010-11-28 05:11:16eric.araujosetdependencies: + Enhance Object/structseq.c to match namedtuple and tuple api
2010-09-02 13:37:36eric.araujosetmessages: + msg115371
2010-09-02 00:30:33rhettingersetpriority: high -> low
resolution: later
messages: + msg115347

versions: + Python 3.3, - Python 2.7, Python 3.2
2010-08-08 01:38:49rhettingersetpriority: normal -> high
2010-02-22 23:23:01rhettingersetmessages: + msg99869
2010-01-30 07:37:04sridsetnosy: + srid
messages: + msg98555
components: + Library (Lib)
2010-01-28 00:01:53pwallersetmessages: + msg98453
2010-01-27 16:36:48amaury.forgeotdarcsetnosy: + amaury.forgeotdarc
messages: + msg98444
2010-01-27 16:24:45theadsetnosy: + thead
2010-01-27 16:23:49pwallersetmessages: + msg98443
2010-01-27 16:21:42pitrousetmessages: + msg98442
2010-01-27 16:18:41pwallersetmessages: + msg98441
2010-01-27 16:16:15eric.araujosetnosy: + eric.araujo
messages: + msg98440
2010-01-27 16:14:10pitrousetpriority: normal

type: enhancement
assignee: rhettinger
versions: + Python 2.7, Python 3.2, - Python 2.6
nosy: + rhettinger, pitrou

messages: + msg98439
stage: needs patch
2010-01-27 16:11:08pwallercreate