Словарь без учета регистра



Я хотел бы, чтобы мой словарь был нечувствительным к регистру.



у меня есть этот код:



text = "practice changing the color"

words = {'color': 'colour',
'practice': 'practise'}

def replace(words,text):

keys = words.keys()

for i in keys:
text= text.replace(i ,words[i])
return text

text = replace(words,text)

print text


выход = практика изменения цвета



Я хотел бы другую строку,"practice changing the Color", (где Color начинается с прописной) также дать тот же результат.



Я считаю, что есть общий способ конвертировать в нижний регистр с помощью
mydictionary[key.lower()] но я не уверен, как наилучшим образом интегрировать это в мой существующий код. (Если это будет разумный, простой подход в любом случае.)

357   8  

8 ответов:

Если я правильно понимаю вас, и вы хотите, чтобы способ ключевых словарей в не чувствительном к регистру моде, один из способов было бы подкласс dict и перегрузить сеттер / геттер:

class CaseInsensitiveDict(dict):
    def __setitem__(self, key, value):
        super(CaseInsensitiveDict, self).__setitem__(key.lower(), value)

    def __getitem__(self, key):
        return super(CaseInsensitiveDict, self).__getitem__(key.lower())

The в настоящее время утвержден ответ не работает большое случаев, поэтому его нельзя использовать в качестве drop-in dict замена. Некоторые хитрые моменты в получении правильного dict замена:

  • перегрузка всех методов, которые включают ключи
  • правильная обработка нестроковых ключей
  • правильная обработка конструктора класса

следующее должно работать много лучше:

class CaseInsensitiveDict(dict):
    @classmethod
    def _k(cls, key):
        return key.lower() if isinstance(key, basestring) else key

    def __init__(self, *args, **kwargs):
        super(CaseInsensitiveDict, self).__init__(*args, **kwargs)
        self._convert_keys()
    def __getitem__(self, key):
        return super(CaseInsensitiveDict, self).__getitem__(self.__class__._k(key))
    def __setitem__(self, key, value):
        super(CaseInsensitiveDict, self).__setitem__(self.__class__._k(key), value)
    def __delitem__(self, key):
        return super(CaseInsensitiveDict, self).__delitem__(self.__class__._k(key))
    def __contains__(self, key):
        return super(CaseInsensitiveDict, self).__contains__(self.__class__._k(key))
    def has_key(self, key):
        return super(CaseInsensitiveDict, self).has_key(self.__class__._k(key))
    def pop(self, key, *args, **kwargs):
        return super(CaseInsensitiveDict, self).pop(self.__class__._k(key), *args, **kwargs)
    def get(self, key, *args, **kwargs):
        return super(CaseInsensitiveDict, self).get(self.__class__._k(key), *args, **kwargs)
    def setdefault(self, key, *args, **kwargs):
        return super(CaseInsensitiveDict, self).setdefault(self.__class__._k(key), *args, **kwargs)
    def update(self, E={}, **F):
        super(CaseInsensitiveDict, self).update(self.__class__(E))
        super(CaseInsensitiveDict, self).update(self.__class__(**F))
    def _convert_keys(self):
        for k in list(self.keys()):
            v = super(CaseInsensitiveDict, self).pop(k)
            self.__setitem__(k, v)

просто для протокола. Я нашел удивительный impementation на запросы:

https://github.com/kennethreitz/requests/blob/v1.2.3/requests/structures.py#L37

не могли бы вы рассмотреть возможность использования string.lower() на ваших входах и с помощью полностью строчного словаря? Это немного хаки решение, но оно работает

в моем конкретном случае мне нужен был поиск без учета регистра, однако я не хотел изменять исходный случай ключа. Например:

>>> d = {}
>>> d['MyConfig'] = 'value'
>>> d['myconfig'] = 'new_value'
>>> d
{'MyConfig': 'new_value'}

вы можете видеть, что словарь по-прежнему имеет исходный ключ, однако он доступен без учета регистра. Вот простое решение:

class CaseInsensitiveKey(object):
    def __init__(self, key):
        self.key = key
    def __hash__(self):
        return hash(self.key.lower())
    def __eq__(self, other):
        return self.key.lower() == other.key.lower()
    def __str__(self):
        return self.key

переопределения __hash__ и __eq__ требуются как для получения, так и для установки записей в словаре. Это создает ключи, которые хэшируются в одну и ту же позицию в словарь, если они нечувствительны к регистру равны.

Теперь либо создать пользовательский словарь, который инициализирует CaseInsensitiveKey с помощью предоставленного ключа:

