不区分大小写


151

我喜欢使用表达

if 'MICHAEL89' in USERNAMES:
    ...

USERNAMES清单在哪里。


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

Answers:


179
username = 'MICHAEL89'
if username.upper() in (name.upper() for name in USERNAMES):
    ...

或者:

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

或者,可以的,您可以定制方法。


8
if 'CaseFudge'.lower() in [x.lower() for x in list]
fredley 2010年

44
[...]创建整个列表。(name.upper() for name in USERNAMES)一次只能创建一个生成器和一个所需的字符串-如果您经常执行此操作,则可以节省大量内存。(如果您只是创建一个小写的用户名列表,然后每次都重复使用以进行检查,则可以节省更多费用)
viraptor 2010年

2
出于性能方面的考虑,在构建字典时,最好降低所有键。
瑞安

1
如果[列表中x的x.lower()]是列表推导,那么(USERNAMES中name的name.upper())是元组推导吗?还是有别的名字?
otocan '18

1
@otocan这是一个生成器表达式。
nmichaels '18

21

我会做一个包装纸,这样您就可以做到无创。至少,例如:

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等等)。


3
对于dict而言不起作用,请尝试是否在{'Michael89':True}:print“ found”中使用CaseInsensitively('MICHAEL89')
Xavier Combelle 2010年

2
Xavier:您需要CaseInsensitively('MICHAEL89') in {CaseInsensitively('Michael89'):True}使其正常工作,而这可能不属于“按要求进行”。
加布(Gabe)2010年

仅有一种明显的方法可以做到这一点。除非经常使用,否则感觉很沉重。就是说,这非常顺利。
nmichaels's

2
@Nathon,在我看来,不得不以侵入方式更改容器是“沉重的”操作。一个完全非侵入性的包装器:比这个包装器“轻”多少?不多;-)。@Xavier是带有大小写混合的键/项的字典或集合的RHS,需要使用它们自己的非侵入式包装器(这etc.是我的回答的简短和“需要更多的努力”部分;-)。
亚历克斯·马丁里 Alex Martelli)2010年

我对重载的定义涉及编写相当多的代码以制作只能使用一次的东西,而健壮性却要短得多的版本可以使用。如果要多次使用它,那将是非常明智的。
nmichaels 2010年

12

通常(至少在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)仅计算一次并将其存储在类中,依此类推...


10

str.casefold建议使用不区分大小写的字符串匹配。@nmichaels的解决方案可以轻松调整。

使用以下任一方法:

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

要么:

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

根据文档

大小写折叠类似于小写字母,但是更具攻击性,因为它旨在消除字符串中的所有大小写区别。例如,德语小写字母“ß”等效于“ ss”。由于它已经是小写字母,lower()因此对“ß”无效。casefold() 将其转换为“ ss”。


8

这是一种方法:

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

为此,string1string2对象都必须是type string


5
AttributeError:“ list”对象没有属性“ lower”
Jeff

@Jeff是因为您的元素之一是列表,并且两个对象都应该是字符串。列表是哪个对象?
用户

1
我会投票给您,但除非您编辑答案,否则我不会。你是绝对正确的。
杰夫

@Jeff我添加了说明。
用户

6

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

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

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

更新资料

@viraptor所说,最好使用生成器而不是map。参见@Nathon答案


或者您可以使用itertoolsfunction imap。它比生成器快得多,但可以实现相同的目标。
麦迪

5

你可以做

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)做到这一点
Iching Chang

0

我的5分(错误)

“”中的'a'.join(['A'])。lower()

更新

uch,完全同意@jpp,我将举一个不良做法的例子:(


2
错了 如果这不是OP想要的,请考虑'a' in "".join(['AB']).lower()收益True
jpp

0

我需要此字典而不是列表,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:


0

为了做到这一点,这就是我所做的:

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

我没有及时测试它。我不确定它的速度/效率。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.