How to raise error if duplicates keys in dictionar

2019-02-16 19:11发布

I try to raise an error if the user enter a duplicate key in a dictionary. The dictionary is in a file and the user can edit the file manually.

Example:

dico= {'root':{
                'a':{'some_key':'value',...},
                'b':{'some_key':'value',...},
                'c':{'some_key':'value',...},
                ...

                'a':{'some_key':'value',...},
              }
      }

the new key 'a' already exist...

How can I test dico and warn the user when I load dico from the file?

5条回答
老娘就宠你
2楼-- · 2019-02-16 19:26

Python's default behavior is to silently overwrite duplicates when declaring a dictionary.

You could create your own dictionary class that would check whether an item was already in a dictionary before adding new elements and then use this. But then you would have to change your declaration of dico in that file to something that allows duplicates, like a list of tuples for example.

Then on loading that data file, you'd parse it into your special 'subclassed' dict.

查看更多
一夜七次
3楼-- · 2019-02-16 19:26

If you want to ensure that an error is raised during dict construction with duplicate keys, just leverage Python's native keyword argument checking:

> dict(a={}, a={})
SyntaxError: keyword argument repeated

Unless I'm missing something, there is no need to subclass dict.

查看更多
何必那么认真
4楼-- · 2019-02-16 19:30

You will need to have custom dict which can reject with ValueError if the key is already present.

class RejectingDict(dict):
    def __setitem__(self, k, v):
        if k in self.keys():
            raise ValueError("Key is already present")
        else:
            return super(RejectingDict, self).__setitem__(k, v)

Here is how it works.

>>> obj = RejectingDict()
>>> obj[1] = True
>>> obj[2] = False
>>> obj
{1: True, 2: False}
>>> obj[1] = False
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "rejectingdict.py", line 4, in __setitem__
    raise ValueError("Key is already present")
ValueError: Key is already present
查看更多
太酷不给撩
5楼-- · 2019-02-16 19:36

Write a subclass of dict, override __setitem__ such that it throws an error when replacing an existing key; rewrite the file to use your new subclass's constructor instead of the default dict built-ins.

import collections

class Dict(dict):
    def __init__(self, inp=None):
        if isinstance(inp,dict):
            super(Dict,self).__init__(inp)
        else:
            super(Dict,self).__init__()
            if isinstance(inp, (collections.Mapping, collections.Iterable)): 
                si = self.__setitem__
                for k,v in inp:
                    si(k,v)

    def __setitem__(self, k, v):
        try:
            self.__getitem__(k)
            raise ValueError("duplicate key '{0}' found".format(k))
        except KeyError:
            super(Dict,self).__setitem__(k,v)

then your file will have to be written as

dico = Dict(
    ('root', Dict(
        ('a', Dict(
            ('some_key', 'value'),
            ('another_key', 'another_value')
        ),
        ('b', Dict(
            ('some_key', 'value')
        ),
        ('c', Dict(
            ('some_key', 'value'),
            ('another_key', 'another_value')
        ),

        ....
    )
)

using tuples instead of dicts for the file import (written using the {} notation, it would use the default dict constructor, and the duplicates would disappear before the Dict constructor ever gets them!).

查看更多
放我归山
6楼-- · 2019-02-16 19:36

WRONG WAY
GO BACK

from x import dico is not a very good idea -- you are letting USERS edit code, which you then execute blindly. You run the risk of simple typos causing a syntax error, up to malicious stuff like import os; os.system("rm whatever"); dico = {}.

Don't faff about with subclassing dict. Write your own dict-of-dicts loader. It's not that hard ... read the data file, check before each insertion whether the key already exists; if it does, log an error message with meaningful stuff like the line number and the duplicate key and its value. At the end, if there have been any errors, raise an exception. You may find that there's an existing module to do all that ... the Python supplied ConfigParser aka configparser doesn't seem to be what you want.

By the way, isn't having a single 'root' key at the top level rather pointless?

查看更多
登录 后发表回答