5

I'm trying to reduce copy/paste in my code and have stumbled upon this problem. I've googled for the answer but all answers use an instance of a class as the key, I can't find anything on using a class definition itself as the key (I don't know if it's possible).

My code is this:

# All chunkFuncs keys are class definitions, all values are functions
chunkFuncs = {Math_EXP : Math_EXPChunk, Assignment : AssignmentChunk, Function : FunctionChunk}

def Chunker(chunk, localScope):
    for chunkType in chunkFuncs:
        if isinstance(chunk,chunkType):
            # The next line is where the error is raised
            localScope = chunkFuncs[chunk](chunk,localScope)
            return localScope

and the error is this

TypeError: unhashable type: 'Assignment'

Here are the class definitions:

class Math_EXP(pyPeg.List):
    grammar = [Number,Symbol],pyPeg.maybe_some(Math_OP,[Number,Symbol])

class Assignment(pyPeg.List):
    grammar = Symbol,'=',[Math_EXP,Number]

class Function(pyPeg.List):
    grammar = Symbol,'(',pyPeg.optional(pyPeg.csl([Symbol,Number])),')'

Are there any alternative methods I could use to get the same effect?

Thanks.

Trent
  • 471
  • 7
  • 14
  • As long as the classes are hashable it will work. Did you try it? What happened? – SethMMorton Oct 08 '13 at 04:20
  • @SethMMorton I updated the question to show the error and the code i'm using, sorry for the premature post. – Trent Oct 08 '13 at 04:22
  • You should check out this post: http://stackoverflow.com/questions/7152497/making-a-python-user-defined-class-sortable-hashable. Check out the second half of the second answer. Your class can't be used as a dictionary key if it is not hashable. – SethMMorton Oct 08 '13 at 04:24
  • 3
    Where did `pyPeg.List` come from? There's something peculiar going on. Perhaps `pyPeg` is an overly fancy ;-) framework playing games with metaclasses - you have to work hard to make class objects unhashable. – Tim Peters Oct 08 '13 at 04:28
  • I see where you're coming from @TimPeters but if hashing succeeds on Math_EXP then fails on the more simple looking Assignment, could it be the '=' character that is causing it to fail ? – Trent Oct 08 '13 at 04:29
  • No, but test it! Temporarily remove the `=` and try again. I bet it still fails. – Tim Peters Oct 08 '13 at 04:31
  • @TimPeters you were correct, it still fails. I don't want to define hash for each class though, could I use the class definition's memory location as the hash/key? – Trent Oct 08 '13 at 04:33
  • 2
    BTW, you didn't show a traceback: which line, exactly, triggers the `TypeError`? And, yes, you can use `id(some_class_object)` as a dict key. – Tim Peters Oct 08 '13 at 04:36
  • What is the result of `hash(pyPeg.List)`? – Steven Rumbalski Oct 08 '13 at 04:38
  • @TimPeters I updated my first code sample to indicate that, thanks. – Trent Oct 08 '13 at 04:40
  • @StevenRumbalski hash(pyPeg.List) == -9223372036851473243 – Trent Oct 08 '13 at 04:41
  • The error `unhashable type: Assignment` suggests that it's trying to hash an instance of Assignment. Please show the full traceback. (How are you calling `Chunker`? If you call it with an Assignment instance as `chunk`, it could raise that error when it tries to look up `chunkFuncs[chunk]`.) – BrenBarn Oct 08 '13 at 04:43
  • Looks like `chunk` that is passed in is an _instance_ of `Assignment`. Should it be `chunkFuncs[chunkType]`? – John La Rooy Oct 08 '13 at 04:46

2 Answers2

6

OK, the comments are getting out of hand ;-)

It seems certain now that the class object isn't the problem. If it were, the error would have triggered on the first line, when the dict was first constructed:

chunkFuncs = {Math_EXP : Math_EXPChunk, Assignment : AssignmentChunk, Function : FunctionChunk}

If you try to construct a dict with an unhashable key, the dict creation fails at once:

>>> {[]: 3}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

But you got beyond that line, and Assignment is a key in the dict you constructed. So the error is in this line:

        localScope = chunkFuncs[chunk](chunk,localScope)

Best guess is that it's an instance of Assignment that's unhashable:

>>> class mylist(list):
...   pass
...
>>> hash(mylist)
2582159
>>> hash(mylist())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'mylist'

See? mylist is hashable, but the instance mylist() is not.

Later: best guess is that you're not going to be able to worm around this. Why? Because of the name of the base class, pyPeg.List. If it's mutable like a Python list, then instances won't be hashable - and shouldn't be (mutable objects are always dangerous as dict keys). You could still index a dict by id(the_instance), but whether that's semantically correct is something I can't guess without knowing a lot more about your code.

Tim Peters
  • 55,793
  • 10
  • 105
  • 118
  • You were correct, the solution I found after reading through your reply was to use the type found the the previous isinstance() call as the dict key instead of the instance of the type itself. Simple mistake but all of the other complexity made it slip by. Thanks ! – Trent Oct 08 '13 at 14:18
2

You should be able to, yes, but you might need an extra type call:

>>> class X:
...     pass
...
>>> class_map = {X: 5}
>>> my_x = X()
>>> class_map[type(my_x)]
5
icktoofay
  • 117,602
  • 18
  • 233
  • 223