Python: как отключить создание новых ключей в атрибуте dict? - PullRequest
2 голосов
/ 19 сентября 2019

Вот простой код атрибута dict:

class AttrDict(dict):
    def __init__(self, *args, **kwargs):
        super(AttrDict, self).__init__(*args, **kwargs)
        self.__dict__ = self

Больше контекста: https://stackoverflow.com/a/14620633/1179925

Я могу использовать его следующим образом:

d = AttrDict({'a':1, 'b':2})
print(d)

Я хочуэто возможно:

d.b = 10
print(d)

Но я хочу, чтобы это было невозможно:

d.c = 4
print(d)

Возможно ли выдать ошибку при создании нового ключа?

Ответы [ 2 ]

2 голосов
/ 19 сентября 2019

Вы можете проверить, есть ли они там уже

class AttrDict(dict):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.__dict__ = self

    def __setattr__(self, key, value):
        if key not in [*self.keys(), '__dict__']:
            raise KeyError('No new keys allowed')
        else:
            super().__setattr__(key, value)

    def __setitem__(self, key, value):
        if key not in self:
            raise KeyError('No new keys allowed')
        else:
            super().__setitem__(key, value)

Сначала я подумал, что это будет плохой идеей, поскольку никакие начальные значения не могут быть добавлены, но из встроенных функций он утверждает следующее: dict(итерируемый) -> новый словарь инициализируется, как если бы через: d = {} для k, v в итерируемом: d [k] = v

Так что это позволяет вам изменять методы без ихвлияет на инициализацию класса, поскольку он создает новый из {} вместо своего собственного экземпляра.

Они смогут менять __дикт __ всегда, хотя ..

1 голос
/ 19 сентября 2019

Вы можете переопределить специальный метод __setattr__.

class AttrDict(dict):
    def __init__(self, *args, **kwargs):
        super(AttrDict, self).__setattr__('_initializing', True)
        super(AttrDict, self).__init__(*args, **kwargs)
        self.__dict__ = self
        super(AttrDict, self).__setattr__('_initializing', False)

    def __setattr__(self, x, value):
        if x == '_initializing':
            raise KeyError("You should not edit _initalizing")
        if self._initializing or x in self.__dict__.keys():
            super(AttrDict, self).__setattr__(x, value)
        else:
            raise KeyError("No new keys allowed!")

Обратите внимание, что мне нужно было добавить атрибут _initializing, чтобы __setattr__ различал атрибуты, созданные __init__, и атрибуты, созданныеusers.
Поскольку у python нет личных атрибутов, пользователи могут по-прежнему устанавливать _initializing в True и затем добавлять свои атрибуты в экземпляр AttrDict, поэтому я добавил дополнительную проверку, чтобы убедиться, что они не пытаютсядля редактирования _initializing.
Это все еще не на 100% безопасно, так как пользователь все еще может использовать super(), чтобы установить _initializing в True.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...