noname1014
noname1014

Reputation: 101

Python Iterating New Data into nested dictionary

I have been working on a Python Role-Playing game and I have a function to import item data from a text file. The text file is structured as follows:

WEAPON 3 sword_of_eventual_obsolescence 6 10 2 0 10
WEAPON 4 dagger_of_bluntness 2 5 3 1 0
WEAPON 5 sword_of_extreme_flimsiness 3 8 3 7 0

The data importing goes like this:

def items_get():
    import os
    global items
    items = {
        "weapon":{},
        "armour":{},
        "potion":{},
        "misc":{}
    }
    file_dir = ( os.getcwd() + '\Code\items.txt' )

    file_in = open( file_dir, 'r')
    for each_line in file_in:
        line = file_in.readline()
        line = line.split(' ')
        if line[0] == "WEAPON":
            weapon_id = line[1]
            name = line[2]
            attack_min = line[3]
            attack_max = line[4]
            range = line[5]
            weight = line[6]
            value = line[7]
            weapon_data = {
                "name": name.replace('_', ' '),
                "atk_min": attack_min,
                "atk_max": attack_max,
                "rng": range,
                "wt": weight,
                "val": value,
            }
            items["weapon"][weapon_id] = {}
            items["weapon"][weapon_id].update(weapon_data)

However, when I print items["weapon"], I get this:

{'4': {'wt': '1', 'atk_min': '2', 'atk_max': '5', 'val': '0', 'name': 'dagger of bluntness', 'rng': '3'}}

As you can see, there is only 1 item there. On other occasions I have had two even though I actually have 3 items listed. Why is this happening, and how do I get all 3 items in the dictionary?

Thanks! :P

EDIT: Here is the data for the potions, in case you were wondering.

 elif line.split()[0] == "POTION":
        _, id, name, hp_bonus, atk_bonus, range_bonus, ac_bonus, str_bonus, con_bonus, dex_bonus, int_bonus, wis_bonus, cha_bonus, wt, val = line.split()

A healing potion looks like this in the file:

POTION 1 potion_of_healing 20 0 0 0 0 0 0 0 0 0 0.1 2

Upvotes: 0

Views: 58

Answers (2)

hygull
hygull

Reputation: 8740

@noname1014, I know you know this but I want to point out few of the problems with your code (that may occur in some special cases, e.g if you change your file name items.txt to new_items.txt, rare_fruits.txt etc.) and some suggestions.

Do not use \ as path separators in Windows. Use \\ otherwise you may get into problems. \Code\time_items.txt will be evaluated as \Code imeitems.txt because \t is TAB here.

Using \ only works in few cases if \ followed by any character A, p, n, t, ", ' etc. does not construct escape sequences like \n, \t, \f, \r, \b etc.

Have a look at the below example for clarification.

>>> import os
>>>
>>> print(os.getcwd() + '\Code\timeitems.txt')
E:\Users\Rishikesh\Python3\Practice\Code        imeitems.txt
>>>
>>> print(os.getcwd() + '\Code\\timeitems.txt')
E:\Users\Rishikesh\Python3\Practice\Code\timeitems.txt
>>>
>>> print(os.getcwd() + '\Code\newitems.txt')
E:\Users\Rishikesh\Python3\Practice\Code
ewitems.txt
>>>
>>> print(os.getcwd() + '\\Code\\newitems.txt')
E:\Users\Rishikesh\Python3\Practice\Code\newitems.txt
>>>
>>> # Do not use it as it may work only in some cases if \ followed by any character does not construct escape sequences.
...
>>> os.getcwd() + '\Code\items.txt'
'E:\\Users\\Rishikesh\\Python3\\Practice\\Code\\items.txt'
>>>
>>> # Use \\ as path separators
...
>>> os.getcwd() + '\\Code\\items.txt'
'E:\\Users\\Rishikesh\\Python3\\Practice\\Code\\items.txt'
>>>
>>> print(os.getcwd() + '\Code\items.txt')
E:\Users\Rishikesh\Python3\Practice\Code\items.txt
>>>
>>> print(os.getcwd() + '\\Code\\items.txt')
E:\Users\Rishikesh\Python3\Practice\Code\items.txt
>>>

If your dictionay is huge and you are facing any issue while looking into its items, pretty it using json module, it has a function called dumps() which is used to pretty print list and dictionary objects.

It is ok to place import statements inside function but placing it on the top is a Pythonic way (https://www.python.org/dev/peps/pep-0008/#imports). It is good for large applications with multiple functions in the same module.

Use with statement for opening files, in this case you do not need to close files.

Your code is fine, I have just modified it as below.

import os
global items
import json

def items_get():
    items = {
        "weapon":{},
        "armour":{},
        "potion":{},
        "misc":{}
    }

    # Do not use \ as path separators in Windows. Use \\ (\t, \n, \' have speacial meanings)
    file_dir = ( os.getcwd() + '\\Code\\items.txt' )

    with open( file_dir, 'r') as file_in:
        lines = file_in.readlines();
        # ['WEAPON 3 sword_of_eventual_obsolescence 6 10 2 0 10\n', 'WEAPON 4 dagger_of_bluntness 2 5 3 1 0\n', 'WEAPON 5 sword_of_extreme_flimsiness 3 8 3 7 0']

        for each_line in lines:
            # Use strip() to remove any leading/trailing whitespaces (\n, \t, spaces etc.)
            line = each_line.strip().split(' ');

            if line[0] == "WEAPON":
                weapon_id = line[1]
                name = line[2]
                attack_min = line[3]
                attack_max = line[4]
                range = line[5]
                weight = line[6]
                value = line[7]

                weapon_data = {
                    "name": name.replace('_', ' '),
                    "atk_min": attack_min,
                    "atk_max": attack_max,
                    "rng": range,
                    "wt": weight,
                    "val": value,
                }
                items["weapon"][weapon_id] = {}
                items["weapon"][weapon_id].update(weapon_data)
    return items 

# Calling items_get() to get dictionary
items = items_get();
# Pretty printing dictionary using json.dumps()
print(json.dumps(items, indent=4))

» Output

{
    "weapon": {
        "3": {
            "name": "sword of eventual obsolescence",
            "atk_min": "6",
            "atk_max": "10",
            "rng": "2",
            "wt": "0",
            "val": "10"
        },
        "4": {
            "name": "dagger of bluntness",
            "atk_min": "2",
            "atk_max": "5",
            "rng": "3",
            "wt": "1",
            "val": "0"
        },
        "5": {
            "name": "sword of extreme flimsiness",
            "atk_min": "3",
            "atk_max": "8",
            "rng": "3",
            "wt": "7",
            "val": "0"
        }
    },
    "armour": {},
    "potion": {},
    "misc": {}
}

Upvotes: 0

user4698348
user4698348

Reputation:

for each_line in file_in:
    line = file_in.readline()

each_line already contains the next line, because iterating through a file-like object (say, with a for loop) causes it to go by lines.

On each iteration of the loop, the file pointer is advanced by one line (file-like objects, though rewindable, keep track of their last-accessed position), and then before anything is done it gets advanced once more by the readline(), so the only line that doesn't get skipped entirely is the middle one (4).

To fix this, use the loop variable (each_line) within the loop body directly and nix the file_in.readline().

Upvotes: 2

Related Questions