A common design pattern when using python descriptors is to have the descriptor keep a dictionary of instances using that descriptor. For example, suppose I want to make an attribute that counts the number of times it's accessed:
class CountingAttribute(object):
def __init__(self):
self.count = 0
self.value = None
class MyDescriptor(object):
def __init__(self):
self.instances = {} #instance -> CountingAttribute
def __get__(self, inst, cls):
if inst in self.instances:
ca = self.instances[inst]
else:
ca = CountingAttribute()
self.instances[inst] = ca
ca.count += 1
return ca
class Foo(object):
x = MyDescriptor()
def main():
f = Foo()
f.x
f.x
print("f.x has been accessed %d times (including the one in this print)"%(f.x.count,))
if __name__ == "__main__":
main()
This is a completely silly example that doesn't do anything useful; I'm trying to isolate the main point.
The problem is that I can't use this descriptor in a class which isn't hashable, because the line
self.instances[inst] = ca
uses instances as a dictionary key. Is there a wise way of handling this sort of case? For example, one immediately thinks to use the instance's id
, but I'm not sure if doing that will break something about how hashes are supposed to be used.
EDIT: I realize that instances
should be something like a weakref.WeakKeyDictionary
but I'm trying to keep it simple here to focus on the issue of hashability.
WeakKeyDictionary
forinstances
. Unfortunately, this doesn't seem to fix the underlying problem of unhashability, but it's a good idea nonetheless. (Otherwise, the descriptor would keep an otherwise-dead object alive.) – Viburnum