如何使数据类更好地与 __slots__ 配合使用?

2023-12-23

It 已决定 https://github.com/ericvsmith/dataclasses/issues/28删除对的直接支持__slots__来自 Python 3.7 的数据类。

尽管如此,__slots__仍然可以与数据类一起使用:

from dataclasses import dataclass

@dataclass
class C():
    __slots__ = "x"
    x: int

然而,由于方式__slots__无法为数据类字段分配默认值:

from dataclasses import dataclass

@dataclass
class C():
    __slots__ = "x"
    x: int = 1

这会导致错误:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: 'x' in __slots__ conflicts with class variable

How can __slots__和默认的dataclass领域可以一起工作吗?


2021 年更新:直接支持__slots__ is 添加到Python 3.10 https://stackoverflow.com/a/69661861/758345。我将这个答案留给后代,并且不会更新它。

这个问题并不是数据类所独有的。任何冲突的类属性都会踩在槽上:

>>> class Failure:
...     __slots__ = tuple("xyz")
...     x=1
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: 'x' in __slots__ conflicts with class variable

这就是老虎机的工作原理。发生错误是因为__slots__为每个槽名称创建一个类级描述符对象:

>>> class Success:
...     __slots__ = tuple("xyz")
...
>>>
>>> type(Success.x)
<class 'member_descriptor'>

为了防止这种变量名冲突错误,必须更改类名称空间before类对象被实例化,以便不存在两个对象竞争类中的相同成员名称:

  • 指定的(默认)值*
  • 槽描述符(由槽机制创建)

为此,一个__init_subclass__父类上的方法是不够的,类装饰器也不够,因为在这两种情况下,当这些函数接收到要更改它的类时,类对象已经创建了。

当前选项:编写元类

直到槽机制被改变以允许更大的灵活性,或者语言本身提供了在类对象实例化之前改变类名称空间的机会,我们唯一的选择是使用元类。

为解决此问题而编写的任何元类至少必须:

  • 从命名空间中删除冲突的类属性/成员
  • 实例化类对象以创建槽描述符
  • 保存对槽描述符的引用
  • 将之前删除的成员及其值放回到类中__dict__(所以dataclass机器可以找到它们)
  • 将类对象传递给dataclass装饰者
  • 将槽描述符恢复到各自的位置
  • 还要考虑大量的极端情况(例如,如果存在__dict__ slot)

至少可以说,这是一项极其复杂的工作。像下面这样定义类会更容易 - 没有默认值,这样根本就不会发生冲突 - 然后添加默认值。

当前选项:在类对象实例化后进行更改

未更改的数据类将如下所示:

@dataclass
class C:
    __slots__ = "x"
    x: int

改变很简单。改变__init__签名以反映所需的默认值,然后更改__dataclass_fields__以反映默认值的存在。

from functools import wraps

def change_init_signature(init):
    @wraps(init)
    def __init__(self, x=1):
        init(self,x)
    return __init__

C.__init__ = change_init_signature(C.__init__)

C.__dataclass_fields__["x"].default = 1

Test:

>>> C()
C(x=1)
>>> C(2)
C(x=2)
>>> C.x
<member 'x' of 'C' objects>
>>> vars(C())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: vars() argument must have __dict__ attribute

有用!

当前选项:asetmember装饰者

经过一番努力,所谓的setmember可以使用装饰器以上述方式自动更改类。这需要偏离数据类 API,以便在类主体内部以外的位置定义默认值,可能类似于:

@setmember(x=field(default=1))
@dataclass
class C:
    __slots__="x"
    x: int

同样的事情也可以通过__init_subclass__父类的方法:

class SlottedDataclass:
    def __init_subclass__(cls, **kwargs):
        cls.__init_subclass__()
        # make the class changes here

class C(SlottedDataclass, x=field(default=1)):
    __slots__ = "x"
    x: int

未来的可能性:改变老虎机机械

