Reputation: 1755
I'd like to use an instance of an inner class (in this case a namedtuple
although the exact same symptoms occur for an inner class defined with class
) as a default value for an outer class method (in this case the constructor). However, when this code is imported from a different module the outer class definition seems to be missing.
Example:
# mymodule.py
from typing import NamedTuple, Tuple
class IdSignal():
Cfg = NamedTuple('IdSignalCfg', [
('nfft', int),
('limits', Tuple[float, float]),
('min_spacing', float),
('nmix', int)])
Cfg.__new__.__defaults__ = (
512,
(1500, 7500),
200,
3
)
def __init__(self, cfg = IdSignal.Cfg()):
self.cfg = cfg
Now executing import mymodule
throws:
Exception has occurred: NameError
name 'IdSignal' is not defined
File "...", line 18, in IdSignal
def __init__(self, cfg = IdSignal.Cfg()):
File "...", line 5, in <module>
class IdSignal():
...
import mymodule
Confusingly, both pylint and mypy don't recognize any error in the above code.
Can this be achieved any other way?
I understand I can use None
as a default value and instantiate IdSignal.Cfg
within the constructor. If this is the only solution, I'd like to understand why is the above code failing?
Upvotes: 2
Views: 212
Reputation: 530872
At the time __init__
is defined, the name IdSignal
isn't yet bound to the class. (That doesn't happen until the entire body of the class
statement is evaluated, and the result of that evaluation is passed to the relevant metaclass.) However, Cfg
is also not yet a class attribute; it's just a name in the same "scope" in which __init__
is defined, so you don't need to qualify the name.
def __init__(self, cfg=Cfg()):
self.cfg = cfg
A class
statement like
class Foo:
x = 3
def __init__(self, y):
self.y = y
is roughly equivalent to
# These names don't really matter, but I'm using different
# names than what the attributes will be just to emphasize
# the point that they really are distinct objects before
# the new class is ever created.
class_x = 3
def some_init(self, y):
self.y = y
Foo = type('Foo', (object,), {'__init__': some_init, 'x': class_x})
Notice that the name Foo
doesn't come into existence until the very end. A class
statement doesn't define a new scope like a module or function does, but neither are names defined in a class
statement part of any enclosing scope; think of them as temporary names that are discarded once the class is created.
Upvotes: 7