On Mon, 09 Jun 2014 16:48:09 +0200 leif <not.rea...@online.de> wrote:
> Volker Braun wrote: > > On Monday, June 9, 2014 12:54:56 AM UTC+1, vdelecroix wrote: > > > > sage: {3, AA(3)} > > {3, 3} > > > > > > IMHO you should never put sage objects with different parents into an > > associative container. > > Although that's presumably an anti-pythonic attitude, +1. (Provided > they *won't* have sex with each other that is.) IMHO this should be disallowed. All objects that implement __hash__ should live up to contract of __hash__, which is that a==b implies hash(a)==hash(b). If this contract is violated, then the object is not worthy of being called hashable and sage should not pretend that it is hashable. It is too easy to forget to coerce and get a dict that violates a==b => d[a]==d[b], especially since this coercion is not necessary in python and not for ZZ->QQ, ZZ->RR, ZZ->CC, and RR->CC. I would propose that instead of __hash__ something like _sage_hash should be used that implements a hash satisfying self == other and self.parent() == other.parent() implies self._sage_hash() == other._sage_hash(). Then there could be sage specific associative containers using this hash. For instance something like this: d = MyDict(CoerceTo(complex)) d[1] = 0 d[1+I] = 3 e = MyDict(CoerceTo(ZZ)) e[1] = 0 e[1+I] = 3 # ERROR! def sage_hash(x): #TODO: handle tuples, ... try: return x._sage_hash() except AttributeError: return hash(x) class Shim: def __init__(self, hash_manager, data): self.hm = hash_manager self.data = data def __hash__(self): return self.hm.hash(self) def __eq__(self, other): return self.hm.equal(self, other) class MyDict: def __init__(self, hash_manager, data = []): self.hm = hash_manager self.content = dict() #TODO initialize with stuff in data def __setitem__(self, key, val): if not self.hm.acceptable(key): throw "cannot accomodate this kind of key" self.content[Shim(hm, key)] = val class CoerceTo: def __init__(self, target): self.target = target def acceptable(self, data): try: data = target(data) except: return False else: return True def hash(self, data): data = target(data) return sage_hash(data) def equal(self, other): return target(self) == target(data) The hash_manager could be lazy about what kinds of objects it accepts. For instance a MyDict with the manager below would accept anything with the same parent as the first element: class CoerceToFirst: def __init__(self): self.inner = None def acceptable(self, data): if self.inner is None: # FIXE: handle python types self.inner = CoerceTo(data.parent()) return self.inner.expand(data) def __getattr__(self, name): return getattr(self.inner, name) It is also possible to have a manager that expands the kinds of keys that it accept, as long as they are compatible with what was received before, or one that doesn't check anything and just calls sage_hash, assuming that the contract is satisfied for all relevant keys. (This would avoid having to change all cached functions.) Regards, Erik Massop -- You received this message because you are subscribed to the Google Groups "sage-devel" group. To unsubscribe from this group and stop receiving emails from it, send an email to sage-devel+unsubscr...@googlegroups.com. To post to this group, send email to sage-devel@googlegroups.com. Visit this group at http://groups.google.com/group/sage-devel. For more options, visit https://groups.google.com/d/optout.