python: defaultdict с аргументом не по умолчанию

Я хочу иметь что-то вроде dict из class TestClass с аргументом, отличным от значения по умолчанию. Когда я получаю доступ, я не знаю, был ли запрошенный элемент уже раньше. Итак, TestClass:

class TestClass(object):
    def __init__(self, name):
        self.name = name
        self.state = 0
    def getName(self):
        self.state = self.state + 1
        return "%s -- %i" % (self.name, self.state)

Затем dict и функция доступа:

db = {}
def getOutput(key):
    # this is a marvel in the world of programming langauges
    if key not in db:
        db[key] = TestClass(key)
    return db[key]

И фактический код тестирования:

if __name__ == "__main__":
    print "testing: %s" % getOutput('charlie').getName()

Хороший. Но мне интересно, есть ли более элегантное решение. Просматривая, я вспоминаю defaultdict. Но это не сработает, потому что я не могу передать аргумент в default_factory:

from collections import defaultdict
d = defaultdict(TestClass)
print "testing %s" % d['tom'].getOutput()

дает TypeError: __init__() takes exactly 2 arguments (1 given)... есть ли другое решение?

Кроме того, я хочу улучшить свой Python. Так что любые другие предложения также приветствуются ;-)


person user3474620    schedule 20.09.2014    source источник


Ответы (1)


Фабрика defaultdict действительно не принимает аргументов.

Однако вы можете создать свой собственный вариант, который работает; хитрость заключается в определении метода __missing__:

class TestClassDict(dict):
    def __missing__(self, key):
        res = self[key] = TestClass(key)
        return res

Всякий раз, когда осуществляется доступ к dict[key] для несуществующего key, вызывается метод __missing__. defaultdict использует этот хук для возврата factory() каждый раз, но вы можете указать свой собственный и передать key.

Демо:

>>> class TestClass(object):
...     def __init__(self, name):
...         self.name = name
...         self.state = 0
...     def getName(self):
...         self.state = self.state + 1
...         return "%s -- %i" % (self.name, self.state)
... 
>>> class TestClassDict(dict):
...     def __missing__(self, key):
...         res = self[key] = TestClass(key)
...         return res
... 
>>> db = TestClassDict()
>>> db['charlie'].getName()
'charlie -- 1'
>>> db
{'charlie': <__main__.TestClass object at 0x102f72250>}
person Martijn Pieters    schedule 20.09.2014
comment
Хорошее решение. Спасибо! - person user3474620; 21.09.2014
comment
Это действительно круто, и единственный ответ, который я смог найти, который легко показывает, как это сделать. - person ragardner; 16.06.2017