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

ids = defaultdict()
ids.default_factory = ids.__len__

Есть ли что-то странное, что может пойти не так из-за использования здесь метода __len__?

4
Davis Yoshida 28 Июн 2019 в 23:08

3 ответа

Лучший ответ

Жаль, что вам нужно каждый раз назначать default_factory. Может быть, попробовать что-то вроде этого

class ConsecutiveIds(defaultdict):
    def __init__(self):
        super(ConsecutiveIds, self)
        self.default_factory = lambda: len(self)

d = ConsecutiveIds()

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

In [4]: d = ConsecutiveIds()

In [5]: d[0]
Out[5]: 0

In [6]: d[10]
Out[6]: 1

In [7]: d[20]
Out[7]: 2

In [8]: d
Out[8]: 
ConsecutiveIds(<function __main__.ConsecutiveIds.__init__.<locals>.<lambda>()>,
               {0: 0, 10: 1, 20: 2})

In [9]: del d[10]

In [10]: d[30]
Out[10]: 2

In [11]: d
Out[11]: 
ConsecutiveIds(<function __main__.ConsecutiveIds.__init__.<locals>.<lambda>()>,
               {0: 0, 20: 2, 30: 2})

Эта аномалия, по-видимому, отсутствует в ответе ikamen, но более кратко вы можете сказать:

class ConsecutiveIds(defaultdict):
    def __init__(self):
        super(ConsecutiveIds, self)
        self.default_factory = self.next_int
        self.index = 0

    def next_int(self):
        result = self.index
        self.index += 1
        return result

Это может быть уточнено как

class ConsecutiveIds(defaultdict):
    def __init__(self):
        super(ConsecutiveIds, self)
        counter = itertools.count()
        self.default_factory = lambda: next(counter)
1
munk 28 Июн 2019 в 20:52

Есть ли что-то странное, что может пойти не так из-за использования __len__ метод здесь?

Зависит от предполагаемого использования и ограничений. Какое поведение предназначено для того, чтобы отличить что-то не так от «вот как работает питон»?

Если вам нужен объект, дающий последовательные идентификаторы другим объектам, которые он «регистрирует», то вот менее удивительная реализация:

class ConseqIds:
    def __init__(self):
        self.ctr = 0
        self.elements = {}

    def assign_next(self, obj):
        self.elements[obj] = self.ctr
        self.ctr += 1

    def __getitem__(self, item):
        if not item in self.elements:
            self.assign_next(item)
        return self.elements[item]

ids = ConseqIds()
print(ids['first element'])
print(ids['second element'])
ids.assign_next('third element')

Вы избежите многих крайних случаев и будете уверены в том, что он делает.

2
ikamen 28 Июн 2019 в 20:38

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

ids = defaultdict()
ids.default_factory = lambda:len(ids)
0
Alain T. 28 Июн 2019 в 20:35