Добре, ето сценария от реалния свят: пиша приложение и имам клас, който представлява определен тип файлове (в моя случай това са снимки, но този детайл е без значение за проблема). Всеки екземпляр на класа Photograph трябва да бъде уникален за името на файла на снимката.
Проблемът е, че когато потребител каже на приложението ми да зареди файл, трябва да мога да идентифицирам кога файловете вече са заредени и да използвам съществуващия екземпляр за това име на файл, вместо да създавам дублирани екземпляри на същото име на файл.
За мен това изглежда като добра ситуация за използване на мемоизация и има много примери за това, но в този случай аз не просто мемоизирам обикновена функция, трябва да мемоизирам __init__()
. Това създава проблем, тъй като докато __init__()
бъде извикан, вече е твърде късно, тъй като вече има създаден нов екземпляр.
В моето проучване открих метода __new__()
на Python и всъщност успях да напиша работещ тривиален пример, но той се разпадна, когато се опитах да го използвам върху моите обекти от реалния свят и не съм сигурен защо (единственото нещо, което за което мога да се сетя е, че моите обекти от реалния свят бяха подкласове на други обекти, които не мога да контролирам наистина, и така имаше някои несъвместимости с този подход). Ето какво имах:
class Flub(object):
instances = {}
def __new__(cls, flubid):
try:
self = Flub.instances[flubid]
except KeyError:
self = Flub.instances[flubid] = super(Flub, cls).__new__(cls)
print 'making a new one!'
self.flubid = flubid
print id(self)
return self
@staticmethod
def destroy_all():
for flub in Flub.instances.values():
print 'killing', flub
a = Flub('foo')
b = Flub('foo')
c = Flub('bar')
print a
print b
print c
print a is b, b is c
Flub.destroy_all()
Което извежда това:
making a new one!
139958663753808
139958663753808
making a new one!
139958663753872
<__main__.Flub object at 0x7f4aaa6fb050>
<__main__.Flub object at 0x7f4aaa6fb050>
<__main__.Flub object at 0x7f4aaa6fb090>
True False
killing <__main__.Flub object at 0x7f4aaa6fb050>
killing <__main__.Flub object at 0x7f4aaa6fb090>
Перфектно е! Бяха направени само две инстанции за двата дадени уникални идентификатора, а Flub.instances очевидно има само две изброени.
Но когато се опитах да използвам този подход с обектите, които използвах, получих всякакви безсмислени грешки за това как __init__()
взе само 0 аргумента, а не 2. Така че промених някои неща и тогава щеше да ми каже, че __init__()
има нужда аргумент. Напълно странно.
След известно време на борба с него, аз просто се отказах и преместих цялата __new__()
черна магия в статичен метод, наречен get
, така че да мога да извикам Photograph.get(filename)
и той ще извика само Photograph(filename)
, ако името на файла вече не е в Photograph.instances
.
Някой знае ли къде сбърках тук? Има ли по-добър начин да направите това?
Друг начин да мислим за това е, че е подобно на сингълтън, с изключение на това, че не е глобално сингълтон, а само сингълтон на име на файл.
Ето моя код от реалния свят, използващ статичния метод get, ако искате да го видите всички заедно.