英語613639 views
高校物理160203 views
りんご209490 views
MathPython497343 views
いろは3010814 views
高校化学2924392 views
Computer368106 views
数学講師2886227 views
LaTeX962153 views
中学数学623755 views
Help
Tools
NewsSpreadsheetCalendarBookkeepingSlidesTier ListPen ToolIllustrationCrayonWatercolorPixel ArtASCII ArtPerspectiveEndless StairsGraphMind MapER DiagramFamily TreeMemeCurved TextImage EditorMosaicRetro FilterPencil SketchSwirl EffectOCR/HighlighterMakeup EditorFaviconVideo TrimmerScrolling VideoVideo TitleColor PickerColor ExtractorBonfireFireworksWater RippleWater SplashBreaking GlassWood GrainMarble TextureCSS ButtonIcon MakerBar ChartGrouped Bar ChartStacked Bar ChartPie ChartLine ChartArea ChartStacked Area ChartScatter Plot3D Bar Chart3D Pie ChartBar Chart RaceBubble ChartPopulation PyramidPictogramEarningsCandlestick ChartInvestment RiskMortgage SimulatorCalculatorMatrix CalculatorFunction GraphPolynomial ExpansionVenn DiagramField VisualizerRubik's Cube Group TheoryTraveling SalesmanVoronoi and DelaunayFractalColumn ArithmeticDraw Math FiguresArithmetic AnimationArithmetic Word ProblemsCounting with Tree DiagramsCube NetsRolling DiceCross SectionsMotion PathMechanicsWavesElectromagnetic WavesLight and LensesThermodynamicsHow Semiconductors WorkMolecular StructuresAtomic OrbitalsElectrochemical CellsChemical EquilibriumCrystal LatticesBuffer pHPeriodic TableComplex IonsDNA Double HelixCell DivisionMembrane ChannelsNerve ImpulseMuscle ContractionHormones and HomeostasisRock ClassificationWeatherConstellationsSolar and Lunar Eclipses3D ModelingFloor PlanSeismic StructuresIntersection TurnMaglevCooking AnimationOrigamiLive Viewer CountGeoJSON MapRailway MapPopulation MapCrime MapLand Price MapSchool MapShrine and Castle MapHouse of Representatives MapWord MapSolitaireReversiHakoiri MusumeChessHamburgerRippleSlide Puzzle MakerNeon PinballNovel MakerJapanese Typing PracticePiano Score EditorMusic TheoryShogi StrategyPiano Rhythm Game

English

__bool__ と __len__ による真偽値カスタマイズ(Python)

Python でオブジェクトを if 文や bool() で評価するとき、そのオブジェクトが「真」か「偽」かを決めるのが __bool____len__ という特殊メソッドだ。これらをカスタマイズすることで、自作クラスのインスタンスがどのように真偽判定されるかを制御できる。

真偽値判定のデフォルト動作

Python のオブジェクトは、特に何も定義しなければ常に True と判定される。

class MyClass:
    pass

obj = MyClass()
print(bool(obj))  # True

if obj:
    print("obj is truthy")  # 出力される

これは「存在するオブジェクトは真」という直感に沿った動作だ。しかし、空のコンテナや無効な状態を表すオブジェクトでは False を返したいことがある。

__bool__ メソッド

__bool__ を定義すると、真偽値判定を直接カスタマイズできる。

class Result:
    def __init__(self, success, data=None):
        self.success = success
        self.data = data
    
    def __bool__(self):
        return self.success

# 使用例
success_result = Result(True, {"id": 1})
failure_result = Result(False)

print(bool(success_result))  # True
print(bool(failure_result))  # False

if success_result:
    print("Success!")  # 出力される

if not failure_result:
    print("Failed!")   # 出力される

__bool__ は必ず True または False を返さなければならない。それ以外を返すと TypeError になる。

class BadBool:
    def __bool__(self):
        return 1  # int を返している

obj = BadBool()
bool(obj)  # TypeError: __bool__ should return bool, returned int

__len__ メソッドによる暗黙の真偽値

__bool__ が定義されていない場合、Python は __len__ を探す。__len__ が 0 を返せば False、それ以外なら True と判定される。