如上所述,另一种可能性是 python 语言改变插槽机制以提供更大的灵活性。实现此目的的一种方法可能是更改槽描述符本身以在类定义时存储类级别数据。

也许,这可以通过提供一个dict as the __slots__论证(见下文)。类级数据(1 代表 x,2 代表 y)可以仅存储在描述符本身上以供稍后检索:

class C:
    __slots__ = {"x": 1, "y": 2}

assert C.x.value == 1
assert C.y.value == y

一个困难:可能只需要一个slot_member.value出现在某些插槽上,而不是其他插槽上。这可以通过从新的工厂导入空槽工厂来解决。slottools图书馆:

from slottools import nullslot

class C:
    __slots__ = {"x": 1, "y": 2, "z": nullslot()}

assert not hasattr(C.z, "value")

上面建议的代码风格与数据类 API 有所不同。然而,插槽机制本身甚至可以进行更改以允许这种风格的代码,特别要考虑到数据类 API 的适应:

class C:
    __slots__ = "x", "y", "z"
    x = 1  # 1 is stored on C.x.value
    y = 2  # 2 is stored on C.y.value

assert C.x.value == 1
assert C.y.value == y
assert not hasattr(C.z, "value")

未来的可能性:在类体内“准备”类名称空间

另一种可能性是改变/准备(与__prepare__元类的方法)类名称空间。

目前,在类对象实例化和槽机制开始工作之前,没有机会(除了编写元类)编写更改类名称空间的代码。可以通过创建一个用于预先准备类名称空间的挂钩来更改此情况,并使其仅在运行该挂钩后才会产生抱怨名称冲突的错误。

这个所谓的__prepare_slots__hook 可能看起来像这样,我认为这还不错:

from dataclasses import dataclass, prepare_slots

@dataclass
class C:
    __slots__ = ('x',)
    __prepare_slots__ = prepare_slots
    x: int = field(default=1)

The dataclasses.prepare_slots函数只是一个函数——类似于__prepare__ method https://docs.python.org/3/reference/datamodel.html#preparing-the-class-namespace-- 接收类名称空间并在创建类之前更改它。特别是对于这种情况,默认数据类字段值将存储在其他方便的位置,以便在创建槽描述符对象后可以检索它们。


