
On Mon, May 02, 2022 at 07:44:14PM +0100, Paul Moore wrote:
I have classes with 20+ parameters (packaging metadata). You can argue that a dataclass would be better, or some other form of refactoring, and you may actually be right. But it is a legitimate design for that use case.
Indeed. 20+ parameters is only a code smell, it's not *necessarily* wrong. Sometimes you just need lots of parameters, even if it is ugly. For reference, open() only takes 8, so 20 is a pretty wiffy code smell, but it is what it is.
In that sort of case, 20+ lines of assignments in the constructor *are* actually rather unreadable, not just a pain to write.
I don't know. Its pretty easy to skim lines when reading, especially when they follow a pattern: self.spam = spam self.eggs = eggs self.cheese = cheese self.aardvark = aardvark self.hovercraft = hovercraft self.grumpy = grumpy self.dopey = dopey self.doc = doc self.happy = happy self.bashful = bashful self.sneezy = sneezy self.sleepy = sleepy self.foo = foo self.bar = bar self.baz = baz self.major = major self.minor = minor self.minimus = minimus self.quantum = quantum self.aether = aether self.phlogiston = phlogiston Oh that was painful to write! But I only needed to write it once, and I bet that 99% of people reading it will just skim down the list rather than read each line in full. To be fair, having written it once, manual refactoring may require me to rewrite it again, or at least edit it. In early development, sometimes the parameters are in rapid flux, and that's really annoying. But that's just a minor period of experimental coding, not an on-going maintenance issue.
Of course the real problem is that you often don't want to *quite* assign the argument unchanged - `self.provides_extras = set(provides_extras or [])` or `self.requires_python = requires_python or specifiers.SpecifierSet()` are variations that break the whole "just assign the argument unchanged" pattern.
Indeed. Once we move out of that unchanged assignment pattern, we need to read more carefully rather than skim self._spam = (spam or '').lower().strip() but you can't replace that with auto assignment.
As a variation on the issue, which the @ syntax *wouldn't* solve, in classmethods for classes like this, I often find myself constructing dictionaries of arguments, copying multiple values from one dict to another, sometimes with the same sort of subtle variation as above:
@classmethod def from_other_args(cls, a, b, c, d): kw = {} kw["a"] = a kw["b"] = b kw["c"] = c kw["d"] = d return cls(**kw)
You may find it easier to make a copy of locals() and delete the parameters you don't want, rather than retype them all like that: params = locals().copy() for name in ['cls', 'e', 'g']: del params[name] return cls(**params)
Again, in "real code", not all of these would be copied, or some would have defaults, etc. The pattern's the same, though - enough args arecopied to make the idea of marking them with an @ seem attractive.
But the @ proposal here won't help. If you mark them with @, won't they be auto-assigned onto cls? -- Steve