使用类方法返回实例并简单定义 __init__(self, **kwargs):

问题描述 投票:0回答:1

我最近开始使用的一种模式是编写类方法来返回实例。特别是,我一直将它用于数据类(带或不带 @dataclass 装饰器)。但这也导致我定义了模糊的 __init__ 方法,如下所示:

def __init__(self, **kwargs):
    for k,v in kwargs:
        setattr(self, k, v)

作为一个更具体的示例,假设我正在编写一个元数据类,其中包含标准化测试问题的详细信息。我希望类的所有实例都具有相同的属性,因此我使用 __slots__,并且我在另一个模块中定义了函数来从 html 文件读取问题的各个部分。

class Metadata:
    __slots__ = question_id, testid, itemnum, subject, system, topic, images, tables, links

    @classmethod
    def from_html(cls, html: BeautifulSoup):
        # next two lines will create the dict metadata with keys for 
        # everything in __slots__
        metadata = MyModule.parse_details(html)
        metadata['images'] = MyModule.process_images(html)
        metadata['tables'] = MyModule.read_tables(html)
        metadata['links'] = MyModule.pull_links(html)
        return cls(**metadata)
        
    @classmethod
    def from_file(filepath: str):
        with open(filepath, 'r') as f:
            metadata = json.load(f)
        return cls(**metadata)

    def __init__(self, **kwargs):
        for k,v in kwargs:
            setattr(self, k, v)

所以对我来说,这似乎是完成任务的最佳方法,即创建一个数据类来保存可以从多个不同源(文件、字典、我定义的其他数据类等)初始化的元数据。缺点是 __init__ 非常不透明。当 __init__ 必须每次都采用相同的关键字参数以使类按照我的意图工作时,使用 **kwargs 感觉很奇怪(这也是我也使用 __slots__ 的部分原因:使数据类的定义更清晰)。

Python 的 attrs 包的

documentation
也这么说:

出于类似的原因,我们强烈反对以下模式:

pt = Point(**row.attributes)

它将您的类与数据库数据模型耦合起来。尝试以一种干净且易于使用的方式设计您的类,而不是基于您的数据库格式。数据库格式可以随时更改,并且您会陷入难以更改的糟糕类设计。将函数和类方法作为现实与最适合您使用的内容之间的过滤器。

这位于我所包含的链接页面顶部附近,我真的不明白它想说什么,因此我提出了问题。

那么你会以不同的方式实现我的代码吗?attrs 文档试图说明什么?

python attr python-dataclasses python-attrs
1个回答
0
投票

假设您有以下 JSON:

{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}

然后你初始化你的类

class Post:
    def __init__(self, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

import json

with open(filepath, encoding='utf-8') as f:
    data = json.load(f)
    post = Post(**data)

以下代码

if not post.completed:
   # do something and exit
else:
   print(post.userId)

将按预期工作。但是,假设您需要将

userId
列重命名为
user_id
(文档的“数据库格式可以随时更改”部分)。现在,您需要将所有代码中出现的所有
post.userId
重命名为
post.user_id
。如果您的代码库仅包含一个 Python 文件,那没问题,但如果它包含大量文件和依赖项怎么办?

现在假设你正在初始化你的类

class Post:
    def __init__(self, postId, id, title, completed):
        self.postId = postId
        self.id = id
        self.title = title
        self.completed = completed

import json

with open(filepath, encoding='utf-8') as f:
    data = json.load(f)
    post = Post(
        postId=data['postId'],
        id=data['id'],
        title=data['title'],
        completed=data['completed'],
    )

现在,如果

postId
重命名为
post_id
,您只需更改整个代码库中的一处:当您从 JSON 文件读取时。

© www.soinside.com 2019 - 2024. All rights reserved.