В Python, как можно повторно использовать существующие одинаковые неизменяемые объекты (например, для str
)? Можно ли это сделать только путем определения метода __hash__
или требуется более сложные меры?
Повторное использование существующих объектов для неизменяемых объектов?
Ответ 1
Если вы хотите создать через конструктор класса и вернуть его ранее созданному объекту, вам необходимо предоставить метод __new__
(потому что к тому времени, когда вы дойдете до __init__
, объект уже создан).
Вот простой пример: если значение, используемое для инициализации, было замечено до этого, возвращается ранее созданный объект, а не новый:
class Cached(object):
"""Simple example of immutable object reuse."""
def __init__(self, i):
self.i = i
def __new__(cls, i, _cache={}):
try:
return _cache[i]
except KeyError:
# you must call __new__ on the base class
x = super(Cached, cls).__new__(cls)
x.__init__(i)
_cache[i] = x
return x
Обратите внимание, что для этого примера вы можете использовать что-либо для инициализации, пока оно hashable. И просто чтобы показать, что объекты действительно используются повторно:
>>> a = Cached(100)
>>> b = Cached(200)
>>> c = Cached(100)
>>> a is b
False
>>> a is c
True
Ответ 2
Я считаю, что вам нужно будет сохранить dict {args: object} из уже созданных экземпляров, затем переопределить метод класса __new__
для проверки этого словаря и вернуть соответствующий объект, если он уже существует. Обратите внимание, что я не реализовал или не тестировал эту идею. Конечно, строки обрабатываются на уровне C.
Ответ 3
Существует два решения для "разработки программного обеспечения", которые не требуют каких-либо низкоуровневых знаний о Python. Они применяются в следующих сценариях:
Первый сценарий:. Объекты вашего класса "равны", если они построены с одинаковыми параметрами конструктора, а равенство не изменится со временем после построения. Решение. Используйте factory, который использует параметры конструктора:
class MyClass:
def __init__(self, someint, someotherint):
self.a = someint
self.b = someotherint
cachedict = { }
def construct_myobject(someint, someotherint):
if (someint, someotherint) not in cachedict:
cachedict[(someint, someotherint)] = MyClass(someint, someotherint)
return cachedict[(someint, someotherint)]
Этот подход существенно ограничивает экземпляры вашего класса одним уникальным объектом для каждой входной пары. Есть и очевидные недостатки: не все типы легко хешируются и т.д.
Второй сценарий: Объекты вашего класса изменяемы, а их "равенство" может меняться со временем. Решение. Определите реестр уровня равным образом:
class MyClass:
registry = { }
def __init__(self, someint, someotherint, third):
MyClass.registry[id(self)] = (someint, someotherint)
self.someint = someint
self.someotherint = someotherint
self.third = third
def __eq__(self, other):
return MyClass.registry[id(self)] == MyClass.registry[id(other)]
def update(self, someint, someotherint):
MyClass.registry[id(self)] = (someint, someotherint)
В этом примере объекты с одинаковой парой someint, someotherint
равны, а третий параметр не учитывается. Трюк заключается в том, чтобы синхронизировать параметры в registry
. В качестве альтернативы update
вы можете переопределить getattr
и setattr
для своего класса; это обеспечило бы синхронизацию любого присваивания foo.someint = y
с вашим словарем уровня. См. Пример здесь.