首页 > 编程语言 >Python中dataclass库

Python中dataclass库

时间:2022-09-27 13:57:25浏览次数:77  
标签:__ hash Python field init dataclass class

目录

dataclass语法

一、 简介

官方文档的地址为:https://docs.python.org/3.9/library/dataclasses.html

dataclass的定义位于PEP-557,根据定义一个dataclass是指“一个带有默认值的可变的namedtuple”,广义的定义就是有一个类,它的属性均可公开访问,可以带有默认值并能被修改,而且类中含有与这些属性相关的类方法,那么这个类就可以称为dataclass,再通俗点讲,dataclass就是一个含有数据及操作数据方法的容器。

乍一看可能会觉得这个概念不就是普通的class么,然而还是有几处不同:

  1. 相比普通class,dataclass通常不包含私有属性,数据可以直接访问
  2. dataclass的repr方法通常有固定格式,会打印出类型名以及属性名和它的值
  3. dataclass拥有__eq____hash__魔法方法
  4. dataclass有着模式单一固定的构造方式,或是需要重载运算符,而普通class通常无需这些工作

我们来创建一个实例:

from dataclasses import dataclass

@dataclass
class InventoryItem:
    """Class for keeping track of an item in inventory."""
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand

同时,我们也可以添加__init__方法:

def __init__(self, name: str, unit_price: float, quantity_on_hand: int = 0):
    self.name = name
    self.unit_price = unit_price
    self.quantity_on_hand = quantity_on_hand

同时使用dataclass也有一些好处,它比namedtuple更灵活。同时因为它是一个常规的类,所以你可以享受继承带来的便利。

二、 装饰器参数

参数为dataclass()

  • init:如果为true(默认),__init__()将生成一个方法。

    如果类已经定义__init__(),则忽略此参数。

  • repr:如果为true(默认),__repr__()将生成一个方法。生成的 repr 字符串将具有类名以及每个字段的名称和 repr,按照它们在类中定义的顺序。不包括标记为从 repr 中排除的字段。例如: 。InventoryItem(name='widget', unit_price=3.0, quantity_on_hand=10)

    如果类已经定义__repr__(),则忽略此参数。

  • eq:如果为true(默认),__eq__()将生成一个方法。此方法按顺序比较类,就好像它是其字段的元组一样。比较中的两个实例必须是相同的类型。

    如果类已经定义__eq__(),则忽略此参数。

  • order: 如果为真(默认为False),将生成__lt__()__le__()__gt__()和方法。__ge__()这些按顺序比较类,就好像它是其字段的元组一样。比较中的两个实例必须是相同的类型。如果order为真且eq为假, ValueError则引发 a。

    如果该类已经定义了__lt__()__le__()__gt__()或中的任何一个,__ge__()TypeError引发。

  • unsafe_hash:if False(默认),__hash__()根据how eqand frozenare set生成一个方法。

    __hash__()由 built-in 使用hash(),并且在将对象添加到散列集合(例如字典和集合)时使用。拥有 a __hash__()意味着类的实例是不可变的。可变性是一个复杂的属性,它取决于程序员的意图、 的存在和行为,以及装饰器中的和标志__eq__()的值。eq``frozendataclass()

    默认情况下, 除非这样做是安全的,否则dataclass()不会隐式添加方法。__hash__()它也不会添加或更改现有的明确定义的__hash__()方法。如文档中所述,设置类属性对 Python 具有特定含义。__hash__ = None__hash__()

    如果__hash__()没有显式定义,或者如果设置为None,则可以添加隐式方法。虽然不推荐,但您可以强制使用 . 如果您的类在逻辑上是不可变的,但仍然可以发生变异,则可能会出现这种情况。这是一个专门的用例,应该仔细考虑。dataclass() __hash__()dataclass()__hash__()unsafe_hash=True

    以下是管理方法隐式创建的规则__hash__() 。请注意,您不能__hash__() 在数据类和 set 中都有显式方法unsafe_hash=True;这将导致一个TypeError.

    如果eqfrozen都为真,默认情况下dataclass()会为你生成一个__hash__()方法。如果eq为真且 frozen为假,__hash__()将设置为None,将其标记为不可散列(它是,因为它是可变的)。如果eq为假, __hash__()将保持不变,这意味着__hash__() 将使用超类的方法(如果超类是 object,这意味着它将回退到基于 id 的散列)。

  • frozen:如果为真(默认为False),分配给字段将产生异常。这模拟只读冻结实例。如果 __setattr__()__delattr__()在类中定义,则 TypeError引发。

