σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft Academyロードマップ
Ctrl K
AO
+1.2k 人の学習者
ログイン参加する ↗
ロードマップ
AO
+1.2k 人の学習者
ログイン
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Python ロードマップ›モジュール 1 · クオンツのためのPython基礎›トピック 1.27·~40 分初級

特殊メソッド(__repr__、__eq__)

ロードマップ上の位置モジュール 1/11 · トピック 27/33
1.27 特殊メソッド(__repr__、__eq__)
—ラボスコア— / 100合計XP—連続日数—

自作オブジェクトを組み込み型のように振る舞わせる

ふつうのオブジェクトを表示すると <__main__.Position object at 0x7f3a> のようになり、Position("A", 1) == Position("A", 1) は False です。既定では、2つのオブジェクトは同じオブジェクトのときだけ等しいからです。特殊メソッド(アンダースコア2つで囲むので dunder メソッドとも呼ばれます)を使うと、クラスを Python の構文に組み込めます。__repr__ はオブジェクトの表示、__eq__ は ==、__add__ は +、__lt__ は < を制御します。直接呼ぶことはなく、repr(x)、a == b、a + b、sorted(items) を使うと Python が呼び出します。

class Plain:
    def __init__(self, cents):
        self.cents = cents

a, b = Plain(500), Plain(500)
print(a == b)                 # False: different objects
print(a.cents == b.cents)     # True
print("object at 0x" in str(a))   # True: the default display is unhelpful

__repr__:オブジェクトをわかりやすく表示する

__repr__ は、開発者向けにオブジェクトを説明する文字列を返します。慣習として、そのオブジェクトを作るコードのような形にします:Money(1999, 'USD')。リスト、デバッガー、エラーメッセージのすべてがこれを使うので、バグが格段に読みやすくなります。__str__ も定義しない限り、print(x) や str(x) も __repr__ を使います。文字列を表示するのではなく返す必要があり、f 文字列の {self.currency!r} は文字列属性を引用符付きの形にします。

class Money:
    def __init__(self, cents, currency="USD"):
        self.cents = cents
        self.currency = currency

    def __repr__(self):
        return f"Money({self.cents}, {self.currency!r})"

price = Money(1999)
print(price)                    # Money(1999, 'USD')
print([Money(5), Money(7, "EUR")])  # [Money(5, 'USD'), Money(7, 'EUR')]

__eq__ と __hash__:等しいとはどういうことか

__eq__(self, other) は self == other の結果を決めます。同一性を決めるフィールドを比較し、other が想定した型でないときは False ではなく特殊な値 NotImplemented を返します。すると Python は反対側を試し、最後に自分で False と答えるので、Money(1) == 5 も安全です。__eq__ を定義するとハッシュが無効になり、__hash__ も整合する形で定義しない限り(例:hash((self.cents, self.currency)))、集合の要素や辞書のキーには使えなくなります。

class Money:
    def __init__(self, cents, currency="USD"):
        self.cents = cents
        self.currency = currency

    def __eq__(self, other):
        if not isinstance(other, Money):
            return NotImplemented            # let Python decide
        return self.cents == other.cents and self.currency == other.currency

    def __hash__(self):
        return hash((self.cents, self.currency))

print(Money(500) == Money(500))      # True
print(Money(500) == Money(500, "EUR"))  # False
print(Money(500) == 500)             # False, no error
print(len({Money(1), Money(1)}))     # 1: usable in sets now

演算子と順序

__add__(self, other) でクラスに + を持たせられます。self を変更せず新しいオブジェクトを返し、無意味な操作は拒否します。ドルとユーロの加算は ValueError、ただの数値との加算は NotImplemented を返します。__lt__(self, other) で < が使え、sorted()、min()、max() は「a は b より小さいか」を尋ねるだけなので、これだけでオブジェクトを並べ替えられます。__le__、__gt__、__ge__ も追加するか、functools.total_ordering に導出させましょう。

class Money:
    def __init__(self, cents, currency="USD"):
        self.cents = cents
        self.currency = currency

    def __repr__(self):
        return f"Money({self.cents}, {self.currency!r})"

    def __add__(self, other):
        if not isinstance(other, Money):
            return NotImplemented
        if other.currency != self.currency:
            raise ValueError("cannot add different currencies")
        return Money(self.cents + other.cents, self.currency)

    def __lt__(self, other):
        if not isinstance(other, Money):
            return NotImplemented
        return self.cents < other.cents

total = Money(150) + Money(275)
print(total)                                   # Money(425, 'USD')
print(sorted([Money(30), Money(10), Money(20)]))  # [Money(10, 'USD'), Money(20, 'USD'), Money(30, 'USD')]
print(sum([Money(1), Money(2)], Money(0)))     # Money(3, 'USD')
メソッド同士の整合性を保つ等しいオブジェクトはハッシュも等しく、順序は等価性と矛盾せず、__repr__ は __eq__ が見るものをすべて表示するべきです。+ に意外な副作用を持たせず、数値と同じように新しい値を返して両方のオペランドを変更しないようにします。ラボのテストは、元のオブジェクトが変わらないこと、通貨が違うと拒否されること、無関係な型には NotImplemented が返ることを確認します。
  • __repr__ は文字列(例:Money(1999, 'USD'))を返す。表示はしない
  • __eq__ はフィールドを比較し、無関係な型には NotImplemented を返す。ハッシュを残すなら __hash__ も定義する
  • __add__ は新しいオブジェクトを返す。sorted・min・max には __lt__ で十分

次は dataclass です。__init__、__repr__、__eq__ を自動で書いてくれます。

ラボ:採点される内容

  • __repr__ で Money オブジェクトをわかりやすく表示する25点
  • __eq__ で2つの金額が等しい条件を決める25点
  • __add__ で2つの金額を足す25点
  • __lt__ で金額を並べ替えられるようにする25点
エクスプローラー · lab-27-1
▾ lab-27-1
…
bash · ブラウザ内
learner@quantcraft:~/lab-27-1$
Pythonは未読み込みPythonLn 1, Col 1Spaces: 4UTF-8

このトピックをリセットしますか?

lab-27-1 の内容はすべてスターターファイルに戻り、ターミナルも消去されます。現在の解答は失われ、元に戻せません。

リセットされるもの
  • main.py → スターターコード
  • ターミナル履歴とテスト出力
  • 未提出の変更
保持されるもの
  • ラボの最高スコア
  • XPと連続日数
  • 講義の進捗とメモ

コードを実行するにはログインしてください。

レクチャーはすべて無料で読めます。コードの実行、テスト、ラボの提出にはアカウントの作成かログインが必要です。

  • ブラウザ内IDEとbashターミナル。インストール不要
  • 即時採点、XP、ロードマップでの現在地
アカウントを作成ログイン
または
GitHubで続ける