Python: 使用嵌套字典创建 class 的嵌套数据class
Python: Use nested dict to create class of nested dataclass
在基本情况下,可以轻松地将字典映射到参数。下面显示了基本示例。
def func1(x: int, y: int):
return x+y
input = {
"x": 1,
"y": 2,
}
## This Works
sum = func1(**input)
# sum = 3
Python 是否提供任何其他类型的快捷方式来为嵌套 类 启用此类行为?
from dataclasses import dataclass
@dataclass
class X:
x: int
@dataclass
class Y:
y: int
def func2(x: X, y: Y):
return x.x + y.y
input_2 = {
"X": {
"x": 1,
},
"Y": {
"y": 1,
},
}
sum = func2(**input_2)
# TypeError: func2() got an unexpected keyword argument 'X'
我试过其他方法。这是一个可行的示例,但不是很通用。
sum = func2(X(input_2[X][x]),Y(input_2[Y][y])
pydantic 也失败了
from pydantic import BaseModel
class X(BaseModel):
x: int
class Y(BaseModel):
y: int
def func2(x: X, y: Y):
return x.x + y.y
input_2 = {
"X": {
"x": 1,
},
"Y": {
"y": 1,
},
}
sum = func2(**input_2)
@dataclass
class Math:
"""Collection of Configurations and Data Loading Utilities for PlayFab Churn Featurization"""
x: X
y: Y
@classmethod
def load(cls, config_json):
return Math(
x=X(**config_json['x']),
y=Y(**config_json['y']),
)
我想绕过构造函数,给自己另辟蹊径。
我仍然获得所有嵌套数据类的好处,保持与我的旧构造函数的向后兼容性,并且仍然不需要 init 方法。
我想创建一个包含 X
和 Y
的新 class,假设 C
可以满足您的情况
from pydantic import BaseModel
class X(BaseModel):
x: int
class Y(BaseModel):
y: int
class C(X, Y):
pass
def func2(c: C):
x = c.x
y = c.y
return x + y
input_2 = C(**{
"x": 1,
"y": 1,
})
sum = func2(input_2)
print(sum)
您可以使用装饰器将函数参数的每个 dict
参数转换为其注释类型,假设在这种情况下类型为 dataclass
或 BaseModel
。
dataclass-wizard
的示例 - 它也应该支持 嵌套数据类 模型:
import functools
from dataclasses import dataclass, is_dataclass
from dataclass_wizard import fromdict
def transform_dict_to_obj(f):
name_to_tp = {name: tp for name, tp in f.__annotations__.items()
if is_dataclass(tp)}
@functools.wraps(f)
def new_func(**kwargs):
for name, tp in name_to_tp.items():
if name in kwargs:
kwargs[name] = fromdict(tp, kwargs[name])
return f(**kwargs)
return new_func
@dataclass
class X:
x: int
@dataclass
class Y:
y: int
@transform_dict_to_obj
def func2(*, x: X, y: Y) -> str:
return x.x + y.y
input_2 = {
"x": {
"x": 1,
},
"y": {
"y": 1,
},
}
sum = func2(**input_2)
print('Sum:', sum)
assert sum == 2 # OK
类似地,pydantic:
import functools
from pydantic import BaseModel
class X(BaseModel):
x: int
class Y(BaseModel):
y: int
def transform_dict_to_obj(f):
name_to_from_dict = {name: tp.parse_obj
for name, tp in f.__annotations__.items()
if issubclass(tp, BaseModel)}
@functools.wraps(f)
def new_func(**kwargs):
for name, from_dict in name_to_from_dict.items():
if name in kwargs:
kwargs[name] = from_dict(kwargs[name])
return f(**kwargs)
return new_func
@transform_dict_to_obj
def func2(*, x: X, y: Y) -> str:
return x.x + y.y
input_2 = {
"x": {
"x": 1,
},
"y": {
"y": 1,
},
}
sum = func2(**input_2)
print('Sum:', sum)
assert sum == 2 # OK
稍微优化一点的版本,不用每次在装饰器中使用for
循环,只需要在运行中添加你需要的逻辑,然后生成新的函数使用dataclasses._create_fn()
或类似的:
from dataclasses import dataclass, is_dataclass, _create_fn
from dataclass_wizard import fromdict
def transform_dict_to_obj_optimized(f):
args = []
body_lines = []
return_type = f.__annotations__.pop('return', None)
for name, tp in f.__annotations__.items():
type_name = tp.__qualname__
args.append(name)
if is_dataclass(tp):
body_lines.append(f'if {name}:')
body_lines.append(f' {name} = fromdict({type_name}, {name})')
body_lines.append(f'return original_fn({",".join(args)})')
return _create_fn(f.__name__, args, body_lines,
return_type=return_type,
locals={'original_fn': f},
globals=globals())
@dataclass
class X:
x: int
@dataclass
class Y:
y: int
@transform_dict_to_obj_optimized
def func2(x: X, y: Y) -> int:
return x.x + y.y
input_2 = {
"x": {
"x": 1,
},
"y": {
"y": 1,
},
}
sum = func2(**input_2)
print('Sum:', sum)
assert sum == 2 # OK
在基本情况下,可以轻松地将字典映射到参数。下面显示了基本示例。
def func1(x: int, y: int):
return x+y
input = {
"x": 1,
"y": 2,
}
## This Works
sum = func1(**input)
# sum = 3
Python 是否提供任何其他类型的快捷方式来为嵌套 类 启用此类行为?
from dataclasses import dataclass
@dataclass
class X:
x: int
@dataclass
class Y:
y: int
def func2(x: X, y: Y):
return x.x + y.y
input_2 = {
"X": {
"x": 1,
},
"Y": {
"y": 1,
},
}
sum = func2(**input_2)
# TypeError: func2() got an unexpected keyword argument 'X'
我试过其他方法。这是一个可行的示例,但不是很通用。
sum = func2(X(input_2[X][x]),Y(input_2[Y][y])
pydantic 也失败了
from pydantic import BaseModel
class X(BaseModel):
x: int
class Y(BaseModel):
y: int
def func2(x: X, y: Y):
return x.x + y.y
input_2 = {
"X": {
"x": 1,
},
"Y": {
"y": 1,
},
}
sum = func2(**input_2)
@dataclass
class Math:
"""Collection of Configurations and Data Loading Utilities for PlayFab Churn Featurization"""
x: X
y: Y
@classmethod
def load(cls, config_json):
return Math(
x=X(**config_json['x']),
y=Y(**config_json['y']),
)
我想绕过构造函数,给自己另辟蹊径。 我仍然获得所有嵌套数据类的好处,保持与我的旧构造函数的向后兼容性,并且仍然不需要 init 方法。
我想创建一个包含 X
和 Y
的新 class,假设 C
可以满足您的情况
from pydantic import BaseModel
class X(BaseModel):
x: int
class Y(BaseModel):
y: int
class C(X, Y):
pass
def func2(c: C):
x = c.x
y = c.y
return x + y
input_2 = C(**{
"x": 1,
"y": 1,
})
sum = func2(input_2)
print(sum)
您可以使用装饰器将函数参数的每个 dict
参数转换为其注释类型,假设在这种情况下类型为 dataclass
或 BaseModel
。
dataclass-wizard
的示例 - 它也应该支持 嵌套数据类 模型:
import functools
from dataclasses import dataclass, is_dataclass
from dataclass_wizard import fromdict
def transform_dict_to_obj(f):
name_to_tp = {name: tp for name, tp in f.__annotations__.items()
if is_dataclass(tp)}
@functools.wraps(f)
def new_func(**kwargs):
for name, tp in name_to_tp.items():
if name in kwargs:
kwargs[name] = fromdict(tp, kwargs[name])
return f(**kwargs)
return new_func
@dataclass
class X:
x: int
@dataclass
class Y:
y: int
@transform_dict_to_obj
def func2(*, x: X, y: Y) -> str:
return x.x + y.y
input_2 = {
"x": {
"x": 1,
},
"y": {
"y": 1,
},
}
sum = func2(**input_2)
print('Sum:', sum)
assert sum == 2 # OK
类似地,pydantic:
import functools
from pydantic import BaseModel
class X(BaseModel):
x: int
class Y(BaseModel):
y: int
def transform_dict_to_obj(f):
name_to_from_dict = {name: tp.parse_obj
for name, tp in f.__annotations__.items()
if issubclass(tp, BaseModel)}
@functools.wraps(f)
def new_func(**kwargs):
for name, from_dict in name_to_from_dict.items():
if name in kwargs:
kwargs[name] = from_dict(kwargs[name])
return f(**kwargs)
return new_func
@transform_dict_to_obj
def func2(*, x: X, y: Y) -> str:
return x.x + y.y
input_2 = {
"x": {
"x": 1,
},
"y": {
"y": 1,
},
}
sum = func2(**input_2)
print('Sum:', sum)
assert sum == 2 # OK
稍微优化一点的版本,不用每次在装饰器中使用for
循环,只需要在运行中添加你需要的逻辑,然后生成新的函数使用dataclasses._create_fn()
或类似的:
from dataclasses import dataclass, is_dataclass, _create_fn
from dataclass_wizard import fromdict
def transform_dict_to_obj_optimized(f):
args = []
body_lines = []
return_type = f.__annotations__.pop('return', None)
for name, tp in f.__annotations__.items():
type_name = tp.__qualname__
args.append(name)
if is_dataclass(tp):
body_lines.append(f'if {name}:')
body_lines.append(f' {name} = fromdict({type_name}, {name})')
body_lines.append(f'return original_fn({",".join(args)})')
return _create_fn(f.__name__, args, body_lines,
return_type=return_type,
locals={'original_fn': f},
globals=globals())
@dataclass
class X:
x: int
@dataclass
class Y:
y: int
@transform_dict_to_obj_optimized
def func2(x: X, y: Y) -> int:
return x.x + y.y
input_2 = {
"x": {
"x": 1,
},
"y": {
"y": 1,
},
}
sum = func2(**input_2)
print('Sum:', sum)
assert sum == 2 # OK