@dataclass
class C:
    ...

@dataclass()
class C:
    ...

@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)
class C:
   ...

三、 数据属性

1、 参数

参数为field()

  • default:如果提供,这将是该字段的默认值。这是必需的,因为field()调用本身会替换默认值的正常位置。

  • default_factory:如果提供,它必须是一个零参数的可调用对象,当该字段需要默认值时将被调用。除其他目的外,这可用于指定具有可变默认值的字段,如下所述。default同时指定和是错误的default_factory

  • init:如果为 true(默认值),则此字段作为参数包含在生成的__init__()方法中。

  • repr:如果为true(默认),则该字段包含在生成的__repr__()方法返回的字符串中。

  • compare: 如果为 true(默认值),则该字段包含在生成的相等和比较方法中(__eq__()、、 __gt__()等)。

  • hash: 这可以是 bool 或None. 如果为 true,则此字段包含在生成的__hash__()方法中。如果None(默认),使用compare: 这通常是预期的行为。如果某个字段用于比较,则应在哈希中考虑该字段。None不鼓励将此值设置为除此之外的任何值。

    hash=False设置的一个可能原因compare=True 是,如果一个字段计算哈希值的成本很高,则需要该字段进行相等性测试,并且还有其他字段有助于该类型的哈希值。即使某个字段从哈希中排除,它仍将用于比较。

  • metadata:这可以是映射或无。None 被视为空字典。这个值被包装 MappingProxyType()成只读的,并暴露在Field对象上。数据类根本不使用它,而是作为第三方扩展机制提供的。多个第三方可以各自拥有自己的密钥,用作元数据中的命名空间。

2、 使用示例

@dataclass
class C:
    x: int
    y: int = field(repr=False)
    z: int = field(repr=False, default=10)
    t: int = 20

3、 注意事项

init参数如果设置为False,表示不为这个field生成初始化操作,dataclass提供了hook——__post_init__供我们利用这一特性:

@dataclass
class C:
    a: int
    b: int
    c: int = field(init=False)
 
    def __post_init__(self):
        self.c = self.a + self.b

__post_init____init__后被调用,我们可以在这里初始化那些需要前置条件的field。

repr参数表示该field是否被包含进repr的输出,compare和hash参数表示field是否参与比较和计算hash值。metadata不被dataclass自身使用,通常让第三方组件从中获取某些元信息时才使用,所以我们不需要使用这一参数。

如果指定一个field的类型注解为dataclasses.InitVar,那么这个field将只会在初始化过程中(__init____post_init__)可以被使用,当初始化完成后访问该field会返回一个dataclasses.Field对象而不是field原本的值,也就是该field不再是一个可访问的数据对象。举个例子,比如一个由数据库对象,它只需要在初始化的过程中被访问:

@dataclass
class C:
    i: int
    j: int = None
    database: InitVar[DatabaseType] = None
 
    def __post_init__(self, database):
        if self.j is None and database is not None:
            self.j = database.lookup('j')
 
c = C(10, database=my_database)

四、 其他

1、 常用函数

dataclasses模块中提供了一些常用函数供我们处理数据类。

使用dataclasses.asdictdataclasses.astuple我们可以把数据类实例中的数据转换成字典或者元组:

>>> from dataclasses import asdict, astuple
>>> asdict(C())
{'name': 'python', 'strong_type': True, 'static_type': False, 'age': 28}
>>> astuple(C())
('python', True, False, 28)

