[Python-ideas] Trial balloon: adding variable type declarations in support of PEP 484

Eric Snow ericsnowcurrently at gmail.com
Tue Aug 9 19:32:18 EDT 2016


On Tue, Aug 9, 2016 at 3:47 PM, אלעזר <elazarg at gmail.com> wrote:
> It's already possible to overload NamedTuple, in a way that will allow the
> following abuse of notation:
>
>     @NamedTuple
>     def Starship(damage:int, captain:str): pass
>
> The 'def' is unfortunate and potentially confusing (although it *is* a
> callable definition), and the ": pass" is meaningless. But I think it is
> clear and concise if you know what NamedTuple is.
>
> Introducing new keyword will of course solve both problems (if there's
> "async def", why not "type def"? :) ).

If we're dealing with classes then we should be using the class
syntax.  There are a number of options here for identifying attributes
in a definition and even auto-generating parts of the class (e.g.
__init__).  Let's look at several (with various objectives):

# For the sake of demonstration, we ignore opportunities for type inference.

# currently (with comments for type info)

class Bee(namedtuple('Bee', 'name ancient_injury managerie')):
    """can a bee be said to be..."""

    # name: str
    # ancient_injury: bool
    # menagerie: bool

    def __new__(cls, name='Eric', ancient_injury=False, menagerie=False):
        return super().__new__(cls, name, ancient_injury, menagerie)

    def half_a(self):
        return self.ancient_injury or self.menagerie


# using attribute annotations and a decorator (and PEP 520)

@as_namedtuple
class Bee:
    """..."""

    name: str = 'Eric'
    ancient_injury: bool = False
    menagerie: bool = False

    def half_a(self): ...


# using attribute annotations and a metaclass (and PEP 520)

class Bee(metaclass=NamedtupleMeta):
    """..."""

    name: str = 'Eric'
    ancient_injury: bool = False
    menagerie: bool = False

    def half_a(self): ...


# using one class decorator and PEP 520 and comments for type info

@as_namedtuple
class Bee:

    name = 'Eric'  # str
    ancient_injury = False  # bool
    menagerie = False  # bool

    def half_a(self): ...


# using one class decorator and comments for type info

@as_namedtuple('name ancient_injury managerie', name='Eric',
ancient_injury=False, menagerie=False)
class Bee:
    """..."""

    # name: str
    # ancient_injury: bool
    # menagerie: bool

    def half_a(self): ...


# using one class decorator (and PEP 468) and comments for type info
# similar to the original motivation for PEP 468

@as_namedtuple(name='Eric', ancient_injury=False, menagerie=False)
class Bee:
    """..."""

    # name: str
    # ancient_injury: bool
    # menagerie: bool

    def half_a(self): ...


# using a class decorator for each attribute

@as_namedtuple('name ancient_injury managerie')
@attr('name', str, 'Eric')
@attr('ancient_injury', bool, False)
@attr('menagerie', bool, False)
class Bee:
    """..."""

    def half_a(self): ...


Those are simple examples and we could certainly come up with others,
all using the class syntax.  For me, the key is finding the sweet spot
between readability/communicating intent and packing too many roles
into the class syntax.  To be honest, the examples using attribute
annotations seem fine to me.  Even if you don't need attr type info
(which you don't most of the time, particularly with default values),
my favorite solution (a class decorator that leverages PEP 520) is
still the most readable and in-line with the class syntax, at least
for me. :)

-eric


p.s. The same approaches could also be applied to generating
non-namedtuple classes, e.g. SimpleNamespace subclasses.


More information about the Python-ideas mailing list