Python defined 'types' - intermediate dev pet peeve

66 views Asked by At

TLDR: This is a specific question about best approach to coding a common need in Python, of creating non-flat 'types' - one that IMHO (as 3 year intermediate level python dev) is probably the greatest problem I have with 'how do I do that easily in Python'.

For moderators: I do have specific code and questions at the bottom :D

Background

Lets say I'm writing some code and I need a non primitive variable type:

So in meta code I'd like to define this in my code to use later:

MyType = type {
    mouth: bool
    nose: string
}

Now lets say the variable type I need isn't 'flat':

MyType = type {
    mouth: bool
    nose: string
    hair: {
        color: string
        length: float
    }
}

Now lets say I'd like to be able to easily (syntax-wise) create instances and reference attributes (again, meta-code):

anInstance = MyType(
    mouth=True,
    nose='big', 
    hair=(color='red', length=5.5)
)

if anInstance.mouth {
    print('has a mouth, and hair is', anInstance.hair.color)
}

So lets say my first requirement is Must Have Requirement:

  1. Easily declare a non-flat types

NOTE - The goal/requirement is to define the non-flat type in one go - not using composition of defining all the members separately and then composing the final type

Now lets also say I'd like - not as must have's but Really Like - two other things to get done in my code Nice To Have Requirements:

  1. Checking at run type for attribute type correctness
  2. Restriction on my type, so I can't add new attributes arbitrarily

So:

anInstance.nose = 5.5     # -> this tells me when it runs that it's Bad - wrong type
anInstance.chin = 'hairy' # -> Bad too - this attribute isn't declared in the definition

Problem

My opinion and experience: The above concept (declare non-flat type ...also perhaps with attribute type and definition enforecement) isn't easy to to in python

What I mean by 'isn't easy to do' is that

  1. There isn't a single standard way to do it
  2. It isn't something that is covered much/well/early in most python classes/tutorials etc - even though IMHO it is a very common code structure requirement

However in many (most?) other languages the above requirements - especially only (1) (non-flat composite types) are syntactically and conceptually easy.


...I know that some people will default to responding with:

  • python is duck-typed, so just deal with it
  • be a Man, and just write it all yourself using class factories
  • just use my favourite module/feature/meta-class template model X, Y, or Z or ...*
  • just do it with dictionaries, and screw using dot '.' attribute referencing.
  • just use a recursively embedded lambda defined default dictionary
  • ... ... ...

...all true points - but I would still contend the need for the above simply isn't quite there, the huge number of different ways of doing things are a problem and it isn't explained how to do in many beginner/intermediate type guides, even though (IHMO) it's a common use-case need in an app


Not-real-great-solution solution

I can easily meet requirement (1) by embedding a dataclass in a dataclass:


@dataclass
class MyType:

    @dataclass
    class HairType:
        color: str
        length: float

    mouth: bool
    nose: str
    hair: HairType

Likewise, I could use embedded named tuples. I could also use any number of modules (marshmellow, pydantic, ..., ...).

But - again - my contention/observation - this isn't concise and simple (as it could/should be?), and there are X different ways of doing it, even though (IMHO) it's a common requirement.

Questions

  • For the above type definition, (and optionally, also the attribute checking goals) what methods do people use? ...what are the pros and cons of the different options?
  • Is there a way to do the above - especially just requirement (1) - in a very simple, concise way in python?
  • Am I alone in thinking a) this is a common requirement and b) it's not quick and easy to do in Python (compared to some other languages)
0

There are 0 answers