How is Python able to distinct type names of namedtuples?

69 views Asked by At

My model class has to provide lots of queries and I would like to return query results as namedtuple. My solution is working, but I don't know why :) - so I would like to get some clarification whether this is a good or valid design - or not.

Simplified sample code:

class SomeModel():
    def query01(self):
        Row = namedtuple("Row", "a")
        row = Row("a1")
        return row

    def query02(self):
        Row = namedtuple("Row", "a b c")
        row = Row(1,2,3)
        return row

sample = SomeModel()
item01 = sample.query01()
item02 = sample.query02()
print(item01)
print(item02)

The output is completely as expected:

Row(a='a1')
Row(a=1, b=2, c=3)

Now for the unexpected ...

print(type(item01))
print(type(item02))

will provide:

<class '__main__.Row'>
<class '__main__.Row'>

If I would replace the namedtuples within the function with local classes the types would look as expected:

<__main__.query01.<locals>.Row at 0x11073eb30>
<__main__.query02.<locals>.Row at 0x11073fcd0>

Finally my question:

I would prefer the namedtuple approach over classes and it works like expected, but it feels a little bit strange that the type names are complete identical. How is Python able to distinct the two items, when their type names are the same? The naming convention for local classes make much more sense for me. Is it safe, to use namedtuple this way?

You can argue that I could use different namedtuple names inside my functions, but with lots of queries you can not guaranty that the names are always unique.

1

There are 1 answers

1
juanpa.arrivillaga On

They are not identical:

If you add:

print(type(item01) is type(item02))

It will print False.

Types are just objects like any other. Their name's can be identical. It doesn't make the type identical. This expectation of yours is simply false. Note an even simpler case:

>>> class Foo: pass
...
>>> foo1 = Foo()
>>> class Foo: pass
...
>>> foo2 = Foo()
>>> type(foo1), type(foo2)
(<class '__main__.Foo'>, <class '__main__.Foo'>)
>>> type(foo1) is type(foo2)
False
>>> print(isinstance(foo1, Foo), isinstance(foo2, Foo))
False True

The only reason this would be unsafe is if some code depended on unique types having unique names. But since that isn't a guarantee, that code would be fundamentally unsound, though.

But note, you should take care not to unnecessarily recreate a named tuple class for each instance of the tuple. That is unnecessarily wasteful.

Row = namedtuple("Row", "a b c")

Should be at the module level.