#python #oop #python-dataclasses
Вопрос:
В настоящее время у меня есть код, который выглядит примерно так, с удалением нерелевантных методов.
import math
import numpy as np
from decimal import Decimal
from dataclasses import dataclass, field
from typing import Optional, List
@dataclass
class A:
S0: int
K: int
r: float = 0.05
T: int = 1
N: int = 2
StockTrees: List[float] = field(init=False, default_factory=list)
pu: Optional[float] = 0
pd: Optional[float] = 0
div: Optional[float] = 0
sigma: Optional[float] = 0
is_put: Optional[bool] = field(default=False)
is_american: Optional[bool] = field(default=False)
is_call: Optional[bool] = field(init=False)
is_european: Optional[bool] = field(init=False)
def __post_init__(self):
self.is_call = not self.is_put
self.is_european = not self.is_american
@property
def dt(self):
return self.T/float(self.N)
@property
def df(self):
return math.exp(-(self.r - self.div) * self.dt)
@dataclass
class B(A):
u: float = field(init=False)
d: float = field(init=False)
qu: float = field(init=False)
qd: float = field(init=False)
def __post_init__(self):
super().__post_init__()
self.u = 1 self.pu
self.d = 1 - self.pd
self.qu = (math.exp((self.r - self.div) * self.dt) - self.d)/(self.u - self.d)
self.qd = 1 - self.qu
@dataclass
class C(B):
def __post_init__(self):
super().__post_init__()
self.u = math.exp(self.sigma * math.sqrt(self.dt))
self.d = 1/self.u
self.qu = (math.exp((self.r - self.div)*self.dt) - self.d)/(self.u - self.d)
self.qd = 1 - self.qu
В принципе, у меня есть класс A
, в котором он определяет некоторые атрибуты, которыми будут делиться все его дочерние классы, поэтому на самом деле он предназначен только для инициализации с помощью создания экземпляров его дочерних классов, а его атрибуты должны наследоваться его дочерними классами. Дочерний класс B
предназначен для процесса, который выполняет некоторые вычисления, которые наследуются, с помощью C
которых выполняется вариация одного и того же вычисления. C
в основном наследует все методы, B
и его единственное отличие заключается в том, что его расчет self.u
и self.d
разные.
Можно запустить код либо с помощью B
вычисления , для которого требуются аргументы pu
, pd
либо C
с помощью вычисления, для которого требуются аргументы sigma
, как показано ниже
if __name__ == "__main__":
am_option = B(50, 52, r=0.05, T=2, N=2, pu=0.2, pd=0.2, is_put=True, is_american=True)
print(f"{am_option.sigma = }")
print(f"{am_option.pu = }")
print(f"{am_option.pd = }")
print(f"{am_option.qu = }")
print(f"{am_option.qd = }")
eu_option2 = C(50, 52, r=0.05, T=2, N=2, sigma=0.3, is_put=True)
print(f"{am_option.sigma = }")
print(f"{am_option.pu = }")
print(f"{am_option.pd = }")
print(f"{am_option.qu = }")
print(f"{am_option.qd = }")
что дает результат
am_option.pu = 0.2
am_option.pd = 0.2
am_option.qu = 0.6281777409400603
am_option.qd = 0.3718222590599397
Traceback (most recent call last):
File "/home/dazza/option_pricer/test.py", line 136, in <module>
eu_option2 = C(50, 52, r=0.05, T=2, N=2, sigma=0.3, is_put=True)
File "<string>", line 15, in __init__
File "/home/dazza/option_pricer/test.py", line 109, in __post_init__
super().__post_init__()
File "/home/dazza/option_pricer/test.py", line 55, in __post_init__
self.qu = (math.exp((self.r - self.div) * self.dt) - self.d)/(self.u - self.d)
ZeroDivisionError: float division by zero
Таким образом, создание экземпляра B
работает нормально, так как оно успешно рассчитало значения pu
, pd
, qu
и qd
. Однако моя проблема возникает, когда экземпляр C
не может вычислить qu
с тех пор pu
и pd
по умолчанию равен нулю, что заставляет его делиться на 0.
Мой вопрос: Как я могу исправить это так , чтобы C
наследовались все атрибуты инициализации (в том числе __post_init__
) A
и все методы B
, и в то же время выполнялись ее вычисления self.u = math.exp(self.sigma * math.sqrt(self.dt))
и self.d = 1/self.u
перезапись self.u = 1 self.pu
self.d = 1 - self.pd
B
, а также сохранение self.qu
и self.qd
то же самое?(они одинаковы для B
и C
)
Комментарии:
1. Вы действительно хотели бы позвонить
super().__post_init__()
в класс C, если расчет отличается от расчета в классе B?2. Какова была бы здесь альтернатива?
A().__post_init__()
Вместо этого звонишь? Если я это сделаю, я получуA().__post_init__() TypeError: __init__() missing 2 required positional arguments: 'S0' and 'K'
, и я не знаю, куда идти дальше? Я все еще учусь разбираться в ООП.3. Когда вы создаете экземпляр C, вы не получаете новый экземпляр B и A — просто у C также есть атрибуты B и A. Поэтому нет способа перезаписать себя. ку на Б, потому что этого не существует. сам. qu и self.qd находятся на C, и когда вы запускаете метод, унаследованный от B, он использует атрибуты вашего экземпляра C.
4. @TonySuffolk66 Создание экземпляра
C
унаследовало бы атрибуты отA
иB
, включаяu
иd
B
, верно?. Разве нет способа написать его так, чтобы создание экземпляраC
имело своеu
значение иd
заменяло унаследованноеu
иd
отB
?5. Можете ли вы установить свои атрибуты, не связанные с инициализацией, независимо от других значений? Я задаюсь вопросом, должны ли какие- либо из них быть атрибутами экземпляра, а не свойствами (только для чтения).
Ответ №1:
Определите другой метод для инициализации u
и d
, чтобы вы могли переопределить эту часть B
, не переопределяя, как qu
и qd
определены.
@dataclass
class B(A):
u: float = field(init=False)
d: float = field(init=False)
qu: float = field(init=False)
qd: float = field(init=False)
def __post_init__(self):
super().__post_init__()
self._define_u_and_d()
self.qu = (math.exp((self.r - self.div) * self.dt) - self.d)/(self.u - self.d)
self.qd = 1 - self.qu
def _define_u_and_d(self):
self.u = 1 self.pu
self.d = 1 - self.pd
@dataclass
class C(B):
def _define_u_and_d(self):
self.u = math.exp(self.sigma * math.sqrt(self.dt))
self.d = 1/self.u
Комментарии:
1. Это почти похоже на то, что мне нужно. Однако я не думаю, что
C
это наследуетсяself.is_call
от__post_init__
ofA
, так как в моем фактическом коде, гдеself.is_call
используется один из методовB
(который наследуетсяC
), говоритсяAttributeError: 'C' object has no attribute 'is_call'
2. Невозможно диагностировать, учитывая код, который вы показали.
C
наследуется__post_init__
отB
, который вызываетA.__post_init__
черезsuper
.
Ответ №2:
Python поддерживает множественное наследование. Вы можете наследовать от A
предыдущих B
, что означает, что любые перекрывающиеся методы будут взяты из A
(например __post_init__
). Любой код, который вы напишете в классе C
, перезапишет то, что унаследовано от A
и B
. Если вам нужно больше контролировать, какие методы исходят из какого класса, вы всегда можете определить метод C
и выполнить вызов функции A
или B
(например A.dt(self)
).
class C(A, B):
...
ЕЩЕ ОДНА ПРАВКА:
Я только что видел, что A
это инициализирует некоторые вещи, которые вы хотите C
включить . Поскольку C
родитель теперь A
(если вы использовали мой код выше), вы можете добавить обратно в super().__post_init__()
строку C
«s __post_init__
«, чтобы он вызывал A
«s __post_init__
«.
Если это не сработает, вы всегда можете просто вставить A.__post_init__(self)
__post_init__
«о C
«.
Комментарии:
1. Деление на 0 не должно быть проблемой , если
C
бы можно было перезаписатьself.u
иself.d
B
, поэтому я спрашиваю, есть ли способ это сделать.2. Если B уже наследует от A, то, честно говоря, делать так, чтобы C также наследовал напрямую от A, кажется бессмысленным.
3. Спасибо 🙂 . Это, однако, сейчас дает
AttributeError: 'C' object has no attribute 'is_call'
. Я думаю, что проблема здесь в том, чтоis_call
определено в__post_init__
A
том, что как-то не инициировано. Должно ли это быть?4. Также
is_call
используется в одном из методовB
, но я просто не описал методы выше.5. Я
C
унаследовал отA
, потому что они хотят получить все , от всех методовA
, но также и некоторые методыB
, поэтомуA
их нужно сначала унаследовать, чтобыB
не переопределять ихC
.