我想创建一个有很多成员元素的类A
。这个类不应该有Optional
成员,以确保对象中的完整信息可用。
然后我想有一个“修改选项”,它具有与A
相同的成员,但作为可选成员。
什么是最好的方法来做到这一点,而不需要编写两个不同的类的成员?
这是我的方法(工作示例):
from copy import deepcopy
from dataclasses import dataclass
from typing import Optional
@dataclass
class A:
x: int
y: int
@dataclass
class A_ModificationOptions:
x: Optional[int] = None
y: Optional[int] = None
def modifyA(original: A, modification: A_ModificationOptions):
if modification.x is not None:
original.x = deepcopy(modification.x)
if modification.y is not None:
original.y = deepcopy(modification.y)
original_A = A(x=1, y=2)
print("A before modification: ", original_A) # A(x=1, y=2)
modification_A = A_ModificationOptions(y=7)
modifyA(original_A, modification_A)
print("A after modification: ", original_A) # A(x=1, y=7)
此代码满足以下要求:
1.原始A
没有可选成员,因此必须设置所有可选成员。
1.在A
的修改中,只需要设置需要适配的成员。
此代码不满足以下要求:
1.我不想再次将A
的每个成员“复制”到A_ModificationOptions
中。
1.如果可能的话,我不想有modifyA()
功能,但内置的东西。
1.如果2是不可能的:我不想在modifyA
中为每个A
成员添加2行。
有没有一个简洁的方法来存储一个潜在的巨大的类稀疏的“修改选项”?
用例:用户创建一个完整的列表,然后在不同的场景中,他可以使用完整列表的增量,并且完整列表的“增量”必须以某种方式存储->所以我想到了一个原始的完整列表类A
和一个“增量”类A_ModificationOptions
,但我希望这可以以更简洁的方式实现。也许是像智能深度复制一样的东西?
更新一:
不好感谢你的评分您对第3点的解决方案没有考虑更深层次的嵌套类,所以我使用您的建议使其适用于嵌套类。下面的代码解决了第3点:
from copy import deepcopy
from dataclasses import dataclass, is_dataclass
from typing import Optional
class Original:
pass
@dataclass
class B(Original):
a1: int
a2: int
a3: int
@dataclass
class A(Original):
x: int
y: int
b: B
class Modification:
pass
@dataclass
class B_Mod(Modification):
a1: Optional[int] = None
a2: Optional[int] = None
a3: Optional[int] = None
@dataclass
class A_Mod(Modification):
x: Optional[int] = None
y: Optional[int] = None
b: Optional[B_Mod] = None
def modifyDataclass(original: Original, modification: Modification):
assert is_dataclass(original) and is_dataclass(modification)
for k, v in vars(modification).items():
if is_dataclass(v):
assert isinstance(v, Modification)
modifyDataclass(original=getattr(original, k), modification=v)
return
if v is not None:
setattr(original, k, v)
original_A = A(x=1, y=2, b=B(a1=3, a2=4, a3=5))
print(
"A before modification: ", original_A
) # A(x=1, y=2, b=B(a1=3, a2=4, a3=5))
modification_A = A_Mod(y=7, b=B_Mod(a2=19))
modifyDataclass(original_A, modification_A)
print(
"A after modification: ", original_A
) # A(x=1, y=7, b=B(a1=3, a2=19, a3=5))
现在如果有一个解决方案的第1和第2点,这将是惊人的!
也许还可以通过某种方式进行推导?就像A_Mod是A的子元素,但随后将所有成员切换为可选成员?
1条答案
按热度按时间eqzww0vc1#
我想我明白你想要什么,这里有一个动态生成
A_ModificationOptions
类的方法。正如注解中所指出的,这将 * 永远 * 通过静态类型检查器。如果你想运行类似mypy
或pyright
的东西,你将不得不Any
的修改选项。这是Python中非常动态的反射。现在,几个音符。
dataclass
是一个装饰器,就像任何装饰器一样,它只是在事后应用于一个类。也就是说,只是
因此,如果我们选择这样做,我们可以像普通的Python函数一样在我们创建的类上调用
dataclass
。当我们谈到这个主题时,我们也可以使用普通的Python来创建类。type
有一个三参数的形式,它充当新类的构造函数。让我们看看我们是如何做到的。我们需要
dataclass
和fields
。我还导入Optional
以获得技术上正确的注解,但这并不影响语义。现在的魔术酱料,评论为您的方便。
要使用它,我们只需传递原始类并将结果赋给一个名称。
您的
modifyA
函数与dataclasses.replace
有 * 种 * 接近,但后者(a)接受字典,(b)返回新示例而不是原地突变。幸运的是,编写我们自己的代码相当简单。这基本上就是wjandrea在评论中所建议的。我只是更喜欢使用
dataclasses.fields
而不是vars
,因为它保证只得到**类的字段,而不是从非类的超类或从别人那里得到任何额外的东西。你的代码就像建议的那样工作。
我将函数重命名为
modify
而不是modifyA
,因为它实际上从来没有做任何特定于A
的事情。这个函数将适用于 any@dataclass
和相应的_ModificationOptions
类。不需要重写,即使是表面上的。在线试用!