如何在Python中将数据类传入常规类的参数?

1 投票
4 回答
83 浏览
提问于 2025-04-14 17:49

我想用一个叫做 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

撰写回答