@dataclass
class Stock:
symbol: str
price: float = get_price(symbol)
Can a dataclass attribute access to the other one? In the above example, one can create a Stock by providing a symbol and the price. If price is not provided, it defaults to a price which we get from some function get_price. Is there a way to reference symbol?
This example generates error NameError: name 'symbol' is not defined.
You can use __post_init__ here. Because it's going to be called after __init__, you have your attributes already populated so do whatever you want to do there:
from typing import Optional
from dataclasses import dataclass
def get_price(name):
# logic to get price by looking at `name`.
return 1000.0
@dataclass
class Stock:
symbol: str
price: Optional[float] = None
def __post_init__(self):
if self.price is None:
self.price = get_price(self.symbol)
obj1 = Stock("boo", 2000.0)
obj2 = Stock("boo")
print(obj1.price) # 2000.0
print(obj2.price) # 1000.0
So if user didn't pass price while instantiating, price is None. So you can check it in __post_init__ and ask it from get_price.
There is also another shape of the above answer which basically adds nothing more to the existing one. I just added for the records since someone might attempt to do this as well and wonder how is it different with the previous one:
@dataclass
class Stock:
symbol: str
price: InitVar[Optional[float]] = None
def __post_init__(self, price):
self.price = get_price(self.symbol) if price is None else price
You mark the price as InitVar and you can get it with a parameter named price in the __post_init__ method.
If the price is always derivable from the symbol, I would go with a @property. Keeps the code lean and it looks like an attribute from the outside:
def get_price(symbol):
return 123
@dataclass
class Stock:
symbol: str
@property
def price(self):
return get_price(symbol)
stock = Stock("NVDA")
print(stock.price) # 123
If you want to have a settable attribute that also has a default value that is derived from the other fields, you may just want to implement your own __init__ or __post_init__ as suggested.
If this is something you encounter a lot and need more sophisticated ways to handle it, I would recommend looking into pydantic and validators.
from typing import Optional
from pydantic import validator
from pydantic.dataclasses import dataclass
def get_price(symbol):
return 123.0
@dataclass
class Stock:
symbol: str
price: Optional[float] = None
@validator('price')
def validate_price(cls, v, values, **kwargs):
return v or get_price(values["symbol"])
print(Stock("NVDA"))
#> Stock(symbol='NVDA', price=123.0)
print(Stock("NVDA", 456))
#> Stock(symbol='NVDA', price=456.0)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With