I have code that looks something like this:
class MyClass:
k: int
v: int
def __init__(self, *, k, v):
self.k = k
self.v = v
def getkv(self):
return self.k, self.v
def test():
a = [{"k": 5, "v": 25}, {"k": 7, "v": 49}, {"k": 9, "v": 81}]
mcl = dict(MyClass(**x).getkv() for x in a)
print(a)
print(mcl)
if __name__ == "__main__":
test()
The output is:
[{'k': 5, 'v': 25}, {'k': 7, 'v': 49}, {'k': 9, 'v': 81}]
{5: 25, 7: 49, 9: 81}
In my actual code I have two classes of this form, and for one of them mypy gives the error:
error: Argument after ** must be a mapping, not "object"
for this line:
mcl = dict(MyClass(**x).getkv() for x in a)
I have spent some time editing the classes to try to make them identical and testing for differences but I can't find anything and mypy does not complain about the code in this sample.
Can anyone suggest a reason why mypy might be objecting? In both cases my test code produces the same expected output.
Thanks
For completeness here is the code that actually fails stripped to shorten it:
from typing import Any, Optional
from pydantic import BaseModel, PositiveFloat, ValidationError, conint, root_validator
class UsageStat(BaseModel):
year: conint(ge=1970, le=2100) # type: ignore
month: conint(ge=1, le=12) # type: ignore
day: Optional[conint(ge=1, le=31)] # type: ignore
time: conint(ge=0, le=44640) # type: ignore
def datekv(self):
return self.day if self.is_daily else self.month, self.time
class EmeterStat(BaseModel):
year: conint(ge=1970, le=3000) # type: ignore
month: conint(ge=1, le=12) # type: ignore
day: Optional[conint(ge=1, le=31)] # type: ignore
energy: PositiveFloat # type: ignore # kW (kilo Watts)
def datekv(self, *, kwh: bool = True):
"""Return key,value pair for period index, energy."""
k = self.day if self.is_daily else self.month
v = self.energy if kwh else int(self.energy * 1000)
return k, v
# Removed some property getters
class Config:
validate_assignment = True
@root_validator(pre=True)
def _convert_energy(cls, values: dict[str, Any]) -> dict[str, Any]:
# removed code to simplify
def test():
usage_stat_list = [
{"year": 2022, "month": 9, "time": 3},
{"year": 2022, "month": 10, "time": 4},
{"year": 2022, "month": 11, "time": 5},
]
usl = dict(UsageStat(**x).datekv() for x in usage_stat_list)
emeter_stat_list = [
{"year": 2022, "month": 9, "energy": 0.3},
{"year": 2022, "month": 10, "energy_wh": 400},
{"year": 2022, "month": 11, "energy": 0.5},
]
# This is the line it complains about
usl = dict(EmeterStat(**x).datekv() for x in emeter_stat_list)
For what mypy
knows, a
might be a list of anything. Help it by typing it as a list of dicts:
from typing import List
...
a: List[dict] = [{"k": 5, "v": 25}, {"k": 7, "v": 49}, {"k": 9, "v": 81}]