強火で進め

このブログではプログラム関連の記事を中心に書いてます。

Pythonの islower() と isupper() の判定処理のワナ

islower() と isupper() の関数ですがリファレンスをざっと確認したり、他の言語の経験のある方は英文字の小文字、大文字を判定する関数だと認識がある人も多いのではないかと思います(自分もそうでした)。

しかし、注意してください。ほぼその認識で大丈夫なのですが一部の場面ではワナが発動します。
※正確には正しい挙動を理解していないのが問題なのかもしれませんが他言語やってた多くの人がこの落とし穴にまりますって orz。ドキュメントに注記入れといてー。

実例を紹介します。 islower() は主に以下のような場合に使用するかと思います。

# -*- coding: utf-8 -*-

if "abcABC".islower():
	print u"すべて小文字です"
else:
	print u"すべて小文字ではありません"

こちらには大文字の ABC が存在するためもちろん判定は False となり「すべて小文字ではありません」と表示されます。

同様に以下のパターンの場合の判定結果にはみなさん納得できるかと思います。

文字列 判定結果
abcABC False
a True
A False
1 False
文字列なし("")のとき False
空白(" ")のとき False

しかし、以下の判定結果はPythonに不慣れな方は違和感を感じるのではないでしょうか。

文字列 判定結果
a1 True
aA1 False
a1b2c3 True
a b(途中に空白) True
a/b True

1 を判定したときはもちろん False となるのですが a1 と英文字の小文字と混在した文字列の場合は True となるのです。

同様に isupper() もこちらの動作は納得できるかと思います。

文字列 判定結果
A True
a False
文字列なし("")のとき False
空白(" ")のとき False

しかし、こちらの判定には違和感を感じる方もいるのでは

文字列 判定結果
A1 True
aA1 False
A1B2C3 True
A B(途中に空白) True
A/B True

islower() 関数のときと同様に A1 という場合は True を返すのです。

なおこちらの仕様ですがはじめはWindows版のPythonのバグの可能性はないのかと思い、他の環境でもテストしてみましたが同様の結果でした。こちらの判定で正しい仕様のようです。

ちなみに確認した環境とPythonのバージョン情報は以下となります。

Windows Vista

Python 2.5.2 (r252:60911, Feb 21 2008, 13:11:45) [MSC v.1310 32 bit (Intel)] on
win32

FreeBSD(さくらインターネットの環境)

Python 2.4.5 (#2, May 22 2008, 13:38:36)
[GCC 3.4.4 [FreeBSD] 20050518] on freebsd6

Mac X(Leopard)

Python 2.5.2 (r252:60911, Feb 22 2008, 07:57:53) 
[GCC 4.0.1 (Apple Computer, Inc. build 5363)] on darwin

まとめ

islower()

判定 条件
True 小文字のみの文字列
True 小文字+大文字小文字の区別の無い文字の文字列
False 大文字小文字の区別の無い文字のみの文字列
False 大文字が含まれる文字列

isupper()

判定 条件
True 大文字のみの文字列
True 大文字+大文字小文字の区別の無い文字の文字列
False 大文字小文字の区別の無い文字のみの文字列
False 小文字が含まれる文字列

他言語と同様の判定を行いたい場合は以下の様に処理する必要があります。

# -*- coding: utf-8 -*-

flag = True
s = "abc1def"
for i in s:
	if not i.islower():
		flag = False
		break

if flag:
	print "'" + s + u"'は小文字のみの文字列です"
else:
	print "'" + s + u"'は小文字のみの文字列ではありません"

2008/06/24追記:

id:c-yanさんのコメントによりもっと短いコードで処理できることが判明したので短いバージョンのサンプルを追加します(c-yanさんありがとうございます)。

# -*- coding: utf-8 -*-

s = "abc1def"
if all(i.islower() for i in s):
	print "'" + s + u"'は小文字のみの文字列です"
else:
	print "'" + s + u"'は小文字のみの文字列ではありません"

ちなみに help(all) すると以下の様に出力されます。

Help on built-in function all in module __builtin__:

all(...)
    all(iterable) -> bool

    Return True if bool(x) is True for all values x in the iterable.

ヘルプの内容を確認すると
イテレータの全てのデータ(今回の場合は i.islower() )が True の場合のみ、Trueを返し、それ以外は False を返すようです。