Otsuhachi’s diary

プログラミングのことや覚書などその他いろいろ

当ブログの内容を使用したり参考にした場合に生じた問題の責任は負いかねます。

Python組み込み関数7 -isinstance・issubclass編-

Pythonおさらい第7段
制約として公式ドキュメントのみ参照*1として、読む癖をつける訓練も兼ねる。

今回はisinstance関数issubclass関数の2種類について。
前回に引き続き、特定の条件を満たすかどうか判定し、boolを返す関数。

重要な関数ではあるものの、TrueFalseが返るだけなので実に地味!
とはいえ、よく使うのは間違いないのでしっかり押さえておきたい。

読み込むドキュメントはこちら[isinstance, issubclass]

構成

今回の関数は以下のようになっている。

isinstance(object, classinfo)

issubclass(class, classinfo)

classinfoとはなんぞや?
ドキュメントを読んでみると、型オブジェクト, 型オブジェクトのタプルあるいは再帰的に複数のタプルだそうだが……再帰的に複数のタプルとはなんぞや?
謎は深まるばかりであるが使ってみた段階でわかることに期待したい。

関数 内容
isinstance objectclassinfoインスタンスかどうか
issubclass classclassinfoサブクラスかどうか
クラスはそれ自身のサブクラスとみなされる

使ってみる

ひとまず、普通に単体のクラスで試してみて、classinfoについては専用の項目で見ていくこととする。
また、クラス定義などの共通部分については前提コードを使いまわす。

使ってみる-前提コード

from abc import ABC, abstractmethod


class Creature(ABC):
    def __init__(self):
        self.alive = True

    @abstractmethod
    def desperate(self):
        pass

    def death(self):
        self.desperate()
        self.alive = False


class Runner(ABC):
    def run(self):
        print(f'{self}は走った!')


class Person(Creature):
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age
        super().__init__()

    def __repr__(self) -> str:
        me = f'{self.name}({self.age})'
        return me


class Man(Person):
    def desperate(self):
        print(f'{self}: グワーッ!')


class Woman(Person):
    def desperate(self):
        print(f'{self}: キャーッ!')


class RunningMan(Runner, Man):
    pass


man = Man('Otsuhachi', 28)
rman = RunningMan('Taro', 55)
woman = Woman('Hanako', 20)
check_classes = [ABC, Creature, Person, Man, Woman, Runner, RunningMan]
member = [man, rman, woman]

どうしようもないクソコードが爆誕してしまった……。

クラスの繋がりは以下の通り。

ABC
  ├ Creature
  │      └ Person
  │          ├ Woman
  │          └ Man
  │             ├─ RunningMan
  └───────── Runner

使ってみる-isinstance

前提コードで定義したmembercheck_classesそれぞれの組み合わせでisinstanceを行い、その結果を出力する。

実行するコードは以下の通り。

# ここより上の行に前提コードがある
for m in member:
    print(f'{m}<{type(m).__name__}>')
    print('-' * 30)
    for cc in check_classes:
        print(f'{cc.__name__}: {isinstance(m,cc)}')
    print()
Otsuhachi(28)<Man>
------------------------------
ABC: True
Creature: True
Person: True
Man: True
Woman: False
Runner: False
RunningMan: False

Taro(55)<RunningMan>
------------------------------
ABC: True
Creature: True
Person: True
Man: True
Woman: False
Runner: True
RunningMan: True

Hanako(20)<Woman>
------------------------------
ABC: True
Creature: True
Person: True
Man: False
Woman: True
Runner: False
RunningMan: False

子クラスのインスタンスが親クラスをclassinfoとしてisinstanceしたときにはTrueが返っているのがわかる。
例えばmanABC->Creature->Person->Manという繋がりを持つ、Manクラスのインスタンスなので、classinfoABC, Creature, Person, ManのいずれでもTrueとなっている。
逆に同じ親クラスを持つWomanは直接継承関係にないのでFalseになるし、
Manクラスの子クラスであるRunningManFalseとなる。

objectのクラスが、どこかの段階でclassinfoクラスを継承しているかを調べる関数だということが理解できた。

使ってみる-issubclass

check_classes[i]check_classes[j]のサブクラスであるかを調べて出力する。
0 <= i,j < len(check_classes)

実行するコードは以下の通り。

