σμ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 AcademySkills
Ctrl K
AO
+1.2k Learners
LoginJoin ↗
Skills
AO
+1.2k Learners
Login
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Python skill›Module 1 · Python foundations for quants›Topic 1.27·~40 minBeginner

Special methods (__repr__, __eq__)

Skill positionModule 1 of 11 · Topic 27 of 33
1.27 Special methods (__repr__, __eq__)
—Lab score— / 100Total XP—Streak—

Making your objects behave like built-in types

Print a plain object and Python shows something like <__main__.Position object at 0x7f3a>, and Position("A", 1) == Position("A", 1) is False because by default two objects are equal only if they are the very same object. Special methods (also called dunder methods, for the double underscores) let your class plug into Python's own syntax: __repr__ controls how an object is shown, __eq__ controls ==, __add__ controls +, __lt__ controls < and so on. You never call them directly; Python calls them when you use repr(x), a == b, a + b or sorted(items).

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__: show the object clearly

__repr__ returns a string that describes the object for developers. By convention it looks like the code that would build it: Money(1999, 'USD'). Lists, the debugger and error messages all use it, which makes bugs much easier to read. print(x) and str(x) use __repr__ too unless you also define __str__ for a prettier, user-facing text. It must return the string, not print it, and write {self.currency!r} in an f-string to get the quoted form of a string attribute.

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__ and __hash__: what does equal mean?

__eq__(self, other) decides self == other. Compare the fields that define identity, and when other is not the type you expect, return the special value NotImplemented (not False): Python then tries the other side and finally answers False itself, so Money(1) == 5 is safe. Defining __eq__ switches off hashing, so objects can no longer be set members or dictionary keys unless you also define __hash__ consistently, for example 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

Operators and ordering

__add__(self, other) gives your class +. Return a new object rather than changing self, and refuse nonsense: adding dollars to euros should raise ValueError, adding a bare number should return NotImplemented. __lt__(self, other) gives <, and that alone is enough for sorted(), min() and max() to order your objects, because they only need to ask "is a smaller than b?". Add __le__, __gt__, __ge__ too, or let functools.total_ordering derive them.

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')
Keep the methods consistentObjects that compare equal must have equal hashes, ordering must agree with equality, and __repr__ should show everything __eq__ looks at. Do not give + surprising side effects: it should return a new value and leave both operands untouched, like numbers do. The lab tests check these points: that originals are unchanged, mismatched currencies are refused, and unrelated types give NotImplemented.
  • __repr__ returns a string (like Money(1999, 'USD')); it does not print.
  • __eq__ compares fields and returns NotImplemented for foreign types; add __hash__ to keep hashing.
  • __add__ returns a new object; __lt__ is enough for sorted, min and max.

Next up: dataclasses, which write __init__, __repr__ and __eq__ for you.

Lab · what's graded

  • Show a Money object clearly with __repr__25 pts
  • Say when two amounts are equal with __eq__25 pts
  • Add two amounts with __add__25 pts
  • Make amounts sortable with __lt__25 pts
Explorer · lab-27-1
▾ lab-27-1
…
bash · in-browser
learner@quantcraft:~/lab-27-1$
Python not loaded yetPythonLn 1, Col 1Spaces: 4UTF-8

Reset this topic?

Everything in lab-27-1 goes back to the starter files and the terminal is cleared. Your current solution will be lost — this can't be undone.

Will be reset
  • main.py → starter code
  • Terminal history and test output
  • Unsubmitted changes
Will be kept
  • Best lab score
  • Your XP and streak
  • Lecture progress and notes

Log in to run your code.

Every lecture is free to read. Create an account or log in to run code, run tests and submit this lab.

  • In-browser IDE with a bash terminal — nothing to install
  • Instant grading, XP and your place on the skill
Create an accountLog in
OR
Continue with GitHub