python-3.x 使用强制转换从Dict设置类的属性

xuo3flqw  于 2023-03-20  发布在  Python
关注(0)|答案(3)|浏览(165)

我正在尝试将一个带有键值对的Dict [str,str]解析为一个对象(类)。第一个包含属性的名称,第二个包含值。
例如:[('CLIENT_ID','123456'), ('AUTO_COMMIT', 'False'), ('BROKER_URL', 'http://foo.bar')]
要解析的代码如下所示:

class KafkaSettings():
    BROKER_URL: str = None
    CLIENT_ID: str = None
    AUTO_COMMIT: bool = False

    def __init__(self, d: Dict[str, str]):
        if d is not None:
            for key, value in d.items():
                attr = self.__getattribute__(key)
                casted_value = cast(attr, value)
                self.__setattr__(key, casted_value)

解析器可以工作,但是值的类型被改变了。例如,AUTO_COMMITattr的类型是 bool,但是setattr的类型变成了 str。将值强制转换成正确的类型是不起作用的。我不懂Python语言。
如何解决这个问题?
谢谢

ycggw6v2

ycggw6v21#

这是标准的pydantic电梯间推介。

from pydantic import BaseModel

class KafkaSettings(BaseModel):
    BROKER_URL: str = None
    CLIENT_ID: str = None
    AUTO_COMMIT: bool = False

    @classmethod 
    def factory(cls, tuples):
        di = {v[0]:v[1] for v in tuples}
        return KafkaSettings(**di)
    
print(KafkaSettings.factory([('CLIENT_ID','123456'), ('AUTO_COMMIT', 'False'), ('BROKER_URL', 'http://foo.bar')]))
输出:
BROKER_URL='http://foo.bar' CLIENT_ID='123456' AUTO_COMMIT=False

顺便说一句,pydantic支持多种类型,并且顺序不同"3" =〉foo : str|int =〉"3",而"3" =〉foo : int|str =〉3,IIRC。
并且('AUTO_COMMIT', '1') =〉True
下面是一个更便于故障排除的工厂版本:

@classmethod 
    def factory(cls, tuples):
        try:
            print(f"{tuples=}")
            di = {v[0]:v[1] for v in tuples}
            print(f"{di=}")
            res = KafkaSettings(**di)
            return res
            #pragma: no cover pylint: disable=unused-variable
        except (Exception,) as e: 
            print(f"{e=}")
            breakpoint()
            raise
wa7juj8i

wa7juj8i2#

您可以使用下面的代码解决这个问题,

from typing import Dict

class KafkaSettings():
    BROKER_URL: str = None
    CLIENT_ID: str = None
    AUTO_COMMIT: bool = False

    def __init__(self, d: Dict[str, str]):
        if d is not None:
            for key, value in d.items():
                attr = self.__getattribute__(key)
                
                # change as follows
                if key == "AUTO_COMMIT":
                    value = bool(value)

                self.__setattr__(key, value)

您可以通过检查key来解决此问题,如果它等于AUTO_COMMIT,则可以使用bool()方法将string值强制转换为bool值。
但请注意,这是针对您的代码的特定解决方案。因此,在生成dictionary时,最好将这些值与其实际的type(根据需要)相加。这样就不需要任何强制转换。
举个例子,在这种情况下,你可以用这个,
[('CLIENT_ID','123456'), ('AUTO_COMMIT', False), ('BROKER_URL', 'http://foo.bar')]
(将False存储为布尔值而不是字符串)
代替这一点,
[('CLIENT_ID','123456'), ('AUTO_COMMIT', 'False'), ('BROKER_URL', 'http://foo.bar')]

ekqde3dh

ekqde3dh3#

不能使用类型提示将值自动转换为另一种类型。需要显式。例如(并修复从变量注解提取类型信息的尝试):

class KafkaSettings():
    BROKER_URL: str = None
    CLIENT_ID: str = None
    AUTO_COMMIT: bool = False

    def __init__(self, d: Dict[str, str]):
        for key, value in d.items():
            if issubclass(KafkaSettings.__annotations__[key], bool):
                casted_value = {'True': True, 'False': False}[value]
            else:
                casted_value = value

            setattr(self, key, casted_value)

您真正需要的是更明确的东西,将__init__划分为更简单的__init__和类方法(更简单的__init__可以由dataclasses.dataclass自动生成)。

# Enhance as you like
bool_map = {
    'False': False,
    'True': True,
    'F': False,
    'T': True,
    'yes': True,
    'no': False,
}
    
@dataclass
class KafkaSettings:
    broker_url: str = None
    client_id: str = None
    auto_commit: bool = False

    @classmethod
    def from_dict(cls, d: Dict[str, str]):
        url = d.get('BROKER_URL')
        client_id = d.get('CLIENT_ID')
        autocommit_str = d.get('AUTO_COMMIT')
        if autocommit_str is None:
            autocommit = False
        else:
            autocommit = bool_map[autocommit_str]

        return cls(url, client_id, autocommit)

使用字典是为属性提供值的一种方式,但它不是基本必需的。示例化KafkaSettings所需要的是两个字符串和一个布尔值:将它们直接提供给__init__,并定义类方法从字典中提取它们。

相关问题