class CaseInsensitiveDict(dict):
    def __setitem__(self, key, value):
        key = CaseInsensitiveKey(key)
        super(CaseInsensitiveDict, self).__setitem__(key, value)
    def __getitem__(self, key):
        key = CaseInsensitiveKey(key)
        return super(CaseInsensitiveDict, self).__getitem__(key)

или просто убедитесь, что всегда передавать экземпляр CaseInsensitiveKey в качестве ключа при использовании словаря.

хотя словарь без учета регистра является решением, и есть ответы на то, как этого достичь, в этом случае есть, возможно, более простой способ. Достаточно поиска без учета регистра:

import re

text = "Practice changing the Color"
words = {'color': 'colour', 'practice': 'practise'}

def replace(words,text):
        keys = words.keys()
        for i in keys:
                exp = re.compile(i, re.I)
                text = re.sub(exp, words[i], text)
        return text

text = replace(words,text)
print text

Я изменил простое, но хорошее решение от pleasemorebacon (спасибо!) что делает его немного более компактным, автономным и с небольшими обновлениями, чтобы позволить строительство от {'a':1, 'B':2} и протокол. Наконец, так как CaseInsensitiveDict.Key ожидается строка (Что еще может быть чувствительным к регистру или нет), это хорошая идея, чтобы получить Key класс str, то можно, например, на свалку CaseInsensitiveDict С json.dumps из коробка.

# caseinsensitivedict.py
class CaseInsensitiveDict(dict):

    class Key(str):
        def __init__(self, key):
            str.__init__(key)
        def __hash__(self):
            return hash(self.lower())
        def __eq__(self, other):
            return self.lower() == other.lower()

    def __init__(self, data=None):
        super(CaseInsensitiveDict, self).__init__()
        if data is None:
            data = {}
        for key, val in data.items():
            self[key] = val
    def __contains__(self, key):
        key = self.Key(key)
        return super(CaseInsensitiveDict, self).__contains__(key)
    def __setitem__(self, key, value):
        key = self.Key(key)
        super(CaseInsensitiveDict, self).__setitem__(key, value)
    def __getitem__(self, key):
        key = self.Key(key)
        return super(CaseInsensitiveDict, self).__getitem__(key)

вот базовый тестовый скрипт для тех, кто любит проверять вещи в действии:

# test_CaseInsensitiveDict.py
import json
import unittest
from caseinsensitivedict import *

class Key(unittest.TestCase):
    def setUp(self):
        self.Key = CaseInsensitiveDict.Key
        self.lower = self.Key('a')
        self.upper = self.Key('A')

    def test_eq(self):
        self.assertEqual(self.lower, self.upper)

    def test_hash(self):
        self.assertEqual(hash(self.lower), hash(self.upper))

    def test_str(self):
        self.assertEqual(str(self.lower), 'a')
        self.assertEqual(str(self.upper), 'A')

class Dict(unittest.TestCase):
    def setUp(self):
        self.Dict = CaseInsensitiveDict
        self.d1 = self.Dict()
        self.d2 = self.Dict()
        self.d1['a'] = 1
        self.d1['B'] = 2
        self.d2['A'] = 1
        self.d2['b'] = 2

    def test_contains(self):
        self.assertIn('B', self.d1)
        d = self.Dict({'a':1, 'B':2})
        self.assertIn('b', d)

    def test_init(self):
        d = self.Dict()
        self.assertFalse(d)
        d = self.Dict({'a':1, 'B':2})
        self.assertTrue(d)

    def test_items(self):
        self.assertDictEqual(self.d1, self.d2)
        self.assertEqual(
            [v for v in self.d1.items()],
            [v for v in self.d2.items()])

    def test_json_dumps(self):
        s = json.dumps(self.d1)
        self.assertIn('a', s)
        self.assertIn('B', s)

    def test_keys(self):
        self.assertEqual(self.d1.keys(), self.d2.keys())

    def test_values(self):
        self.assertEqual(
            [v for v in self.d1.values()],
            [v for v in self.d2.values()])

Я просто создал функцию для обработки этого:

def setLCdict(d, k, v):
    k = k.lower()
    d[k] = v
    return d

myDict = {}

так вместо

myDict['A'] = 1
myDict['B'] = 2

вы можете:

myDict = setLCdict(myDict, 'A', 1)
myDict = setLCdict(myDict, 'B', 2)

затем вы можете либо в нижнем регистре значение, прежде чем искать его или написать функцию для этого.

    def lookupLCdict(d, k):
        k = k.lower()
        return d[k]

    myVal = lookupLCdict(myDict, 'a')

вероятно, не идеально, если вы хотите сделать это глобально, но хорошо работает, если его просто подмножество вы хотите использовать его.

Comments

    Ничего не найдено.