beautifulsoup4==4.12.3 で動作確認しています。
まとめ
bs4.element.Tag オブジェクト x に対し、 x[attr_name] は「x の属性 attr_name の値」、y in x は「HTML 要素 y が x 直下にあるか」を意味する ([] と in で対象が異なる)。
スニペット
from bs4 import BeautifulSoup
text = '<div id="hoge"><p>Apple</p>Banana</div>'
soup = BeautifulSoup(text, 'html.parser')
div = soup.find('div')
p = soup.find('p')
# 1
assert div['id'] == 'hoge'
# 2
assert 'id' not in div
# 3
assert 'id' in div.attrs
assert type(div.attrs) is dict
# 4
assert p in div
assert 'Apple' not in div
assert 'Banana' in div
assert type(div.contents) is list
-
bs4.element.Tagオブジェクトは角括弧で属性にアクセスできる。 - じゃあ
in演算子で属性の有無を判定できるかというとできない。 - 属性の有無を判定するには
.attrsメンバにin演算子を適用する (.attrsメンバからそのタグの属性の辞書にアクセスできる)。 -
bs4.element.Tagオブジェクトに直接in演算子を適用したときに判定できるのは直下の要素の有無である (in演算子は直下の要素のリスト.contentsに対するinとして実装されているため)。