[Tutor] Type annotation errors
Peter Otten
__peter__ at web.de
Thu Jun 4 09:46:51 EDT 2020
boB Stepp wrote:
One more (remember I'm very new to mypy):
> The following function yields type annotation errors and I do not
> understand why:
>
> def evaluate_portfolio(
> portfolio: List[Dict[str, Union[str, int, float]]], stock_prices:
> Dict[str, float]
> ) -> Tuple[float, float]:
> """Compute the current value and gain/loss of a portfolio."""
> portfolio_value = 0.0
> gain_loss = 0.0
> for stock in portfolio:
> current_value = stock["shares"] * stock_prices[stock["name"]]
> current_gain_loss = current_value - (stock["shares"] *
> stock["price"]) portfolio_value += current_value
> gain_loss += current_gain_loss
> return portfolio_value, gain_loss
>
> The error messages generated are:
>
> report.py|47 col 43 info| Left operand is of type "Union[str, int, float]"
> report.py|47 col 43 error| Unsupported operand types for * ("str" and
> "float")
Given
x: Union[str, int, float]
y: float
x * y
mypy ensures that * is defined for
str * float # error
int * float # OK
float * float # OK
There might be a way to tell mypy that two cases cannot occur and still use
dicts as pseudo-structs, but I expect that there will be a big impact of
optional typing on the design, and unlike unit tests it will not just
improve the structure, it will also make the code less flexible.
Replacing the somewhat amorphous dict with a dataclass:
Money = float # fixme
@dataclass
class Record:
shares: int
name: str
price: Money
def evaluate_portfolio(
portfolio: List[Record],
stock_prices: Dict[str, Money]
) -> Tuple[float, float]:
"""Compute the current value and gain/loss of a portfolio."""
portfolio_value = 0.0
gain_loss = 0.0
for stock in portfolio:
current_value = stock.shares * stock_prices[stock.name]
current_gain_loss = current_value - stock.shares * stock.price
portfolio_value += current_value
gain_loss += current_gain_loss
return portfolio_value, gain_loss
PS: In spite of my defeatist remark above I prefer this version over the one
you posted; maybe not all is lost in Python land ;)
PPS: For someone who knows a little C the obvious fix for your version is to
cast the union to the expected type:
for stock in portfolio:
shares = cast(int, stock["shares"])
name = cast(str, stock["name"])
price = cast(float, stock["price"])
current_value = shares * stock_prices[name]
current_gain_loss = current_value - shares * price
portfolio_value += current_value
gain_loss += current_gain_loss
> report.py|47 col 56 error| Invalid index type "Union[str, int,
> float]" for "Dict[str, float]"; expected type "str" report.py|48 col 29
> info| Both left and right operands are unions report.py|48 col 29 error|
> Unsupported operand types for - ("float" and "str") report.py|48 col 46
> error| Unsupported operand types for * ("str" and "str") report.py|48 col
> 46 error| Unsupported operand types for * ("str" and "float") report.py|48
> col 46 error| Unsupported operand types for * ("float" and "str")
>
> For reference line 47 is the line in the for loop beginning
> "current_value..."
>
> portfolio is a list of dictionaries where each dictionary is of the form
> {"name": "stock name", "shares": number_of_shares_owned, "price":
> price_per_share}
>
> The code runs flawlessly and gives the expected results. Where am I going
> wrong in my type annotations?
>
More information about the Tutor
mailing list