檢測字串是否包含任一字元集合中的字元,或是否包含所有字元,是常見驗證需求。本文對比三種實作模式的語意、效能與適用場景。
contain_any(seq, charset):seq 中是否包含 charset 中任一字元contain_all(seq, charset):seq 中是否包含 charset 中所有字元
def contain_any(seq, charset):
for c in seq:
if c in charset:
return True
return False
def contain_all(seq, charset):
for c in charset:
if c not in seq:
return False
return True
import itertools
def contain_any_itertools(seq, charset):
# ifilter(predicate, iterable) -> 傳回 predicate 為 True 的元素
# charset.__contains__ 即 lambda c: c in charset
for _ in itertools.filterfalse(charset.__contains__, seq):
return False # 找到不在 charset 的元素?不,邏輯需修正
# 正確寫法:任何元素在 charset 中
def contain_any_itertools(seq, charset):
return any(itertools.filterfalse(lambda c: c not in charset, seq))
# 更簡潔:any + generator
def contain_any_any(seq, charset):
return any(c in charset for c in seq)
any(gen) 內建短路,效能與手寫迴圈相近,語法更簡潔。
def contain_any_set(seq, charset):
return bool(set(seq) & set(charset)) # 交集非空
def contain_all_set(seq, charset):
return set(charset).issubset(seq) # charset ⊆ seq
# 或
return not set(charset) - set(seq) # 差集為空
import timeit, random, string
seq = ''.join(random.choices(string.ascii_letters, k=10000))
charset = set('aeiou')
print('loop:', timeit.timeit('contain_any(seq, charset)', globals=globals(), number=1000))
print('any:', timeit.timeit('contain_any_any(seq, charset)', globals=globals(), number=1000))
print('set:', timeit.timeit('contain_any_set(seq, charset)', globals=globals(), number=1000))
# 典型:set > any ≈ loop (小 charset 時 set 優勢明顯)
# 兩集合各自獨有的元素
L1 = [1,2,3,4]
L2 = [3,4,5,6]
print(set(L1).symmetric_difference(L2)) # {1, 2, 5, 6}
| 場景 | 推薦 | 理由 |
|---|---|---|
| 一般腳本、可讀性優先 | any(c in charset for c in seq) | Pythonic、短路、無額外記憶體 |
| 熱點路徑、大字串/字元集 | bool(set(seq) & charset_set) | C 層集合運算最快 |
| 需檢查「所有字元都在」 | charset_set.issubset(seq) | 語意最精確 |
| 序列非字串 (列表、元組、生成器) | any(x in charset for x in seq) | 通用性最強 |
| charset 固定、重複呼叫 | 預先建立 charset_set = set(charset) | 避免重複建集合 |
class CharSetChecker:
def __init__(self, charset):
self.charset = set(charset)
def any(self, seq):
return any(c in self.charset for c in seq)
def all(self, seq):
return self.charset.issubset(seq)
def none(self, seq):
return not self.any(seq)
# 使用
checker = CharSetChecker('aeiou')
print(checker.any('hello')) # True
print(checker.all('aei')) # False
print(checker.none('bcdfg')) # True
善用集合運算與 any/all 內建,寫出既快又清晰的字元檢測邏輯。