* 请注意,与槽冲突的默认字段值也可能由数据类机制创建,如果dataclasses.field正在使用中。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使数据类更好地与 __slots__ 配合使用? 的相关文章

  • 与区域指示符字符类匹配的 python 正则表达式

    我在 Mac 上使用 python 2 7 10 表情符号中的标志由一对表示区域指示符号 https en wikipedia org wiki Regional Indicator Symbol 我想编写一个 python 正则表达式来在
  • 使用 kivy textinput 的 'input_type' 属性的问题

    您好 我在使用 kivy 的文本输入小部件的 input type 属性时遇到问题 问题是我制作了两个自定义文本输入 其中一个称为 StrText 其中设置了 input type text 然后是第二个文本输入 名为 NumText 其
  • 使用Python请求登录Google帐户

    在多个登录页面上 需要谷歌登录才能继续 我想用requestspython 中的库以便让我自己登录 通常这很容易使用requests库 但是我无法让它工作 我不确定这是否是由于 Google 做出的一些限制 也许我需要使用他们的 API 或
  • 如何将张量流模型部署到azure ml工作台

    我在用Azure ML Workbench执行二元分类 到目前为止 一切正常 我有很好的准确性 我想将模型部署为用于推理的 Web 服务 我真的不知道从哪里开始 azure 提供了这个doc https learn microsoft co
  • datetime.datetime.now() 返回旧值

    我正在通过匹配日期查找 python 中的数据存储条目 我想要的是每天选择 今天 的条目 但由于某种原因 当我将代码上传到 gae 服务器时 它只能工作一天 第二天它仍然返回相同的值 例如当我上传代码并在 07 01 2014 执行它时 它
  • Python 2:SMTPServerDisconnected:连接意外关闭

    我在用 Python 发送电子邮件时遇到一个小问题 me my email address you recipient s email address me email protected cdn cgi l email protectio
  • 从Python中的字典列表中查找特定值

    我的字典列表中有以下数据 data I versicolor 0 Sepal Length 7 9 I setosa 0 I virginica 1 I versicolor 0 I setosa 1 I virginica 0 Sepal
  • Python,将函数的输出重定向到文件中

    我正在尝试将函数的输出存储到Python中的文件中 我想做的是这样的 def test print This is a Test file open Log a file write test file close 但是当我这样做时 我收到
  • 在 Sphinx 文档中*仅*显示文档字符串?

    Sphinx有一个功能叫做automethod从方法的文档字符串中提取文档并将其嵌入到文档中 但它不仅嵌入了文档字符串 还嵌入了方法签名 名称 参数 我如何嵌入only文档字符串 不包括方法签名 ref http www sphinx do
  • 加快网络抓取速度

    我正在使用一个非常简单的网络抓取工具抓取 23770 个网页scrapy 我对 scrapy 甚至 python 都很陌生 但设法编写了一个可以完成这项工作的蜘蛛 然而 它确实很慢 爬行 23770 个页面大约需要 28 小时 我看过scr
  • javascript 是否有等效的 __repr__ ?

    我最接近Python的东西repr这是 function User name password this name name this password password User prototype toString function r
  • Python3 在 DirectX 游戏中移动鼠标

    我正在尝试构建一个在 DirectX 游戏中执行一些操作的脚本 除了移动鼠标之外 我一切都正常 是否有任何可用的模块可以移动鼠标 适用于 Windows python 3 Thanks I used pynput https pypi or
  • 不同编程语言中的浮点数学

    我知道浮点数学充其量可能是丑陋的 但我想知道是否有人可以解释以下怪癖 在大多数编程语言中 我测试了 0 4 到 0 2 的加法会产生轻微的错误 而 0 4 0 1 0 1 则不会产生错误 两者计算不平等的原因是什么 在各自的编程语言中可以采
  • 从 NumPy ndarray 中选择行

    我只想从 a 中选择某些行NumPy http en wikipedia org wiki NumPy基于第二列中的值的数组 例如 此测试数组的第二列包含从 1 到 10 的整数 gt gt gt test numpy array nump
  • 仅第一个加载的 Django 站点有效

    我最近向 stackoverflow 提交了一个问题 标题为使用mod wsgi在apache上多次请求后Django无限加载 https stackoverflow com questions 71705909 django infini
  • 使用特定颜色和抖动在箱形图上绘制数据点

    我有一个plotly graph objects Box图 我显示了箱形 图中的所有点 我需要根据数据的属性为标记着色 如下所示 我还想抖动这些点 下面未显示 Using Box我可以绘制点并抖动它们 但我不认为我可以给它们着色 fig a
  • 如何使用原始 SQL 查询实现搜索功能

    我正在创建一个由 CS50 的网络系列指导的应用程序 这要求我仅使用原始 SQL 查询而不是 ORM 我正在尝试创建一个搜索功能 用户可以在其中查找存储在数据库中的书籍列表 我希望他们能够查询 书籍 表中的 ISBN 标题 作者列 目前 它
  • 根据列 value_counts 过滤数据框(pandas)

    我是第一次尝试熊猫 我有一个包含两列的数据框 user id and string 每个 user id 可能有多个字符串 因此会多次出现在数据帧中 我想从中导出另一个数据框 一个只有那些user ids列出至少有 2 个或更多string
  • Python ImportError:无法导入名称 __init__.py

    我收到此错误 ImportError cannot import name life table from cdc life tables C Users tony OneDrive Documents Retirement retirem
  • 更改 Tk 标签小部件中单个单词的颜色

    我想更改 Tkinter 标签小部件中单个单词的字体颜色 我知道可以使用文本小部件来实现与我想要完成的类似的事情 例如使单词 YELLOW 显示为黄色 self text tag config tag yel fg clr yellow s

随机推荐