如何在Python中将数据类传入常规类的参数?
我想用一个叫做 Points 的数据类来创建一个普通的类 Motion。这个普通类需要四个参数,分别是 self、x、y 和 z。但是,当我把这个数据类传给普通类,并运行测试方法时,出现了一个错误:
Motion.__init__() 缺少两个必需的位置参数:'y' 和 'z'。
我该怎么解决这个问题呢?
源代码:
from dataclasses import dataclass
@dataclass
class Points:
x: int
y: int
z: int
class Motion:
def __init__(self, x:int, y:int, z:int):
self.x = x
self.y = y
self.z = z
def test(self):
print(self.x, self.y, self.z)
result = Motion(Points(1,2,3))
result.test()
4 个回答
1
你可以使用 asdict
,就像其他回答中提到的那样,但如果你多花点功夫,可以像使用 kwargs
一样直接传递它。要让一个类支持字典解包,只有两个要求:它必须实现 __getitem__
和 keys
方法。
from dataclasses import dataclass, asdict
from typing import Any, Iterable
@dataclass
class BaseDataclass:
# required for dictionary unpacking
def keys(self) -> Iterable:
return asdict(self).keys()
# required for dictionary unpacking
def __getitem__(self, key:str) -> Any:
return getattr(self, key, None)
@dataclass
class Point(BaseDataclass):
x:int
y:int
z:int
class Motion:
def __init__(self, x: int, y: int, z: int):
self.x = x
self.y = y
self.z = z
def test(self):
# you don't have to specify each property individually
print(vars(self))
m = Motion(**Point(1, 2, 3))
m.test() # {'x':1, 'y':2, 'z':3}
不过,有一点你可能需要考虑:Motion
是一个 Point
。
from __future__ import annotations
from dataclasses import dataclass, asdict
from typing import Any, Iterable
import json
@dataclass
class BaseDataclass:
# required for dictionary unpacking
def keys(self) -> Iterable:
return asdict(self).keys()
# required for this example
def values(self) -> Iterable:
return asdict(self).values()
# we have keys and values, might as well include items
def items(self) -> Iterable:
return asdict(self).items()
# required for dictionary unpacking
def __getitem__(self, key:str) -> Any:
return getattr(self, key, None)
# nice
def __str__(self) -> str:
return json.dumps(asdict(self), indent=4)
# nice
def __call__(self, **kwargs) -> BaseDataclass:
for key, value in kwargs.items():
if hasattr(self, key):
setattr(self, key, value)
return self
@dataclass
class Point(BaseDataclass):
x:int
y:int
z:int
@dataclass
class Motion(Point):
speed_x:int
speed_y:int
speed_z:int
def stats(msg:str):
print(msg)
print('point:' , p, sep='\n')
print('motion:', m, sep='\n', end='\n\n')
p = Point(1,2,3)
m = Motion(*p.values(), 5, 10, 15)
stats('#new point and motion')
p(x=3,y=4,z=5)
m(**p)
stats('#change point then apply to motion')
m(**p(y=5000), speed_x=30)
stats('#change point while applying to motion')
# extra: override a property without changing the original
m(**{**p, 'x':1, 'y':1}) # more than one value on one line
m(**p).x = 1 # only one value
m.y = 1 # override proceeding values like this
输出
#new point and motion
point:
{
"x": 1,
"y": 2,
"z": 3
}
motion:
{
"x": 1,
"y": 2,
"z": 3,
"speed_x": 5,
"speed_y": 10,
"speed_z": 15
}
#change point then apply to motion
point:
{
"x": 3,
"y": 4,
"z": 5
}
motion:
{
"x": 3,
"y": 4,
"z": 5,
"speed_x": 5,
"speed_y": 10,
"speed_z": 15
}
#change point while applying to motion
point:
{
"x": 3,
"y": 5000,
"z": 5
}
motion:
{
"x": 3,
"y": 5000,
"z": 5,
"speed_x": 30,
"speed_y": 10,
"speed_z": 15
}
2
你可以使用 dataclasses.asdict
这个功能:
from dataclasses import asdict, dataclass
@dataclass
class Points:
x: int
y: int
z: int
class Motion:
def __init__(self, x: int, y: int, z: int):
self.x = x
self.y = y
self.z = z
def test(self):
print(self.x, self.y, self.z)
result = Motion(**asdict(Points(1, 2, 3)))
result.test()
输出结果是:
1 2 3
2
你只给这个类传了一个 Point
对象,所以不需要三个参数。这个数据类并不是分开成几个参数的。
class Motion:
def __init__(self, p: Point):
self.x = p.x
self.y = p.y
self.z = p.z
def test(self):
print(self.x, self.y, self.z)
另外,不要重复 Point
数据类的功能。与其提取每个坐标,不如直接保存这个 Point
对象。
class Motion:
def __init__(self, p: Point):
self.point = p
def test(self):
print(self.point.x, self.point.y, self.point.z)
如果你想要更简洁的写法,可以定义一些属性:
@property
def x(self):
return self.point.x
@x.setter
def x(self, new_x):
self.point.x = new_x