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:
- 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:
- Checking at run type for attribute type correctness
- 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
- There isn't a single standard way to do it
- 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)