大文字と小文字を区別しない「中」


150

表現が大好き

if 'MICHAEL89' in USERNAMES:
    ...

どこUSERNAMESリストです。


大文字と小文字を区別せずにアイテムを一致させる方法はありますか、またはカスタムメソッドを使用する必要がありますか?これのために追加のコードを書く必要があるかどうか疑問に思っています。

回答:


178
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]
フレドリー

44
[...]リスト全体を作成します。(name.upper() for name in USERNAMES)ジェネレータと必要な文字列を一度に1つだけ作成します。この操作を頻繁に行うと、メモリを大幅に節約できます。(毎回チェックするために再利用する小文字のユーザー名のリストを単に作成した場合、さらに節約できます)
viraptor

2
パフォーマンス上の理由から、dictを構築するときはすべてのキーを下げることをお勧めします。
ライアン

1
[リストのxのx.lower()]がリスト内包である場合、(USERNAMESの名前の(name.upper())はタプル内包ですか?それとも別の名前ですか?
オトカン

1
@otocanジェネレータ式です。
nmichaels 2018

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:必要に応じて動作するはずです(右側がリスト、dict、セットのいずれであるか)。(文字列を含めるために同様の結果を得るには、より多くの労力が必要になる場合があります。一部のケースでは警告を回避するunicodeなど)。


3
{'Michael89':True}:print "found"内のCaseInsensitively( 'MICHAEL89')の場合、dictは機能しません
Xavier Combelle

2
ザビエル:CaseInsensitively('MICHAEL89') in {CaseInsensitively('Michael89'):True}それが機能するためには、おそらく「必要に応じて振る舞う」には該当しないでしょう。
Gabe

それを行うには明らかな方法が1つしかないため、これで終わりです。これは、頻繁に使用しない限り重く感じます。とはいえ、それは非常にスムーズです。
nmichaels

2
@Nathon、侵略的にコンテナを変更する必要があることは「重いと感じる」操作だと私には思えます。完全に非侵襲的なラッパー:これより「どれだけ軽量」か。あまりない;-)。@Xavier、大文字と小文字が混在するキー/アイテムを含むディクショナリまたはセットであるRHSには、独自の非侵襲的なラッパーがetc.必要です(私の回答の短い部分と「より多くの努力が必要」部分)。
Alex Martelli、2010

ヘビーの私の定義は、一度だけ使用されるものを作るためにかなりのコードを書くことを含みます。これが複数回使用される場合は、それは完全に賢明です。
nmichaels

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

これが1つの方法です。

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

これが機能するには、string1string2オブジェクトの両方がタイプである必要がありstringます。


5
AttributeError: 'list' object has no attribute 'lower'
Jeff

@Jeffは、要素の1つがリストであり、両方のオブジェクトが文字列である必要があるためです。どのオブジェクトがリストですか?
ユーザー

1
私はあなたに賛成票を投じますが、あなたの答えを編集しない限り私はできません。あなたは、絶対に正しい。
ジェフ

@ジェフ私は明確化を追加しました。
ユーザー

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(間違った)セント

"" .join(['A'])。lower()の 'a'

更新

痛い、完全に@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')

時間的にはテストしていません。それがどれほど速くて効率的かはわかりません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.