我最近开始使用的一种模式是编写类方法来返回实例。特别是,我一直将它用于数据类(带或不带 @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 文档试图说明什么?
假设您有以下 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 文件读取时。