Python組み込み関数6 -bool・callable・hasattr編-
Pythonおさらい第6段
制約として公式ドキュメントのみ参照*1として、読む癖をつける訓練も兼ねる。
今回はbool関数
, callable関数
, hasattr関数
の3種類について。
isinstance
, issubclass
もbool
を返すという点では似ているが、次回に回す。
読み込むドキュメントはこちら[bool, callable, hasattr]
構成
今回の関数は以下のようになっている。
bool([x])
callable(object)
hasattr(object, name)
ドキュメントによると、それぞれ以下の項目を調べ、bool型
を返す。
関数 | 引数 | 判定する内容 |
---|---|---|
bool | __bool__() を持つクラスおよびそのインスタンス |
クラス定義の真偽値 |
callable | オブジェクト | オブジェクトが呼び出し可能かどうか |
hasattr | オブジェクト, 名前 | オブジェクトがその名前の属性を持つかどうか |
主にif文
などで使用し、例外を投げたり処理を分岐させる際に活用する関数とみて間違いないだろう。
hasattr(x)
は以下のようなコードとほぼ等価らしい。
def hasattr(object, name): try: getattr(object, name) return True except AttributeError: return False
使ってみる
使ってみる-bool
int型
の-28
,-1
,0
,1
,28
をそれぞれ判定してみるstr型
の' '
,''
,'\n'
,'\t'
,'Otsuhachi'
をそれぞれ判定してみる- 自作クラス
Person
を定義して判定してみる*2
実行するコードは以下の通り。
class Person: def __init__(self, name: str, age: int): self.name = name self.age = age def __repr__(self) -> str: me = f'{self.name}({self.age})' return me def __bool__(self) -> bool: return self.age >= 20 def check(data: list): if not data: msg = f'引数を1つ以上与える必要があります。' raise ValueError(msg) if len(set(type(x) for x in data)) != 1: msg = 'dataは同じ型で統一されている必要があります' raise TypeError(msg) t = type(data[0]).__name__ print(t) print('-' * 30) for d in data: print(f'"{d}": {bool(d)}') data = [ [-28, -1, 0, 1, 28], [' ', '', '\n', '\t', 'Otsuhachi'], [Person('Otsuhachi', 28), Person('Yamaki', 19)], ] for d in data: check(d) print()
int ------------------------------ "-28": True "-1": True "0": False "1": True "28": True str ------------------------------ " ": True "": False " ": True " ": True "Otsuhachi": True Person ------------------------------ "Otsuhachi(28)": True "Yamaki(19)": False
int型
は0
がFalse
でそれ以外はTrue
を返すようだ。
str型
は空文字がFalse
でそれ以外はエスケープシーケンス
であっても空白
であってもTrue
を返すようだ。
自作クラスPerson
では未成年をFalse
とし、成人をTrue
とする定義通りの結果が返っていることがわかる。
使ってみる-callable
int
,0
,str
,''
をそれぞれ判定してみる- 自作クラス
Person
のクラス、インスタンスをそれぞれ判定してみる Person
を継承し、__call__()
を定義したPerson2
のクラス、インスタンスをそれぞれ判定してみる
実行するコードは以下の通り。
class Person: def __init__(self, name: str, age: int): self.name = name self.age = age def __repr__(self) -> str: me = f'{self.name}({self.age})' return me class Person2(Person): def __call__(self, other): text = f'{repr(self)}: {"{}"}' if isinstance(other, Person): text = text.format(f'こんにちは、{other.name}') else: text = text.format(f'{type(other).__name__}型の{other}か') print(text) def check(data: list): if not data: msg = f'引数を1つ以上与える必要があります。' raise ValueError(msg) for d in data: print(f'{d}<{type(d).__name__}>: {callable(d)}') p1 = Person('Otsuhachi', 28) p2 = Person2('Yamaki', 19) data = [int, 0, str, '', Person, p1, Person2, p2] check(data)
<class 'int'><type>: True 0<int>: False <class 'str'><type>: True <str>: False <class '__main__.Person'><type>: True Otsuhachi(28)<Person>: False <class '__main__.Person2'><type>: True Yamaki(19)<Person2>: True
結果をまとめると以下の通りになっている。
オブジェクトの種類 | 結果 |
---|---|
クラスオブジェクト | True |
インスタンス(__call__() あり) |
True |
インスタンス(__call__() なし) |
False |
callable(x)
がTrue
を返すようなx
は、x(*args, **kwargs)
という式を実行可能であるということがわかる。
使い方-hasattr
- 自作クラス
Person
のクラス、インスタンスに対し、以下の属性名を確認するname
age
birthday
gender
実行するコードは以下の通り。
class Person: name: str age: int = 0 def __init__(self, name: str, age: int, birthday: str): self.name = name self.age = age self.birthday = birthday def __repr__(self) -> str: me = f'{self.name}({self.age})' return me p = Person('Otsuhachi', 28, '1995/06/04') t = Person.__name__ for attr in ('name', 'age', 'birthday', 'gender'): print(f'{attr}') print('-' * 30) print(f'{t}: {hasattr(Person,attr)}') print(f'{p}: {hasattr(p,attr)}') print()
name ------------------------------ Person: False Otsuhachi(28): True age ------------------------------ Person: True Otsuhachi(28): True birthday ------------------------------ Person: False Otsuhachi(28): True gender ------------------------------ Person: False Otsuhachi(28): False
Person
に対し、hasattr
した結果、型ヒントだけ与えた属性name
はFalse
で、代入を終えた属性age
はTrue
を返した。
また、Person
から生成したインスタンスではname
, age
に加え、クラス属性では触れていないbirthday
に関してもTrue
を返すようになった。
このことから、__init__()
内のself.birthday = birthday
の時点で、属性が追加されているのがわかる。
当然、クラス定義段階でもインスタンス生成段階でも触れなかった属性gender
については常にFalse
が返った。
個人的にはこう書きたい
Pythonのバージョンが進み、:=
演算子が使えるようになった今、
hasattr
で属性を確認したあと、その属性に用があるのなら以下のように書き換えてもいいかもしれない。
書き換え前
name = 'otsuhachi' attr_name = 'capitalize' if hasattr(name, attr_name): aft_name = getattr(name, attr_name)() print(name, '->', aft_name)
書き換え後
name = 'otsuhachi' attr_name = 'capitalize' if (attr := getattr(name, attr_name, None)) is not None: aft_name = attr() print(name, '->', aft_name)
結果はどちらもotsuhachi -> Otsuhachi
となる。
もちろん、特定の属性を持つか持たないかがわかればいいだけなら、hasattr
を使うべきだ。
締め
今回、真偽値を判定する関数3種類を紹介した。
引数のチェックが必要だったり、特定の属性を持つクラスのみの挙動を設定したりと使いどころは多いはずなので、なるべく早く慣れてしまいたい。