【问题标题】:Case insensitive 'in'不区分大小写的 'in'
【发布时间】:2010-09-02 13:56:30
【问题描述】:

我喜欢使用表达方式

if 'MICHAEL89' in USERNAMES:
    ...

USERNAMES 是一个列表。


有什么方法可以匹配不区分大小写的项目,还是我需要使用自定义方法?只是想知道是否需要为此编写额外的代码。

【问题讨论】:

    标签: python string list case-insensitive


    【解决方案1】:
    username = 'MICHAEL89'
    if username.upper() in (name.upper() for name in USERNAMES):
        ...
    

    或者:

    if username.upper() in map(str.upper, USERNAMES):
        ...
    

    或者,是的,您可以制作自定义方法。

    【讨论】:

    • if 'CaseFudge'.lower() in [x.lower() for x in list]
    • [...] 创建整个列表。 (name.upper() for name in USERNAMES) 一次只会创建一个生成器和一个需要的字符串 - 如果您经常执行此操作,则会节省大量内存。 (如果您只是创建一个小写用户名列表,您可以重复使用这些用户名列表来每次检查)
    • 出于性能原因,在构建 dict 时倾向于降低所有键。
    • 如果 [x.lower() for x in list] 是一个列表推导, (name.upper() for name in USERNAMES) 是一个元组推导吗?还是有别的名字?
    • @otocan 这是一个生成器表达式。
    【解决方案2】:

    str.casefold 推荐用于不区分大小写的字符串匹配。 @nmichaels's solution 可以轻松适应。

    使用任一:

    if 'MICHAEL89'.casefold() in (name.casefold() for name in USERNAMES):
    

    或者:

    if 'MICHAEL89'.casefold() in map(str.casefold, USERNAMES):
    

    根据docs

    大小写折叠类似于小写,但更具侵略性,因为它 旨在删除字符串中的所有大小写区别。例如, 德语小写字母“ß”相当于“ss”。因为它是 已经小写了,lower() 不会对 'ß' 做任何事情; casefold() 将其转换为“ss”。

    【讨论】:

      【解决方案3】:

      我会制作一个包装器,这样您就可以无创。至少,例如...:

      class CaseInsensitively(object):
          def __init__(self, s):
              self.__s = s.lower()
          def __hash__(self):
              return hash(self.__s)
          def __eq__(self, other):
              # ensure proper comparison between instances of this class
              try:
                 other = other.__s
              except (TypeError, AttributeError):
                try:
                   other = other.lower()
                except:
                   pass
              return self.__s == other
      

      现在,if CaseInsensitively('MICHAEL89') in whatever: 应该按要求运行(无论右侧是列表、字典还是集合)。 (可能需要付出更多努力才能获得类似的字符串包含结果,避免在某些涉及unicode 的情况下出现警告等)。

      【讨论】:

      • 这对 dict 不起作用 try if CaseInsensitively('MICHAEL89') in {'Michael89':True}:print "found"
      • Xavier:你需要CaseInsensitively('MICHAEL89') in {CaseInsensitively('Michael89'):True} 才能工作,这可能不属于“按要求行事”。
      • 只有一种明显的方法可以做到这一点。这感觉很重,除非它会被大量使用。也就是说,它非常流畅。
      • @Nathon,在我看来,不得不侵入性地改变容器是“感觉很重”的操作。一个完全非侵入性的包装器:一个人能得到比这更“轻”多少?!不多;-)。 @Xavier,具有混合大小写键/项目的字典或集合的 RHS 需要自己的非侵入性包装器(短 etc. 的一部分和我回答的“需要更多努力”部分;-)。
      • 我对沉重的定义涉及编写大量代码来制作只能使用一次的东西,而一个不太健壮但更短的版本可以做到这一点。如果这将被多次使用,这是非常明智的。
      【解决方案4】:

      通常(至少在 oop 中)您将对象塑造成您想要的行为方式。 name in USERNAMES 不区分大小写,所以USERNAMES 需要更改:

      class NameList(object):
          def __init__(self, names):
              self.names = names
      
          def __contains__(self, name): # implements `in`
              return name.lower() in (n.lower() for n in self.names)
      
          def add(self, name):
              self.names.append(name)
      
      # now this works
      usernames = NameList(USERNAMES)
      print someone in usernames
      

      这样做的好处是它为许多改进开辟了道路,而无需更改类外的任何代码。例如,您可以将self.names 更改为一组以加快查找速度,或者只计算一次(n.lower() for n in self.names) 并将其存储在类中等等...

      【讨论】:

        【解决方案5】:

        这是一种方法:

        if string1.lower() in string2.lower(): 
            ...
        

        为此,string1string2 对象都必须是 string 类型。

        【讨论】:

        • AttributeError: 'list' 对象没有属性 'lower'
        • @Jeff 那是因为你的元素之一是一个列表,两个对象都应该是一个字符串。哪个对象是列表?
        • 我会投票给你,但除非你编辑你的答案,否则我不能。你是绝对正确的。
        • @Jeff 我添加了说明。
        【解决方案6】:

        我认为您必须编写一些额外的代码。例如:

        if 'MICHAEL89' in map(lambda name: name.upper(), USERNAMES):
           ...
        

        在这种情况下,我们正在形成一个新列表,其中 USERNAMES 中的所有条目都转换为大写,然后与这个新列表进行比较。

        更新

        正如@viraptor 所说,使用生成器而不是map 会更好。见@Nathonanswer

        【讨论】:

        • 或者你可以使用itertools函数imap。它比生成器快得多,但可以实现相同的目标。
        【解决方案7】:

        你可以的

        matcher = re.compile('MICHAEL89', re.IGNORECASE)
        filter(matcher.match, USERNAMES) 
        

        更新:玩了一会儿,我认为您可以使用更好的短路类型方法

        matcher = re.compile('MICHAEL89', re.IGNORECASE)
        if any( ifilter( matcher.match, USERNAMES ) ):
            #your code here
        

        ifilter 函数来自 itertools,这是我在 Python 中最喜欢的模块之一。它比生成器快,但只在调用时创建列表的下一项。

        【讨论】:

        • 补充一点,模式可能需要转义,因为它可能包含诸如“.”、“?”之类的字符,这在正则表达式模式中具有特殊含义。使用 re.escape(raw_string) 来做到这一点
        【解决方案8】:

        要将它放在一行中,这就是我所做的:

        if any(([True if 'MICHAEL89' in username.upper() else False for username in USERNAMES])):
            print('username exists in list')
        

        不过,我没有按时间进行测试。我不确定它的速度/效率有多高。

        【讨论】:

          【解决方案9】:

          我的 5(错误)美分

          'a' in "".join(['A']).lower()

          更新

          哎呀,完全同意@jpp,我将作为不良做法的一个例子:(

          【讨论】:

          • 这是错误的。考虑'a' in "".join(['AB']).lower() 返回True,而这不是 OP 想要的。
          【解决方案10】:

          我需要这个作为字典而不是列表,Jochen 解决方案对于这种情况是最优雅的,所以我对其进行了一些修改:

          class CaseInsensitiveDict(dict):
              ''' requests special dicts are case insensitive when using the in operator,
               this implements a similar behaviour'''
              def __contains__(self, name): # implements `in`
                  return name.casefold() in (n.casefold() for n in self.keys())
          

          现在您可以像 USERNAMESDICT = CaseInsensitiveDict(USERNAMESDICT) 这样转换字典并使用 if 'MICHAEL89' in USERNAMESDICT:

          【讨论】:

            猜你喜欢
            • 2012-04-21
            • 2012-11-01
            • 2012-12-01
            • 2013-03-06
            • 2020-02-18
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多