Skip to content

Instantly share code, notes, and snippets.

@xmodar
Created September 18, 2019 22:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xmodar/362dc860642b8ff9c765c72c740e213b to your computer and use it in GitHub Desktop.
Save xmodar/362dc860642b8ff9c765c72c740e213b to your computer and use it in GitHub Desktop.
Create namedtuple types for function outputs only once with nice type name.
from collections import namedtuple
from types import MethodType, FunctionType
# using neither typing.NamedTuple nor dataclasses.dataclass
def named_tuple(function, field_names, *args, name=None, **kwargs):
"""Memoize namedtuple types for function outputs."""
if isinstance(function, MethodType):
function = function.__func__
assert isinstance(function, FunctionType)
if name is None or name.startswith('.'):
suffix = '' if name is None else name
name = function.__qualname__ + suffix
name = name.replace('.', chr(0xB7)) # centered dot
assert name.isidentifier(), f'Invalid identifier: {name}'
if not hasattr(function, name):
output_type = namedtuple(name, field_names, *args, **kwargs)
output_type.__module__ = function.__module__
setattr(function, name, output_type)
return getattr(function, name)
# usage examples...
def f():
Output = named_tuple(f, ('x', 'y'))
return Output(3, 2)
def g():
# always use unique names for the same function
Point = named_tuple(g, ('x', 'y'), name='Point')
# prepend a dot to the name for a fully qualified type name
Line = named_tuple(g, ('start', 'end'), name='.Line')
return Line(Point(2, 1), Point(3, 2))
class A:
def f(self):
Output = named_tuple(self.f, ('x', 'y'))
return Output(3, 2)
@classmethod
def g(cls):
Output = named_tuple(cls.g, ('x', 'y'))
return Output(3, 2)
@staticmethod
def h():
Output = named_tuple(A.h, ('x', 'y'))
return Output(3, 2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment