Dictionary populated on demand
__missing__
I would have started to propose a simpler design using the __missing__ dunder so that, each time a user tries to access a command that has not been populated, the __missing__ method would be called and you could fetch the informations:
def Foo(dict):
    def __missing__(self, key):
        self[key] = item = fetch_item(key)
        return item

And voilà. I wouldn't try to enforce barriers to avoid users behaviour. After all, we're all consenting adults here.
Playing it nice with the interactive shell
This behaviour, however let the user clueless about all the possible commands available at the tap of the TAB key. They are still able to call get_item_names if need be. But you could automatically build them using some metaclass logic.
The class you want in the end could be:
def Foo:
    _&lt;some_name&gt; = None

    @property
    def &lt;some_name&gt;(self):
        if self._&lt;some_name&gt; is None:
            self._&lt;some_name&gt; = fetch_item(&lt;some_name&gt;)
        return self._&lt;some_name&gt;

    def __getitem__(self, key):
        return getattr(self, key)

with every &lt;some_name&gt; being returned by get_item_names. Read-only of the attributes are ensured by the property.
So you need to define your base class being:
class Foo(metaclass=CommandBuilder):
    def __getitem__(self, key):
        return getattr(self, key)

And build your metaclass like
class CommandBuilder(type):
    def __new__(cls, name, bases, dct):
        for command_name in get_item_names():
            _command_name = '_{}'.format(command_name)
            dct[_command_name] = None

            def getter(self):
                command_value = getattr(self, _command_name)
                if command_value is None:
                    command_value = fetch_item(command_name)
                    setattr(self, _command_name, command_value)
                return command_value

            dct[command_name] = property(getter)

        return super(CommandBuilder, cls).__new__(cls, name, bases, dct)

If you trully need to use a subclass of dict here, it wouldn't be much more difficult:
def Foo(dict, metaclass=CommandBuilder):
    def __missing__(self, key):
        item = getattr(self, key)  # will call the property
        super(Foo, self).__setitem__(key, item)
        return item

    def __setitem__(self, key, value):
         pass  # or raise an exception of your own

    def __delitem__(self, key):
        pass  # or raise an exception of your own

Immutability of the attributes is still guarantied by property.