class Playlist:
    def __init__(self, songs=None):
        self.songs = songs or []
    
    def __len__(self):
        return len(self.songs)
    
    # __bool__ は定義していない

# 使用例
empty_playlist = Playlist()
full_playlist = Playlist(["song1", "song2"])

print(bool(empty_playlist))  # False(len が 0)
print(bool(full_playlist))   # True(len が 2)

if full_playlist:
    print(f"Playing {len(full_playlist)} songs")

これにより、コンテナ的なオブジェクトは「空なら False、要素があれば True」という自然な振る舞いを得られる。

__bool____len__ の優先順位

両方定義されている場合、__bool__ が優先される。

class Container:
    def __init__(self, items):
        self.items = items
        self.active = True
    
    def __len__(self):
        print("__len__ called")
        return len(self.items)
    
    def __bool__(self):
        print("__bool__ called")
        return self.active

c = Container([1, 2, 3])
c.active = False

print(bool(c))
# 出力:
# __bool__ called
# False

# __len__ は呼ばれない

これは意図的な設計だ。__bool__ を定義したなら、それがオブジェクトの真偽を決める責任を持つ。

実践的な使用例

いくつかの実践的なパターンを見てみよう。

# 例1: 検証結果クラス
class ValidationResult:
    def __init__(self):
        self.errors = []
    
    def add_error(self, message):
        self.errors.append(message)
    
    def __bool__(self):
        return len(self.errors) == 0
    
    def __len__(self):
        return len(self.errors)

result = ValidationResult()
if result:
    print("Validation passed")

result.add_error("Name is required")
if not result:
    print(f"Validation failed with {len(result)} errors")
# 例2: Optional 風のラッパー
class Maybe:
    def __init__(self, value=None):
        self._value = value
        self._has_value = value is not None
    
    def __bool__(self):
        return self._has_value
    
    @property
    def value(self):
        if not self._has_value:
            raise ValueError("No value present")
        return self._value

some = Maybe(42)
none = Maybe()

if some:
    print(f"Value: {some.value}")  # Value: 42

if not none:
    print("No value")  # No value
# 例3: 接続状態を表すクラス
class Connection:
    def __init__(self, host):
        self.host = host
        self.connected = False
    
    def connect(self):
        self.connected = True
    
    def disconnect(self):
        self.connected = False
    
    def __bool__(self):
        return self.connected

conn = Connection("localhost")
if not conn:
    print("Not connected, connecting...")
    conn.connect()

if conn:
    print("Ready to use")

注意点:__len__ が負の値を返す場合

__len__ は非負の整数を返すことが期待される。負の値を返すと ValueError になる。

class BadLength:
    def __len__(self):
        return -1

obj = BadLength()
len(obj)   # ValueError: __len__() should return >= 0
bool(obj)  # ValueError: __len__() should return >= 0

注意点:継承時の挙動

親クラスで __bool____len__ を定義していると、子クラスはそれを継承する。必要に応じてオーバーライドする。

class BaseContainer:
    def __init__(self):
        self.items = []
    
    def __len__(self):
        return len(self.items)

class SpecialContainer(BaseContainer):
    def __init__(self):
        super().__init__()
        self.enabled = True
    
    # __bool__ をオーバーライドして独自の判定
    def __bool__(self):
        return self.enabled and len(self.items) > 0

sc = SpecialContainer()
sc.items = [1, 2, 3]
print(bool(sc))  # True

sc.enabled = False
print(bool(sc))  # False(enabled が False)

まとめ

__bool____len__ を適切に実装することで、自作クラスのオブジェクトを if 文で自然に扱えるようになる。

__bool__ を使う場面

オブジェクトの有効/無効、成功/失敗、接続/切断など、長さとは関係ない状態を真偽値で表現したいとき。

__len__ に任せる場面

コンテナ的なオブジェクトで、「空なら False、要素があれば True」という挙動が自然なとき。

両方定義する場合は __bool__ が優先されることを忘れずに。意図した挙動になるよう、どちらを使うか(あるいは両方使うか)を設計時に決めておこう。