如何从数据类中动态删除字段?
How to remove dynamically fields from a dataclass?
我想继承我的数据类但删除它的一些字段。
我怎样才能在运行时做到这一点,这样我就不需要一个一个地复制所有成员?
示例:
from dataclasses import dataclass
@dataclass
class A:
a: int
b: int
c: int
d: int
@remove("c", "d")
class B(A):
pass
这样 A
会定义 a, b, c, d
而 B
只会定义 a
和 b
。
我们可以从 __annotations__
字典和 __dataclass_fields__
中删除特定字段,然后使用 dataclasses.make_dataclass
:
重建我们的 class
def remove(*fields):
def _(cls):
fields_copy = copy.copy(cls.__dataclass_fields__)
annotations_copy = copy.deepcopy(cls.__annotations__)
for field in fields:
del fields_copy[field]
del annotations_copy[field]
d_cls = dataclasses.make_dataclass(cls.__name__, annotations_copy)
d_cls.__dataclass_fields__ = fields_copy
return d_cls
return _
注意 我们复制注释和字段是为了不影响 A。否则它也会从 A 中删除这些字段以及任何其他继承 A 和删除的尝试同样,我们已经删除的字段会导致错误。即:
from dataclasses import dataclass
@dataclass
class A:
a: int
b: int
c: int
d: int
@remove("c", "d")
class B(A):
pass
@remove("b", "c")
class C(A):
pass
这会给我们一个 KeyError
因为我们已经删除了“c”并且它不再存在于 A 的字典中。
这是另一种实现方式。
from dataclasses import make_dataclass, fields, dataclass
@dataclass
class A:
a: int
b: int
c: int
d: int
def remove(*exclusions):
def wrapper(cls):
new_fields = [(i.name, i.type, i) for i in fields(cls) if i.name not in exclusions]
return make_dataclass(cls.__name__, new_fields)
return wrapper
@remove('b', 'a')
class B(A):
pass
foo = B(1, 2)
baz = A(1, 2, 3, 4)
print(foo)
print(baz)
虽然在写出来时我意识到它可能更适合作为 class 工厂函数,因为 isinstance(foo, A)
等实例检查会失败并且代码的用户可能会感到惊讶。
from dataclasses import make_dataclass, fields, dataclass
@dataclass
class A:
a: int
b: int
c: int
d: int
def factory(base, name, exclusions):
new_fields = [(i.name, i.type, i) for i in fields(base) if i.name not in exclusions]
return make_dataclass(name, new_fields)
B = factory(base=A, name='B', exclusions=('b', 'c'))
foo = B(1, 2)
baz = A(1, 2, 3, 4)
print(foo)
print(baz)
我想继承我的数据类但删除它的一些字段。 我怎样才能在运行时做到这一点,这样我就不需要一个一个地复制所有成员?
示例:
from dataclasses import dataclass
@dataclass
class A:
a: int
b: int
c: int
d: int
@remove("c", "d")
class B(A):
pass
这样 A
会定义 a, b, c, d
而 B
只会定义 a
和 b
。
我们可以从 __annotations__
字典和 __dataclass_fields__
中删除特定字段,然后使用 dataclasses.make_dataclass
:
def remove(*fields):
def _(cls):
fields_copy = copy.copy(cls.__dataclass_fields__)
annotations_copy = copy.deepcopy(cls.__annotations__)
for field in fields:
del fields_copy[field]
del annotations_copy[field]
d_cls = dataclasses.make_dataclass(cls.__name__, annotations_copy)
d_cls.__dataclass_fields__ = fields_copy
return d_cls
return _
注意 我们复制注释和字段是为了不影响 A。否则它也会从 A 中删除这些字段以及任何其他继承 A 和删除的尝试同样,我们已经删除的字段会导致错误。即:
from dataclasses import dataclass
@dataclass
class A:
a: int
b: int
c: int
d: int
@remove("c", "d")
class B(A):
pass
@remove("b", "c")
class C(A):
pass
这会给我们一个 KeyError
因为我们已经删除了“c”并且它不再存在于 A 的字典中。
这是另一种实现方式。
from dataclasses import make_dataclass, fields, dataclass
@dataclass
class A:
a: int
b: int
c: int
d: int
def remove(*exclusions):
def wrapper(cls):
new_fields = [(i.name, i.type, i) for i in fields(cls) if i.name not in exclusions]
return make_dataclass(cls.__name__, new_fields)
return wrapper
@remove('b', 'a')
class B(A):
pass
foo = B(1, 2)
baz = A(1, 2, 3, 4)
print(foo)
print(baz)
虽然在写出来时我意识到它可能更适合作为 class 工厂函数,因为 isinstance(foo, A)
等实例检查会失败并且代码的用户可能会感到惊讶。
from dataclasses import make_dataclass, fields, dataclass
@dataclass
class A:
a: int
b: int
c: int
d: int
def factory(base, name, exclusions):
new_fields = [(i.name, i.type, i) for i in fields(base) if i.name not in exclusions]
return make_dataclass(name, new_fields)
B = factory(base=A, name='B', exclusions=('b', 'c'))
foo = B(1, 2)
baz = A(1, 2, 3, 4)
print(foo)
print(baz)