init Cannot Be a Generator
ID |
python.init_not_generator |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
Python |
Tags |
reliability, suspicious-construct |
Description
Reports init methods that contain yield or yield from. The presence of a yield anywhere in a function turns it into a generator: the body does not execute when called, and the call returns a generator object. Inside init that means the constructor never runs the assignments to self, so the resulting instance is half-constructed; worse, init is contractually supposed to return None, and the implicit "return generator" raises TypeError once Python tries to assign the generator to nothing.
class Bad:
def __init__(self):
self.x = 1
yield self # FLAW
class BadFrom:
def __init__(self, items):
yield from items # FLAW
class Good:
def __init__(self):
self.x = 1
Rationale
The combination of "constructor expected to mutate self`" and "generator that defers all work" is a category error. A class that wants to expose iteration should expose it through `iter, not by accident through init.
Remediation
Move the iteration logic into iter (or a dedicated method) and keep init as a regular function that initialises attributes:
class Good:
def __init__(self, items):
self.items = items
def __iter__(self):
yield from self.items
References
-
https://docs.python.org/3/reference/datamodel.html#object.init[Python — init]