# Namedtuple instance check

**URL:** <https://discuss.python.org/t/namedtuple-instance-check/103651>\
**Category:** Ideas\
**Created:** [September 9, 2025, 2:34pm UTC](https://discuss.python.org/t/namedtuple-instance-check/103651 "2025-09-09T14:34:20Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![kddnewton](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/kddnewton/32/30757_2.png) [@kddnewton](https://discuss.python.org/u/kddnewton)\
**Post date:** [September 9, 2025, 2:34pm UTC](https://discuss.python.org/t/namedtuple-instance-check/103651/1 "2025-09-09T14:34:20Z")

</div>

I would like a way to be able to check if an object is an instance of a type that was created by the namedtuple function. Usually I would think this kind of introspection is not necessary, but in the case of serializing/deserializing and preserving the type, it can end up being helpful. Currently in the Python ecosystem, this is implemented with myriad error-prone heuristics:

- pytorch/pytorc (pytorch/pytorch/blob/4dd73e659a8fd4872e5f49cfd72e420fa7c4e6c9/torch/utils/\_pytree.py#L675-L684): checks for `_fields` and that `_make` and `_asdict` are callable
- metaopt/optree (metaopt/optree/blob/e0e772ea3bb4ab551cb6f102b8074022bb8542ec/include/optree/pytypes.h#L217-L254): same as pytorch, but implemented in C++ with a custom weakref LRU cache in front
- jax-ml/jax (jax-ml/jax/blob/792577ff9ee8a55968baa02719e3c84e518cdf35/jaxlib/pytree.cc#L216-L219): checks only for the presence of `_fields`
- pytorch/tensordict (pytorch/tensordict/blob/785349d82c6e17928cfb4581e73dab16156c8a6c/tensordict/utils.py#L2378-L2381): checks for `_fields`, `_replace`, and `_asdict`
- numba/numba (numba/numba/blob/5e0ceb077fb219a21ac8a50e5234ca643aaf96f9/numba/core/ir\_utils.py#L1962-L1977): checks that `tuple` is the only superclass, as well as `_make` and `_fields`

and many, many more: [Code search results · GitHub](https://github.com/search?q=is_namedtuple_class&type=code). These kinds of heuristics are still propagating out today, as seen from this 15-year old [stackoverflow post](https://stackoverflow.com/questions/2166818/how-to-check-if-an-object-is-an-instance-of-a-namedtuple).

In terms of implementation, I would think it would either involve adding a marker type into the hierarchy for named tuples so that they do not directly inherit from `tuple`, or adding a querying function to the collections module that would reliably be able to check this.

P.S., apologies for the formatting. This is my first post, and it will not let me post more than 2 links. All of the code examples are links from GitHub.

---

<div class="post-metadata">

**Author:** ![NeilGirdhar](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/neilgirdhar/32/5776_2.png) [@NeilGirdhar](https://discuss.python.org/u/NeilGirdhar)\
**Post date:** [September 9, 2025, 3:43pm UTC](https://discuss.python.org/t/namedtuple-instance-check/103651/2 "2025-09-09T15:43:46Z")

</div>

> [@kddnewton](#):
>
> I would think it would either involve adding a marker type into the hierarchy

Yes, this is the most obvious solution. I think some people just hate tiny costs more than they hate creating issues like the ones you’ve linked.

---

<div class="post-metadata">

**Author:** ![blhsing](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/blhsing/32/25812_2.png) [@blhsing](https://discuss.python.org/u/blhsing)\
**Post date:** [September 10, 2025, 4:20am UTC](https://discuss.python.org/t/namedtuple-instance-check/103651/3 "2025-09-10T04:20:23Z")

</div>

One [officially documented](https://docs.python.org/3/library/types.html#types.get_original_bases) workaround is to test if the factory function `typing.NamedTuple` is the class’ original base:

```python
from typing import NamedTuple
from types import get_original_bases

class Point(NamedTuple):
    x: float
    y: float

assert get_original_bases(Point) == (NamedTuple,) # passes

```

or to account for `typing.Generic` as a possible original base:

```python
assert NamedTuple in get_original_base(Point)

```

I suggest that this workaround be included in `NamedTuple`’s documentation.

Furthermore, I think it makes sense to make `issubclass(A, B)` perform an additional search for `B` in `A. __orig_bases__ `, so the more readable and idiomatic approach of `issubclass(Point, NamedTuple)` can just intuitively work. After all, when one reads `class A(B):` they should expect `issubclass(A, B)` to return `True`.

Same goes for `TypedDict`.

---

<div class="post-metadata">

**Author:** ![AndersMunch](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/andersmunch/32/12823_2.png) [@AndersMunch](https://discuss.python.org/u/AndersMunch)\
**Post date:** [September 10, 2025, 10:33am UTC](https://discuss.python.org/t/namedtuple-instance-check/103651/4 "2025-09-10T10:33:27Z")

</div>

@kddnewton asked for test for an instance of a class created by the namedtuple function. This is not it.

```python
import collections, types, typing

Point = collections.namedtuple('Point', ['x', 'y'])
assert typing.NamedTuple in types.get_original_bases(Point) # fails

```

---

<div class="post-metadata">

**Author:** ![Dutcho](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/dutcho/32/7074_2.png) [@Dutcho](https://discuss.python.org/u/Dutcho)\
**Post date:** [September 10, 2025, 10:46am UTC](https://discuss.python.org/t/namedtuple-instance-check/103651/5 "2025-09-10T10:46:35Z")

</div>

Indeed @blhsing’s solution **works** as demonstrated for `typing.NamedTuple`, but **not** for `collections.namedtuple`. As `typing` remains optional, it’s a partial solution at best, alas

---

<div class="post-metadata">

**Author:** ![Dutcho](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/dutcho/32/7074_2.png) [@Dutcho](https://discuss.python.org/u/Dutcho)\
**Post date:** [September 10, 2025, 11:33am UTC](https://discuss.python.org/t/namedtuple-instance-check/103651/6 "2025-09-10T11:33:12Z")

</div>

For comparison, `dataclass`es have `dataclasses.is_dataclass()` and `useful_types.experimental.DataclassLike`. These both test for presence of attribute ` __dataclass_fields__ `.  
`is_namedtuple()` could do the same by checking for any subset of attributes mentioned by OP.

---

<div class="post-metadata">

**Author:** ![blhsing](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/blhsing/32/25812_2.png) [@blhsing](https://discuss.python.org/u/blhsing)\
**Post date:** [September 10, 2025, 2:14pm UTC](https://discuss.python.org/t/namedtuple-instance-check/103651/7 "2025-09-10T14:14:20Z")

</div>

> [@AndersMunch](#):
>
> @kddnewton asked for test for an instance of a class created by the namedtuple function. This is not it.

> [@Dutcho](#):
>
> Indeed @blhsing’s solution **works** as demonstrated for `typing.NamedTuple`, but **not** for `collections.namedtuple`. As `typing` remains optional, it’s a partial solution at best, alas

Oops I totally misread the OP and thought it was for `typing.NamedTuple`, probably due to the capitalized letter N in the title.

> [@kddnewton](#):
>
> Currently in the Python ecosystem, this is implemented with myriad error-prone heuristics

Have there been incidents where such heuristics caused actual errors/false positives in the real world? In the spirit of Python’s tradition of duck typing I think the heuristics of testing for the presence of those well-documented methods are an acceptable practice. You can even bundle them as a protocol if you’d like.

If you’re really concerned about the heuristics being error-prone, how about the following test as a more error-proof heuristic?

```python
import collections

Point = collections.namedtuple('Point', ['x', 'y'])
assert Point._make. __func__. __code__.co_filename == collections. __file__

```

---

<div class="post-metadata">

**Author:** ![storchaka](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/storchaka/32/217_2.png) [@storchaka](https://discuss.python.org/u/storchaka)\
**Post date:** [September 10, 2025, 3:31pm UTC](https://discuss.python.org/t/namedtuple-instance-check/103651/8 "2025-09-10T15:31:59Z")

</div>

AFAIK, it was intentional design decision that `isinstance()` and `issublass()` don’t work for named tuples. Named tuples are not part of specific class hierarhy, this is a class of types that implements some interface (access to fields by attribute name and by index). You can implement a named tuple without using `namedtuple()`. For example, by using the `PyStructSequence_*` C API.

cc @rhettinger

---

<div class="post-metadata">

**Author:** ![NeilGirdhar](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/neilgirdhar/32/5776_2.png) [@NeilGirdhar](https://discuss.python.org/u/NeilGirdhar)\
**Post date:** [September 10, 2025, 4:16pm UTC](https://discuss.python.org/t/namedtuple-instance-check/103651/9 "2025-09-10T16:16:19Z")

</div>

> [@blhsing](#):
>
> In the spirit of Python’s tradition of duck typing I think the heuristics of testing for the presence of those well-documented methods are an acceptable pr

Even with duck typing, I think you should be able to easily ask questions about a type if you want to—even if this happened to be a conscious design decision.

> [@blhsing](#):
>
> You can even bundle them as a protocol if you’d like.

In this case, you can’t do that.

> [@blhsing](#):
>
> If you’re really concerned about the heuristics being error-prone, how about the following test as a more error-proof heuristic?

Yuck though, right 🙂

---

<div class="post-metadata">

**Author:** ![jorenham](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jorenham/32/27806_2.png) [@jorenham](https://discuss.python.org/u/jorenham)\
**Post date:** [September 10, 2025, 4:27pm UTC](https://discuss.python.org/t/namedtuple-instance-check/103651/10 "2025-09-10T16:27:46Z")

</div>

This makes a lot of sense. I actually think it’s rather strange that this doesn’t work already, and I can see how someone might even classify this as a bug.

The workarounds that have been proposed here work at runtime, but not for static typing, i.e. type-narrowing.

---

<div class="post-metadata">

**Author:** ![blhsing](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/blhsing/32/25812_2.png) [@blhsing](https://discuss.python.org/u/blhsing)\
**Post date:** [September 11, 2025, 1:49am UTC](https://discuss.python.org/t/namedtuple-instance-check/103651/11 "2025-09-11T01:49:40Z")

</div>

> [@NeilGirdhar](#):
>
> Even with duck typing, I think you should be able to easily ask questions about a type if you want to—even if this happened to be a conscious design decision.

True.

The best way forward is for sure to make `issubclass` work for `namedtuple`-created classes, but adding a dummy marker class to the bases may break some of the existing codes that explicitly check that `tuple` is the only superclass, e.g.:

> - numba/numba (numba/numba/blob/5e0ceb077fb219a21ac8a50e5234ca643aaf96f9/numba/core/ir\_utils.py#L1962-L1977): checks that `tuple` is the only superclass, as well as `_make` and `_fields`

So it might make sense to make `collections.namedtuple` follow the footsteps of `typing.NamedTuple` and add itself to the new class’ ` __orig_bases__ ` and make its ` __mro_entries__ ` return `(tuple,)`, and make `issubclass` search in ` __orig_bases__ `, so `issubclass(Point, namedtuple)` can work without a dummy superclass.

On the other hand, codes that explicitly check that `tuple` is the only superclass would already fail to recognize a generic `typing.NamedTuple` class as a valid `namedtuple` class (because `typing.Generic` would be the second base class), so it’s probably good in the long term to break them and force them to adopt a better check.

---

<div class="post-metadata">

**Author:** ![NeilGirdhar](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/neilgirdhar/32/5776_2.png) [@NeilGirdhar](https://discuss.python.org/u/NeilGirdhar)\
**Post date:** [September 11, 2025, 5:30am UTC](https://discuss.python.org/t/namedtuple-instance-check/103651/12 "2025-09-11T05:30:36Z")

</div>

> [@blhsing](#):
>
> So it might make sense to make `collections.namedtuple` follow the footsteps of `typing.NamedTuple` and add itself to the new class’ ` __orig_bases__ ` and make its ` __mro_entries__ ` return `(tuple,)`, and make `issubclass` search in ` __orig_bases__ `, so `issubclass(Point, namedtuple)` can work without a dummy superclass.

It would be much simpler to just put `NamedTuple` into the ` __mro__ ` like any other base class.

---

<div class="post-metadata">

**Author:** ![blhsing](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/blhsing/32/25812_2.png) [@blhsing](https://discuss.python.org/u/blhsing)\
**Post date:** [September 11, 2025, 5:37am UTC](https://discuss.python.org/t/namedtuple-instance-check/103651/13 "2025-09-11T05:37:18Z")

</div>

> [@NeilGirdhar](#):
>
> It would be much simpler to just put `NamedTuple` into the ` __mro__ ` like any other base class.

But `NamedTuple` is a function, not a class, so it can’t be in ` __mro__ `, which is why we need ` __orig_bases__ ` and ` __mro_entries__ `.

---

<div class="post-metadata">

**Author:** ![NeilGirdhar](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/neilgirdhar/32/5776_2.png) [@NeilGirdhar](https://discuss.python.org/u/NeilGirdhar)\
**Post date:** [September 11, 2025, 5:40am UTC](https://discuss.python.org/t/namedtuple-instance-check/103651/14 "2025-09-11T05:40:35Z")

</div>

I mean to create a class that would be added to all named tuples.

---

<div class="post-metadata">

**Author:** ![blhsing](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/blhsing/32/25812_2.png) [@blhsing](https://discuss.python.org/u/blhsing)\
**Post date:** [September 11, 2025, 5:42am UTC](https://discuss.python.org/t/namedtuple-instance-check/103651/15 "2025-09-11T05:42:56Z")

</div>

Yes, if we are willing to break some existing codes, like I mentioned.

---

<div class="post-metadata">

**Author:** ![NeilGirdhar](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/neilgirdhar/32/5776_2.png) [@NeilGirdhar](https://discuss.python.org/u/NeilGirdhar)\
**Post date:** [September 11, 2025, 5:46am UTC](https://discuss.python.org/t/namedtuple-instance-check/103651/16 "2025-09-11T05:46:17Z")

</div>

How does having `namedtuple` return a class having an extra base class breaks any explicit promises in the documentation?

---

<div class="post-metadata">

**Author:** ![blhsing](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/blhsing/32/25812_2.png) [@blhsing](https://discuss.python.org/u/blhsing)\
**Post date:** [September 11, 2025, 5:50am UTC](https://discuss.python.org/t/namedtuple-instance-check/103651/17 "2025-09-11T05:50:25Z")

</div>

Nothing promised is broken. It’s just that some existing codes (such as the example from numba) makes the false (well, true at the time, but fragile) assumption that `tuple` is always the only superclass of a `namedtuple` class. Adding a new base class will break those codes.

---

<div class="post-metadata">

**Author:** ![kddnewton](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/kddnewton/32/30757_2.png) [@kddnewton](https://discuss.python.org/u/kddnewton)\
**Post date:** [September 20, 2025, 1:24am UTC](https://discuss.python.org/t/namedtuple-instance-check/103651/18 "2025-09-20T01:24:34Z")

</div>

So what would be next steps here? It seems like this has some for and some against. Is this the kind of thing where I should put together a commit that we could look at together? (Sorry still my first post here, so trying to determine the process.)

---

<div class="post-metadata">

**Author:** ![dg-pb](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/dg-pb/32/16248_2.png) [@dg-pb](https://discuss.python.org/u/dg-pb)\
**Post date:** [September 20, 2025, 4:37am UTC](https://discuss.python.org/t/namedtuple-instance-check/103651/19 "2025-09-20T04:37:01Z")

</div>

Maybe something along the lines…

In `collections. __init__.py`:

```python
class _NAMEDTUPLE: pass

def namedtuple(...):
    ...
    class_namespace = {
        ...
        __namedtuple__ : _NAMEDTUPLE
    }

```

In `_collections_abc.py`:

```python
class NamedTuple(metaclass=ABCMeta):
    @classmethod
    def __subclasshook__ (cls, C):
        return (
            C. __mro__ [1] is tuple
            and getattr(C, ' __namedtuple__', None) is collections._NAMEDTUPLE
        )

```

Then:

```python
MyNamedTuple = collections.namedtuple('MNT', [])
print(isinstance(MyNamedTuple, NamedTuple))
# Hopefully True

```

---

<div class="post-metadata">

**Author:** ![oscarbenjamin](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/oscarbenjamin/32/1209_2.png) [@oscarbenjamin](https://discuss.python.org/u/oscarbenjamin)\
**Post date:** [September 20, 2025, 12:14pm UTC](https://discuss.python.org/t/namedtuple-instance-check/103651/20 "2025-09-20T12:14:14Z")

</div>

In general I don’t see why it is necessary to use `isinstance` here. A class created by `namedtuple` is just a class and different classes created by `namedtuple` are just different classes. There is no need for there to be any common superclass besides tuple.

Maybe a function `is_namedtuple` could be added like `is_dataclass` (although `is_dataclass` has a weird [dual meaning](https://docs.python.org/3/library/dataclasses.html#dataclasses.is_dataclass)).

For serialisation you can use ` __getnewargs__ `:

```python
In [1]: from collections import namedtuple

In [2]: A = namedtuple('A', ['x', 'y'])

In [3]: a = (1, 2)

In [4]: type(a)(*a. __getnewargs__ ())
Out[4]: (1, 2)

In [5]: a = A(1,2)

In [6]: type(a)(*a. __getnewargs__ ())
Out[6]: A(x=1, y=2)

```

Of course you would need to be able to serialise the class `A` and pickle has a fragile mechanism for doing this that fails for dynamically generated namedtuple classes.

For manual serialisation it is important to check exact types like:

```python
# Don't use isinstance(obj, tuple):
if type(obj) is tuple:
    # Handle the tuple case
    # Any subclass of tuple may have a different constructor
    # and the class also needs to be serialised so we don't
    # handle those cases here.

```