# ここより上の行に前提コードがある
for subcls in check_classes:
    print(subcls.__name__)
    print('-' * 30)
    for cls in check_classes:
        print(f'{cls.__name__}: {issubclass(subcls,cls)}')
    print()
ABC
------------------------------
ABC: True
Creature: False
Person: False
Man: False
Woman: False
Runner: False
RunningMan: False

Creature
------------------------------
ABC: True
Creature: True
Person: False
Man: False
Woman: False
Runner: False
RunningMan: False

Person
------------------------------
ABC: True
Creature: True
Person: True
Man: False
Woman: False
Runner: False
RunningMan: False

Man
------------------------------
ABC: True
Creature: True
Person: True
Man: True
Woman: False
Runner: False
RunningMan: False

Woman
------------------------------
ABC: True
Creature: True
Person: True
Man: False
Woman: True
Runner: False
RunningMan: False

Runner
------------------------------
ABC: True
Creature: False
Person: False
Man: False
Woman: False
Runner: True
RunningMan: False

RunningMan
------------------------------
ABC: True
Creature: True
Person: True
Man: True
Woman: False
Runner: True
RunningMan: True

自身と、自身のクラスが継承しているクラスがclassinfoの場合Trueが返るのがわかる。

isinstanceと違うのは第一引数がclassな点くらいだろうか……。
メタプログラミングとかの際には必要になるのかもしれないが、今のところisinstanceほど魅力を感じない。

使ってみる-classinfo

2つの関数共通の第二引数であるclassinfoについて。
受け取る形式は以下の3通り。

  1. 型オブジェクト
  2. 型オブジェクトのタプル
  3. 再帰的に複数の型オブジェクトのタプル

1., 2.についてはわかりやすいのでさらっと以下のコードで確かめる。

# ここより上の行に前提コードがある
classinfo = [str, (str, Runner)]
for m in member:
    print(f'{m}<{type(m).__name__}>')
    print('-' * 30)
    for ci in classinfo:
        if type(ci) is tuple:
            c_name = tuple(map(lambda x: x.__name__, ci))
        else:
            c_name = ci.__name__
        print(f'{c_name}: {isinstance(m,ci)}')
    print()
Otsuhachi(28)<Man>
------------------------------
str: False
('str', 'Runner'): False

Taro(55)<RunningMan>
------------------------------
str: False
('str', 'Runner'): True

Hanako(20)<Woman>
------------------------------
str: False
('str', 'Runner'): False

型オブジェクトのタプルを渡した場合、いずれかのクラスがTrueを返せばTrueを返しているのがわかる。
以下のようなイメージすればわかりやすいだろう。

def tupled_isinstance(object, classinfo: tuple) -> bool:
    for ci in classinfo:
        if isinstance(object, ci):
            return True
    return False

問題なのは3.だが、再帰的に複数のタプルとはなんなのか?
確信はないが、入れ子構造のタプルであると思われる。
以下のコードで確認してみる。

# ここより上の行に前提コードがある
def convert_classnames(ct: tuple):
    res = []
    for c in ct:
        if type(c) is tuple:
            res.append(convert_classnames(c))
        else:
            res.append(c.__name__)
    return tuple(res)


classinfo = [(str, Man), (str, (Woman, (Runner, RunningMan)))]

for m in member:
    print(f'{m}<{type(m).__name__}>')
    print('-' * 60)
    for ci in classinfo:
        c_name = convert_classnames(ci)
        print(f'{c_name}: {isinstance(m,ci)}')
    print()
Otsuhachi(28)<Man>
------------------------------------------------------------
('str', 'Man'): True
('str', ('Woman', ('Runner', 'RunningMan'))): False

Taro(55)<RunningMan>
------------------------------------------------------------
('str', 'Man'): True
('str', ('Woman', ('Runner', 'RunningMan'))): True

Hanako(20)<Woman>
------------------------------------------------------------
('str', 'Man'): False
('str', ('Woman', ('Runner', 'RunningMan'))): True

これが正しい再帰的に複数のタプルであるかはわからないが、入れ子構造のタプルを渡した場合も問題なく判定していることは分かった。

締め

今回の2つの関数についてはクラスの継承がどんなものか、インスタンスとクラスオブジェクトの違いの2点がなんとなくでもわかっていれば、問題なく使うことができるだろう。

