Пользовательский python словарь, принимающий в качестве ключей и строку, и перечисления - PullRequest
0 голосов
/ 06 апреля 2020

Я хочу создать словарь, в котором его ключи будут храниться как строковые переменные, но при этом можно будет получать его значения, используя перечисления.

Причина в том, что мой код делится этим словарем с другими программами и процессами, сохраняя его в базе данных и отправляя его другим приложениям с помощью REST. Чтобы упростить управление самим словарем, я бы хотел, чтобы ключи имели тип string. Тем не менее, я был бы признателен, если бы я мог получить значения по перечислениям.

Я не хочу получать доступ к значениям просто с помощью строк, поскольку это будет использовать "magi c strings".
Опция константных переменных просто безобразна, и кажется, что enum - это лучшее решение этой проблемы. При этом я не хочу, чтобы он использовал .value каждый раз, потому что это делает код длиннее и уродливее.

Можно ли добавить поддержку как строк, так и перечислений?

Ответы [ 2 ]

0 голосов
/ 06 апреля 2020

Объедините ваш Enum с str, тогда каждый член будет строкой, а также Enum, и вы можете использовать обычный dict:

class Test(str, Enum):
    A = "abc"
    B = "xyz"
    C = "qwerty"

my_dict = {"ok": "fine", Test.B: "bla-bla"}

Единственное отличие - отображение из условия, когда ключом Enum является ключ:

>>> my_dict
{'ok': 'fine', <Test.B: 'xyz'>: 'bla-bla'}

Но он все равно работает просто отлично:

>>> my_dict['xyz']
'bla-bla'

>>> my_dict[Test.B]
'bla-bla'
0 голосов
/ 06 апреля 2020

Вам необходимо переопределить методы __setitem__ и __getitem__ для поддержки как строковых, так и перечислимых типов.
Ваша цель - получить параметр метода и преобразовать его в строку, если это перечисление. Таким образом, можно получить и сохранить строковые значения с помощью переменной enum.

Ваш код должен выглядеть следующим образом:

from collections import UserDict
from enum import Enum


class EnumDict(UserDict):
    def __setitem__(self, key, value):
        string_key = key.value if isinstance(key, Enum) else key
        super().__setitem__(string_key, value)

    def __getitem__(self, item):
        string_item = item.value if isinstance(item, Enum) else item
        return super().__getitem__(string_item)

Обратите внимание, что это сработает, только если вы на самом деле наследуете от UserDict.
Пожалуйста, прочитайте этот отличный пост в блоге " Проблема с наследованием от dict и list в Python ", чтобы понять, почему я использую UserDict вместо обычного dict. В противном случае вам также потребуется переопределить методы __init__, update и get.

Обратите внимание, что если вы используете python2 (что делать не следует, поскольку он больше не поддерживается), приведенный выше код не будет работать, поскольку UserDict используется как средство обратной совместимости для время, в которое вы не можете наследовать напрямую от dict. Он не обладает тем же набором функций и возможностей, что и в python3.

В любом случае, если вы используете python2 или просто не хотите наследовать от UserDict, ваш код должен выглядеть так:

from enum import Enum


class EnumDict(dict):
    def __init__(self, *args, **kwargs):
        self.update(*args, **kwargs)

    def update(self, *args, **kwargs):
        try:
            for key, value in args[0].items():
                self.__setitem__(key, value)
        except Exception:
            super().update(*args, *kwargs)

    def __setitem__(self, key, value):
        string_key = key.value if isinstance(key, Enum) else key
        super().__setitem__(string_key, value)

    def __getitem__(self, item):
        string_item = item.value if isinstance(item, Enum) else item
        return super().__getitem__(string_item)

    def get(self, key, default=None):
        string_key = key.value if isinstance(key, Enum) else key
        return super().get(string_key, default)

Для суммирования вверх, вся идея здесь заключается в приведении enum к его строковому значению.
При использовании dict, а не UserDict, нужно также позаботиться об инициализации, обновлении и регулярных процессах get - убедившись, что вы не будете застрять с перечислениями вместо обычных строк.

Вот пример того, как работает этот словарь:

In [1]: class Test(Enum): 
   ...:     A = "abc" 
   ...:     B = "xyz" 
   ...:     C = "qwerty"

In [2]: my_dict = EnumDict({"ok": "fine", Test.B: "bla-bla"})                   

In [3]: my_dict                                                                 
Out[3]: {'ok': 'fine', 'xyz': 'bla-bla'}

In [4]: my_dict[Test.A] = "hello"                                               

In [5]: my_dict["testing"] = "works"                                            

In [6]: my_dict                                                                 
Out[6]: {'ok': 'fine', 'xyz': 'bla-bla', 'abc': 'hello', 'testing': 'works'}

In [7]: my_dict.update({"isOkay": "yes", Test.B: "wow"})                        

In [8]: my_dict                                                                 
Out[8]: {'ok': 'fine', 'xyz': 'wow', 'abc': 'hello', 'testing': 'works', 'isOkay': 'yes'}

In [9]: my_dict.get(Test.C, "does not exist")                                  
Out[9]: 'does not exist'

In [10]: my_dict.get(Test.A)                                                    
Out[10]: 'hello'

In [11]: my_dict[Test.A]                                                        
Out[11]: 'hello'

In [12]: my_dict["xyz"]                                                         
Out[12]: 'wow'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...