#1
  1. Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Dec 2003
    Location
    Houston, TX
    Posts
    161
    Rep Power
    11

    "TypeError: list objects are unhashable"


    I'm a new python programmer using Python 2.4 (#60, Nov 30 2004, 11:49:19) [MSC v.1310 32 bit (Intel)] on win32...IDLE

    I have a class which creates a linked list of non-player characters, called Rival. It generates random Japanese names and random stats and saves them as an instance of Rival, which is added to a linked list.

    I'm trying to create a function which randomly selects a province that one element of the list can move to. The available provinces are limited to the exits listed as part of the province he is in now.

    The function seems to work correctly if I use it only once per instance. But if I call the function again for the same element, I get a "list objects are unhashable" error. I can call the function once for each item in the element, but if it's called twice on one element I always get this error.

    The Rival class imports the "names" script which does the actual generation of Japanese names, and is used for determining the exits available from the new province selected. The error is from the names script.

    Here is the relevant code:
    Code:
     #Rival class function moveR(self, itemNum)
    
    def moveR(self,itemNum):
            """This function finds the element related to itemNum, and
    uses the name[4] (province exits) of that element to randomly
    choose one of the adjacent provinces. It then uses the
    names.exits(province) function to determine the new exits
    available to the new province. It then writes all of this data
    back to the element."""
    
            c=j[itemNum-1]
            returnProv=[]
            returnProv.append(choice(c.name[4]))
            returnProv.append(names.exits(returnProv[0]))
    
            print c.name[2], 'has moved from', c.name[3],
            print 'to',returnProv[0]
    
            c.name[3]=returnProv[0]
            c.name[4]=returnProv[1]
            #I suspect the problem may be here.
    
            c=None
            del c
    
    #names.py function exits(province)
    def exits(province):
        """This function searches in the province buffer for the exits
    related to the province given.
    It finds which line number the province is on in the list of
    Provinces. It then uses that number to access the full (province,
    exit, ..., exit) list of data, and returns that information."""
    
        count=0
        
        for line in provinceName:
            count=count+1
            found=re.search(province,line)
            if found:
                num=count
                rawProvinceData=provinceList[num-1]
                parsedProvince=formatProv(rawProvinceData)
                returnData=[parsedProvince[1:]]
    
        del found, count, rawProvinceData, parsedProvince, province, num
        return returnData
    
    #The error received when the moveR() function is called for the
    same element twice:
    #x is the name of the Rival instance. itemNum 1 is element 0.
    
    >>> x.moveR(1)
    Tomika Ryuzaburo has moved from Hida to Kaga
    #Kaga is the new province. The remaining provinces are the
    exits available as adjacent to Kaga.
    
    >>> x.moveR(1)
    
    Traceback (most recent call last):
      File "<pyshell#2>", line 1, in -toplevel-
        x.moveR(1)
      File "C:\Python24\work\rv.py", line 99, in moveR
        returnProv.append(names.exits(returnProv[0]))
      File "C:\Python24\work\names.py", line 63, in exits
        found=re.search(province,line)
      File "C:\Python24\lib\sre.py", line 134, in search
        return _compile(pattern, flags).search(string)
      File "C:\Python24\lib\sre.py", line 216, in _compile
        p = _cache.get(cachekey)
    TypeError: list objects are unhashable
    >>>
    Any help would be appreciated. Instruction related to better coding practices is preferred, since I'm teaching myself. If you can offer an underlying abstract analysis of the reason why this is happening, that would be best of all!
    Thank you.

    Attached is the full code if the snippets are not enough to determine the problem.
    -Tim
    Attached Files
    Avalokiteshvara Bodhisattva, when practicing deeply the Prajna Paramita, perceives that all five skhandas in their own being are empty and is saved from all suffering.
  2. #2
  3. No Profile Picture
    Contributing User
    Devshed Intermediate (1500 - 1999 posts)

    Join Date
    Feb 2004
    Location
    London, England
    Posts
    1,585
    Rep Power
    1373
    The exception is happening because the line
    found=re.search(province,line)
    is being called when province is a list instead of a string. This is happening because the function exits is returning a list with a single string in, instead of a string. Replace

    returnData=[parsedProvince[1:]]

    with
    returnData=parsedProvince[1:]

    and it should work (or at least fix that problem - there may well be others).

    You also asked for comments on coding style - here are some observations from my brief read through:

    1) you seem to be using a list to hold structured data in. It is much better to use a class for this, since it will make your code a lot clearer. e.g. instead of refering to name[4] you could use name.exit instead.

    2) In the exits function you do not initialise returnData outside of the loop. If the loop completes without finding what it is looking for then it will throw an exception because returnData will be undefined. Either initialise it to None at the start of the loop, or (preferably, IMHO) do not have a returnData variable at all, and have a return statement at the point in the loop where you find what you are looking for. i.e. replace
    returnData=parsedProvince[1:]
    with
    return parsedProvince[1:]

    3) You go out of your way to kill the variable c in the moveR function. This is unnecessary, since it is a local variable and will be deleted automatically when the function exits.

    Dave - The Developers' Coach
  4. #3
  5. No Profile Picture
    Contributing User
    Devshed Novice (500 - 999 posts)

    Join Date
    Nov 2003
    Posts
    624
    Rep Power
    35
    Code:
    def exits(province):
        count=0
        
        for line in provinceName:
            count=count+1
            found=re.search(province,line)
            if found:
                num=count
                rawProvinceData=provinceList[num-1]
                parsedProvince=formatProv(rawProvinceData)
                returnData=[parsedProvince[1:]]
    
        del found, count, rawProvinceData, parsedProvince, province, num
        return returnData
    You don't need to explicitly count here, if you need an index as well, you can use the enumerate() builtin to return each item with an index as well. Then there's no need for the num-1 either.

    Since you aren't using the return value from re.search(), merely checking that it exists, you don't need to store it, and ditto with rawProvinceData.

    Code:
    def exits(province):
        for count, line in enumerate(provinceName):
            if re.search(province, line):
                return formatProv(provinceList[count])[1:]
    If 'province' is just a string and not a regular expression (I can't tell, but I suspect it might be), this could become:

    Code:
            if province in line:
    3) You go out of your way to kill the variable c in the moveR function. This is unnecessary, since it is a local variable and will be deleted automatically when the function exits.
    Doesn't the same idea apply to all variables though -
    Once it's no longer needed, it will be cleared up automatically, so del is pretty much never needed unless you know you need it?
    Last edited by sfb; January 9th, 2005 at 02:17 PM.
  6. #4
  7. Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Dec 2003
    Location
    Houston, TX
    Posts
    161
    Rep Power
    11

    Error fixed. Where should I put a new class?


    Thank you both for your replies. I appreciate the critique of my messy code a lot.
    I managed to determine that there were multiple problems causing different errors.

    First, DevCoach was right about the reason for the unhashable error. When I analyzed specifically what the exits function was searching for the second time it was trying to match a list against a string, which doesn't make sense. It was doing this because I was loading information into c.name[4] as a list, not as a string.

    Next, I determined that some of the provinces in the raw list have exclamation points after them, which indicates that the province is bordered by water. But I forgot to strip these out when I was searching for the new exits.

    Also, some provinces had exits to provinces that didn't exist in the province list. This created a one way door from a searcing point of view, which created an endless loop in the exits function. I think you mentioned this too DevCoach.

    Code:
    #names function exits(provinceString)
    def exits(provinceString):
        """This function searches in the province buffer for the exits
    related to the province given.
    It finds which line number the province is on in the list of
    Provinces. It then uses that number to access the full (province,
    exit, ..., exit) list of data, and returns that information."""
        
        count=num=0
        rawProvinceData=''
    
        #strip ending !, if it exists
        screen=re.compile('!')
        found=screen.search(provinceString)
        if found:
            provinceString=provinceString[0:-1]
            
        #determine exits   
        while rawProvinceData=='':
            for line in provinceName:
                count=count+1
                if count>100:
                    print rawProvinceData,'! ',provinceString, line,'!! '
                if provinceString in line:
                    num=count
                    rawProvinceData=provinceList[num-1]
        return rawProvinceData
    
    #Rival function moveR(self, itemNum)
    def moveR(self,itemNum):
            """This function finds the element related to itemNum, and
    uses the name[4] (province exits) of that element to randomly
    choose one of the adjacent provinces. It then uses the
    names.exits(province) function to determine the new exits
    available to the new province. It then writes all of this data
    back to the element."""
            from random import choice
            
            rawProvData=names.exits(choice(j[itemNum-1].name[4]))
            parsedProvData=names.formatProv(rawProvData)
            exits=parsedProvData[1:]
    
            old=j[itemNum-1].name[3]
            temp=j[itemNum-1]
            j[itemNum-1].name=[temp.name[0],temp.name[1],temp.name[2],parsedProvData[0],exits]
            print j[itemNum-1].name[2], 'has moved from', old,
            print 'to',j[itemNum-1].name[3]
    This is the code I came up with to solve the problems, but I'm going to try to use your suggestions to clean it up even more.

    One thing that puzzles me is that in the moveR function, the choice() function was not working the second time around, which is why I explicitly import it at the beginning of the function now. Originally, I import it before the class definition. Why does it do this?

    Also, I'm completely unsure how to create, sustain, and modify global variables within a class. I would like to keep the itemNum total as a global variable.

    DevCoach, you mentioned making a class out of the linked list. Could you explain this further? I have a class that is supposed to handle everything related to the non-player characters, called rivals, in the game. It imports a class to handle random name generation and province logistics. What other class were you saying would make this easier/work better?
    Eventually, the main module of the game will import the Rival module, so it's important that the Rival class is performing things well because it will need to flawlessly pass the necessary data back to the main module.

    sfb, thank you for the info about the enumerate function. I didn't know what it did. I apprecaite the example code which is much shorter and less error-prone than mine as well.
    -Tim
    Avalokiteshvara Bodhisattva, when practicing deeply the Prajna Paramita, perceives that all five skhandas in their own being are empty and is saved from all suffering.

IMN logo majestic logo threadwatch logo seochat tools logo