時々やらかすのが、classinfoインスタンスを指定してしまうことぐらいだろうか。
タプルの再帰に気を遣うぐらいならclassinfoインスタンスの時にtype(instance)してくれる方がよほどありがたく感じる。

タプルはともかく、再帰的なタプルは本当にわからん。

*1:わかりやすく解説している他サイトを見ない

Python組み込み関数6 -bool・callable・hasattr編-

Pythonおさらい第6段
制約として公式ドキュメントのみ参照*1として、読む癖をつける訓練も兼ねる。
今回はbool関数, callable関数, hasattr関数の3種類について。
isinstance, issubclassboolを返すという点では似ているが、次回に回す。

読み込むドキュメントはこちら[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

  1. int型-28, -1, 0, 1, 28をそれぞれ判定してみる
  2. str型' ', '', '\n', '\t', 'Otsuhachi'をそれぞれ判定してみる
  3. 自作クラス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型0Falseでそれ以外はTrueを返すようだ。
str型は空文字がFalseでそれ以外はエスケープシーケンスであっても空白であってもTrueを返すようだ。
自作クラスPersonでは未成年をFalseとし、成人をTrueとする定義通りの結果が返っていることがわかる。

使ってみる-callable

  1. int, 0, str, ''をそれぞれ判定してみる
  2. 自作クラスPersonのクラス、インスタンスをそれぞれ判定してみる
  3. 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

  1. 自作クラス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した結果、型ヒントだけ与えた属性nameFalseで、代入を終えた属性ageTrueを返した。

また、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種類を紹介した。

引数のチェックが必要だったり、特定の属性を持つクラスのみの挙動を設定したりと使いどころは多いはずなので、なるべく早く慣れてしまいたい。

*1:わかりやすく解説している他サイトを見ない

*2:all・any編で定義したものとほぼ同じ

Python組み込み関数5 -bin・hex・oct編-

Pythonおさらい第5段
制約として公式ドキュメントのみ参照*1として、読む癖をつける訓練も兼ねる。

今回はbin関数oct関数hex関数について。*2

それぞれ整数を2, 8, 16進数表現の文字列に変換する関数である。

今回読み込むドキュメントはこちら[bin, oct, hex]

構成

今回の関数は以下のようになっている。

bin(x)

oct(x)

hex(x)

ドキュメントによると、それぞれ以下のように変換されるらしい。
整数を先頭に特定の文字列をつけた上でn進数表現の文字列に変換する。
結果はPythonの式としても使える形式になる

関数 先頭の文字列 N進数
bin 0b 2
oct 0o 8
hex 0x 16

使ってみる

どういうわけかhexの説明にだけ*3、整数を返す__index__()を定義したら使えるとある。

  1. 正の整数でそれぞれ試す
  2. 負の整数でそれぞれ試す
  3. 自作クラスPersonで以下を試す
    1. __index__()を定義したIdxPersonを定義してそれぞれ試す
    2. __int__()を定義したIntPersonを定義してそれぞれ試す。

実行するコードは以下の通り。

class Person:
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

    def __repr__(self) -> str:
        return f'{self.name}({self.age})'


class IdxPerson(Person):
    def __index__(self):
        return id(self)


class IntPerson(Person):
    def __int__(self):
        return self.age


def show_boh(number):
    print(number)
    print('-' * 70)
    for name, f in zip(('bin', 'oct', 'hex'), (bin, oct, hex)):
        print(f'{name}: {f(number)}')


tests = [
    1930,
    -1930,
    IdxPerson('__index__', 28),
    IntPerson('__int__', 28),
]

for test in tests:
    try:
        show_boh(test)
    finally:
        print()
1930
----------------------------------------------------------------------
bin: 0b11110001010
oct: 0o3612
hex: 0x78a

-1930
----------------------------------------------------------------------
bin: -0b11110001010
oct: -0o3612
hex: -0x78a

__index__(28)
----------------------------------------------------------------------
bin: 0b101010111101010100000100010001111111010000
oct: 0o52752404217720
hex: 0x2af54111fd0

__int__(28)
----------------------------------------------------------------------

Traceback (most recent call last):
...
TypeError: 'IntPerson' object cannot be interpreted as an integer

ドキュメントではhexのみだったが、bin, octでも問題なく__index__()を定義することで使用できるようだ。
整数を返せればいいのかと、__int__()だけ定義したIntPersonではこれらを使用できないこともわかった。

続いてPythonの式として使用してみる。
文字列を返す関数を式に……というのがいまいちピンとこないが、ひとまず以下のコードを実行してみる。

print(bin(18) + bin(10))
0b100100b1010

この結果はシンプルに2つの文字列を連結しただけのようだ。
この時点で勘違いに気付く。

Pythonの式として使える形式というのは、文字が混ざっていても数値として解釈されるということだったらしい。

以下のコードを実行してみる。

print(0b10010 + 0b1010)
print(type(0b10010))
28
int

2進数表現で18+10の計算を行い、その結果を出力しているので、出力は28と正しい値を返した。
また、型名を調べる関数を使用した結果からもint型として扱われているのがわかった。

締め

今回は整数を2, 8, 16進数表現の文字列として取得する方法と、2, 8, 16進数表現でint型を扱う方法が理解できた。
前者はbin, oct, hex関数を使わずにformat関数や文字列に変数を埋め込む書式などでも代用可能らしいが、それらについてはformat関数で触れることにする。覚えていれば

*1:わかりやすく解説している他サイトを見ない

*2:ページタイトルと順番が違うのは、ページタイトルは50音順で表記するため

*3:日本語版ドキュメントでの話なので、英語版ならどれにも書いてあるかもしれない。

Python組み込み関数4 -ascii・repr・str編-

Pythonおさらい第4弾
制約として公式ドキュメントのみ参照*1として、読む癖をつける訓練も兼ねる。

今回はascii関数repr関数、さらにstr関数について。
知ってる関数で似通ったものはできる限りまとめていきたい。

普段使うのはreprstrで、asciiについてはよく知らない。

読み込むドキュメントはこちら[ascii, repr, str]

構成

今回の関数は以下のようになっている。

ascii(object)

repr(object)

str(object='')

str(object=b'', encoding='utf-8', errors='strict')

ascii

ドキュメントによると、asciireprと同様にオブジェクトの印字可能な表現を含む文字列を返すが、非ASCII文字はエスケープされるらしい。
Python2reprと等価。

repr

reprはオブジェクトの印字可能な表現を含む文字列を返し、その文字列は主に以下のようなものになるらしい。
また、自作クラスでは__repr__()を定義することでこの関数で返す文字列を制御することができる。*2

  1. eval関数に渡されたときと同じ値を持つようなオブジェクト表す文字列
  2. 山括弧に囲まれたオブジェクトの型の名前と追加の情報*3

str

strobjectのみを受け取るものとキーワード引数encoding, errorsを指定するものが存在しているらしい。
objectのみのものは、objectの文字列版を返すが、objectが空の場合は空文字が返る。
自作クラスの場合では__str__()を定義することでこの関数で返す文字列を制御することができる。*4
__str__()を持たないクラスはreprの結果と等価。

キーワード引数encoding, errorsの少なくとも一方が与えられた場合のstrについての説明は現時点ではよくわからないが、objectbyte-like objectである必要があり、bytes.decode(encoding, errors)と等価になるらしい。
今回のおさらいでは割愛。

使ってみる

以下の説明のそれぞれ試すとはascii, repr, strでの出力を試みることである。

  1. __repr__()__str__()も定義しない自作クラスPersonでそれぞれ試す
  2. Personを継承し、__repr__()を定義したRPersonでそれぞれ試す
  3. Personを継承し、__str__()を定義したSPersonでそれぞれ試す
  4. RPerson, SPersonを継承したRSPersonでそれぞれ試す

実行するコードは以下の通り。

from typing import Iterable


class Person:
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age


class RPerson(Person):
    def __repr__(self) -> str:
        me = f'{self.name}({self.age})'
        return me


class SPerson(Person):
    def __str__(self) -> str:
        me = f'{self.name}、{self.age}歳です!'
        return me


class RSPerson(RPerson, SPerson):
    pass


def show_person(persons: Iterable):
    for i, person in enumerate(persons):
        if i:
            print()
        print(type(person).__name__)
        print('-' * 55)
        print(f'repr: {repr(person)}')
        print(f'ascii: {ascii(person)}')
        print(f'str: {str(person)}')


me = {'name': 'Otsuhachi', 'age': 28}
persons = [p(**me) for p in (Person, RPerson, SPerson, RSPerson)]
show_person(persons)
Person
-------------------------------------------------------
repr: <__main__.Person object at 0x0000028225D32A60>
ascii: <__main__.Person object at 0x0000028225D32A60>
str: <__main__.Person object at 0x0000028225D32A60>

RPerson
-------------------------------------------------------
repr: Otsuhachi(28)
ascii: Otsuhachi(28)
str: Otsuhachi(28)

SPerson
-------------------------------------------------------
repr: <__main__.SPerson object at 0x0000028225D32820>
ascii: <__main__.SPerson object at 0x0000028225D32820>
str: Otsuhachi、28歳です!

RSPerson
-------------------------------------------------------
repr: Otsuhachi(28)
ascii: Otsuhachi(28)
str: Otsuhachi、28歳です!

表にまとめると以下のようになる。

関数 未定義 reprのみ strのみ 両方
repr <__main__.Person object at 0x0000028225D32A60> Otsuhachi(28) <__main__.Person object at 0x0000028225D32A60> Otsuhachi(28)
ascii <__main__.Person object at 0x0000028225D32A60> Otsuhachi(28) <__main__.Person object at 0x0000028225D32A60> Otsuhachi(28)
str <__main__.Person object at 0x0000028225D32A60> Otsuhachi(28) Otsuhachi、28歳です! Otsuhachi、28歳です!

__repr__()__str__()も定義していない場合に出力される文字列はすべてのクラスのスーパークラスであるobject__repr__()である。
__str__()が定義されていない場合、__repr__()の結果が返るが、その逆は発生しないことがわかる。
asciiについては残念ながらこの例では出力の差異を見ることができなかった。

締め

今回、str関数に2種類あることがわかったが、現状ではasciiのありがたい使い道についてはわからなかった。
__repr__()evel関数で等価なオブジェクトを生成できるものが好ましいという点も記憶の片隅にとどめておいた方がいいかもしれない。
eval関数はセキュリティリスクが面倒くさそうだからそもそもあまり使いたくないイメージ。

__str__()は人にやさしい出力を定義する慣習があるっぽい。
リストの中身が直接見れないからって__repr__()__str__()代わりに使ってる身としては耳が痛い。

*1:わかりやすく解説している他サイトを見ない

*2:第一弾で使用

*3:大抵の場合はオブジェクトの名前とアドレスを含む

*4:第一弾で使用

Python組み込み関数3 -all・any編-

Pythonおさらい第3段
制約として公式ドキュメントのみ参照*1として、読む癖をつける訓練も兼ねる。

今回はall関数any関数の2種類について。
ブログのレイアウト弄りをしていたとはいえ、日数が空いたうえでのこの薄さ……まずい。

allはイテラブルの中身がすべてTrueかどうか。
anyはイテラブルの中身のいずれかがTrueかどうか。
残念ながらこれ以上の情報はこの記事から得られないので、どう水増しするか気になる人以外は解散!お疲れ様でした!

読み込むドキュメントはこちら[(all), (any)]。

構成

今回の2つの関数は共通項が多いので同時に紹介していく。

all(iterable)

any(iterable)

ドキュメントによると、それぞれ以下のようなコードと等価です。

# all
def all(iterable):
  for element in iterable:
    if not element:
      return False
  return True

# any
def any(iterable):
  for element in iterable:
    if element:
      return True
  return False

使ってみる

  1. 自作クラスPersonを定義して、名簿memberを作成
  2. memberに対しall, anyをそれぞれ使用してその出力を見る

実行するコードは以下の通り。

class Person:
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

    def __bool__(self):
        print(f'Check {self.name}: {self.age}')
        return self.age >= 20


member = [
    Person('Otsuhachi', 28),
    Person('Taro', 18),
    Person('Jiro', 20),
    Person('Hanako', 14),
]

print('All')
print(all(member), '\n')
print('Filterd')
print(all(filter(lambda x: x.age >= 20, member)))
print('-' * 30)
print('Any')
print(any(member), '\n')
print('Filterd')
print(any(filter(lambda x: x.age < 20, member)))
All
Check Otsuhachi: 28
Check Taro: 18
False

Filterd
Check Otsuhachi: 28
Check Jiro: 20
True
------------------------------
Any
Check Otsuhachi: 28
True

Filterd
Check Taro: 18
Check Hanako: 14
False

今回、Personクラスでは成人をTrue、未成年をFalseと定義している。

allを使用した結果、TaroFalseを返した時点でFalseが返されているのがわかる。
つぎにfilter(lambda x: x.age >= 20, member)を使用して、allに渡すメンバーから未成年を除外すると、成人であるOtsuhachiJiroがチェックされ、Trueが返った。

anyを使用した結果、OtsuhachiTrueを返した時点でTrueが返されているのがわかる。
つぎにfilter(lambda x: x.age < 20, member)を使用してanyに渡すメンバーから成人を除外した結果、未成年であるTaroHanakoがチェックされ、Falseが返った。

締め

ネタとしては極めて薄い関数ながら、コードの短縮という意味ではなかなかにありがたい関数だと思われる。

*1:わかりやすく解説している他サイトを見ない

Index -プログラミング-

Python

組み込み関数

A

B

  • bin
  • bool
  • breakpoint
  • bytearray
  • bytes

C

D

  • delattr
  • dict
  • dir
  • divmod

E

  • enumerate
  • eval
  • exec

F

  • filter
  • float
  • format
  • frozenset

G

  • getattr
  • globals

H

I

L

  • len
  • list
  • locals

M

  • map
  • max
  • memoryview
  • min

N

  • next

O

P

R

  • range
  • repr
  • reversed
  • round

S

  • set
  • setattr
  • slice
  • sorted
  • staticmethod
  • str
  • sum
  • super

T

  • tuple
  • type

V

  • vars

Z

  • zip

Python組み込み関数2 -abs編-

Pythonおさらい第2弾
制約として公式ドキュメントのみ参照*1として、読む癖をつける訓練も兼ねる。

今回はabs関数について。
前回結構頑張ったので今回は緩く済ませられそうな関数をチョイス。
いわゆる絶対値を返す関数としてしか認識していないが実態はいかに……。

読み込むドキュメントはこちら

構成

abs関数の詳細はこんな感じ。

abs(x)

ドキュメントによると、数値の絶対値を返すとのこと。
int, float, complexに加えて__abs__()を実装しているオブジェクトに対応するそう。

以上!

認識が一切覆ることなく、でしょうねという感想しか出てこない。

使ってみる

  1. int型-10, 0, 10に対し使用
  2. float型-10.1, -0.1, 0.0, 0.1, 10.01に対し使用

実行するコードは以下の通り。

def show_abs(text, list_):
    print(text)
    print('-' * 30)
    for i in list_:
        print(f'abs({i}): {abs(i)}')


int_list = [-10, 0, 10]
float_list = [-10.1, -0.1, 0.0, 0.1, 10.01]
show_abs('int型', int_list)
print()
show_abs('float型', float_list)
int型
------------------------------
abs(-10): 10
abs(0): 0
abs(10): 10

float型
------------------------------
abs(-10.1): 10.1
abs(-0.1): 0.1
abs(0.0): 0.0
abs(0.1): 0.1
abs(10.01): 10.01

当然な結果。

自作クラス

さすがにあんまりなので自作クラスに__abs__()を定義して使ってみることにする。
complex複素数型らしいが、学がないのでそもそも複素数が何かわからんです。

実行するコードとその出力は以下の通り。

class Person:
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

    def __int__(self) -> int:
        return self.age

    def __str__(self) -> str:
        me = f'{self.name}({self.age})'
        return me

    def __abs__(self) -> int:
        return id(self)


me = Person('Otsuhachi', 28)
print(me, int(me), abs(me), sep='\n')
Otsuhachi(28)
28
2523203087376

abs(me)で返るのは今回実行時のmeのIDなので、プログラム終了までは一定で、実行毎に変動する値になる。
無事、絶対値を取得できる自作クラスを作成できた。

締め

今回は特別学ぶことはなかった印象。
__abs__()を実装すれば自作クラスでも動くとはいえ、現状ではあまり用途が思いつかない。
組み込み関数コンプリートのために書いた感が拭えない結果となった。

これからもif x < 0: x *= -1を省略するためにしか使わなそう。

*1:わかりやすく解説している他サイトを見ない