使用dataclasses.is_dataclass可以判断一个类或实例对象是否是数据类

2、 继承

dataclass装饰器会检查当前class的所有基类,如果发现一个dataclass,就会把它的字段按顺序添加进当前的class,随后再处理当前class的field。所有生成的方法也将按照这一过程处理,因此如果子类中的field与基类同名,那么子类将会无条件覆盖基类。子类将会根据所有的field重新生成一个构造函数,并在其中初始化基类。

看个例子:

@dataclass
class Base:
    x: float = 25.0
    y: int = 0
 
@dataclass
class C(Base):
    z: int = 10
    x: int = 15
 
>>> C()
C(x=15, y=0, z=10)

C中的x则覆盖了Base中的定义

3、 总结

合理使用dataclass将会大大减轻开发中的负担,将我们从大量的重复劳动中解放出来,这既是dataclass的魅力,不过魅力的背后也总是有陷阱相伴,最后我想提几点注意事项:

  • dataclass通常情况下是unhashable的,因为默认生成的__hash__None,所以不能用来做字典的key,如果有这种需求,那么应该指定你的数据类为frozen dataclass
  • 小心当你定义了和dataclass生成的同名方法时会引发的问题
  • 当使用可变类型(如list)时,应该考虑使用fielddefault_factory
  • 数据类的属性都是公开的,如果你有属性只需要初始化时使用而不需要在其他时候被访问,请使用dataclasses.InitVar

只要避开这些陷阱,dataclass一定能成为提高生产力的利器。

标签:__,hash,Python,field,init,dataclass,class
From: https://www.cnblogs.com/liuzhongkun/p/16734293.html

相关文章

  • python中pydantic库
    目录pydantic库详解一、概述1、简介2、优势3、环境配置二、Model1、模型属性2、基本使用3、数据导入3.1orm3.2pickle3.3json4、数据导出三、验证器1、类内添......
  • Python学习笔记3
    problem1方程\(ax2+bx+c=0\),输入\(a,b,c\)的值,根据\(a,b,c\)的值判断不同的输出情况情况,判断并输出结果“有两个根\(x1=?\),\(x2=?\)”;“有一个根\(x=?\)”;“没有根”......
  • IPython的%魔术命令
    %magic显示所有魔术命令%hist 显示IPython命令的输入历史%pdb 异常发生后自动进入调试器%reset 删除当前命名空间中的全部变量或名称%who 显示IPython当前命......
  • 女同桌找我要表情包,还好我会Python,分分钟给她下载几十个G...
    emmm~起因呢,这昨晚女同桌跟我说电脑有点卡,喊我去宿舍给她装个新系统,装系统就装系统吧,结果又说新系统表情包都没保存~我当时就有点生气,真当我是万能的呢?于是我直接就用Py......
  • python使用paramiko实现ssh定时执行命令
    原文https://www.cnblogs.com/Rosaany/p/16093521.html#!/usr/bin/envpython3#-*-coding:utf-8-*-#@Author:Rosaanyimportfunctoolsfromparamiko.ssh_exce......
  • Python 安装
    进入Python官网选择安装包根据自己需要选择安装包,这里我选用的是Python3.10.7的完整安装包Windowsinstaller(64-bit),注意:embeddable:表示绿色免安装版本,可......
  • python时间加减
      运行结果:  ......
  • python 实现发送邮件功能
    一、前言   在开始正题之前,我们先理一下常见的电子邮件协议: SMTP、POP、IMAP都遵循TCP/IP协议规范。至于Exchange是邮件服务器,不是收邮件和发邮件的协议,不要混淆概......
  • Python多行字符串
    Sometimeswehaveaverylongstringandwewanttowriteitintomultiplelinesforbettercodereadability.Pythonprovidesvariouswaystocreatemultiline......
  • python中利用smtplib发送邮件的3中方式 普通/ssl/tls
    #!/usr/bin/python#coding:utf-8importsmtplibfromemail.MIMETextimportMIMETextfromemail.Utilsimportformatdatefromemail.HeaderimportHe......