Python 3.7+中的数据类探索

130 阅读6分钟

Python 3.7+中的数据类

Python类的主要功能是包含和表示数据。在以前的Python版本中,__init__() 函数在类的实例上存储数值。这是一个常见的模式,带来了一个问题;为什么我们必须明确地将每个参数存储在对象上?好吧,在 Python 3.7 中,我们不需要。

一个叫做数据类的新特性被引入,它解决了这个问题。数据类有助于自动创建和管理那些主要为了保存数据而存在的类。

前提条件

要开始使用数据类,你必须安装Python 3.7 或更新的版本,因为以前的版本不支持它。

为了更好地理解这篇文章,读者还应该能够使用Python类和对象。

定义一个数据类

一个Python类是一个用于创建对象及其属性的程序。我们使用关键字class ,在Python中创建一个类。

对于要初始化的类属性,我们使用一个叫做__init__() 的构造方法,当在Python类中创建一个对象时,这个方法被调用。

让我们来实现一个Python类。

class Student:
    def __init__(self, name, course, gpa):
        self.name = name
        self.course = course
        self.gpa = gpa

# create instances
student1 = Student('James', 'Comp Science', 3.7)
student2 = Student('Angie', 'Bcom', 4.0)

print(student1.course)
print(student2.name)

在上面的类中,我们的__init__() 方法接收了四个参数。self 关键字代表该类的实例,并允许我们访问该类中定义的任何属性。我们的属性,在这种情况下,是name,course, 和gpa

我们使用self 和属性一起访问其实例。例如,self.name 可以访问name属性。然后我们创建两个对象student1student2 ,并创建一个我们的类的实例。然后我们把它们打印出来。

这就是我们在以前的 Python 版本中定义类的方式。在 Python 3.7 + 中,引入了数据类来简化类的创建。让我们来研究一个例子,看看有什么变化。

from dataclasses import dataclass

@dataclass
class Student:
    name: str
    course: str
    gpa: float

# create instances
student1 = Student('James', 'Comp Science', 3.7)
student2 = Student('Angie', 'Bcom', 4.0)

#access fields
print(student1.course)
print(student2.name)

# print student1 itself
print(student1)

我们首先导入dataclass 。我们用导入的数据类中的@dataclass 装饰器为我们的类打头阵。在我们的类中,我们用它们的数据类型定义我们的字段。

例如,namecourse 是字符串。我们用str 来表示它们的数据类型。我们的gpa 将是一个小数,所以我们使用float

通过运行dataclass() 装饰器,该类的属性由一个内置的__init__() 函数初始化。输出结果与前面例子中的类似。

让我们在我们的类中添加另一个方法,看看我们如何能改变字段。

from dataclasses import dataclass

@dataclass
class Student:
    name: str
    course: str
    gpa: float

    def studentinfo(self):
        return f"{self.name} had {self.gpa}"

# create instances
student1 = Student('James', 'Comp Science', 3.7)
student2 = Student('Angie', 'Bcom', 4.0)

#access fields
print(student1.course)
print(student2.name)

# print student1 itself
print(student1)

# Change some fields
student1.name = "Clark"
student1.gpa = 3.9

print(student1.studentinfo())

我们已经创建了一个方法,studentinfo() ,它返回一个格式化的名字和gpa,作为一个字符串。我们在一个数据类中定义一个方法,就像其他的方法一样。

name 然后我们改变student1 和他的gpa 的属性。然后我们使用studentinfo() 方法打印第一个学生的学生信息。我们可以看到输出的变化。

这表明我们如何使用Python数据类来改变各种字段。接下来,我们将看一下如何使用__post_init__() 函数。

使用后置初始化

__post_init__() 函数允许我们在对象初始化时定制我们不想要的额外属性。

让我们继续看看这是如何工作的。

from dataclasses import dataclass

@dataclass
class Student:
    name: str
    course: str
    gpa: float

    def __post_init__(self):
        self.description = f"{self.name} from {self.course} has {self.gpa} gpa"

#create instances
student1 = Student("James", "Comp Science", 3.7)
student2 = Student("Angie", "Bcom", 4.0)

print(student1.description)
print(student2.description)

我们传递一个新的属性,description__post_init__() 函数。我们使用已经由__init__() 函数定义的属性来创建一个格式化的字符串。

使用我们已经创建的变量;student1student2 我们执行__post_init__() 函数。当你想给你的数据类添加或定制属性时,使用后初始化是很有用的。

使用默认值

数据类还允许我们在声明属性时定义它们的默认值。下面的片段展示了如何去做这件事。

from dataclasses import dataclass, field
import random

def gpa_range():
    return float(random.randrange(2, 5))

@dataclass
class Student:
    name: str = "No Name"
    course: str = "No Course"
    gpa: float = field(default_factory=gpa_range)

# create a default student object
student1 = Student()
print(student1)

# Create a specified book, gpa is set by field operator
student1 = Student('James', 'Comp Science')
student2 = Student('Angie', 'Bcom')

print(student1)
print(student2)

在我们的类对象中,我们通过使用等号和相应的数据类型来表示默认值。我们为字符串数据类型传递"No Name""No Course" ,并为gpa属性传递一个带有定义函数的字段gpa_range() ,以生成随机的GPA值。

default_factory 允许我们使用field() 指定器来使用可变的默认值。gpa_range() 函数生成2到5之间的随机值。

然后,我们通过传递namecourse 属性来创建一个学生类的实例。gpa 属性是由我们导入的字段操作符设置的。运行该文件会输出一个具有所有属性的学生对象。

不可变的数据类

不可变的数据类是不允许我们改变其值的类,并且在修改时抛出一个异常。为什么要使用不可变的类?

嗯,首先,不可变的类是线程安全的,因为它们的内容不能被改变。它还可以提高阅读代码时的清晰度和准确性。下面的片段解释了如何去做这件事。

from dataclasses import dataclass

@dataclass(frozen=True)
class ImmutableDataClass:
    val1: str = "This value"
    val2: int = 0

obj = ImmutableDataClass()
print(obj.val1)

obj.val1 = "Another value"
print(obj.val1)

我们创建一个新的类,ImmutableDataClass() ,并定义了两个属性:val1val2 。我们给这两个属性指定默认值:"This value"0 。我们创建一个变量obj ,并打印val1"This value"

如果我们试图修改val1 ,给它分配另一个值," Another value" ,我们会得到一个异常,FrozenInstanceError

这是因为这个类是不可改变的。在我们的装饰器中,我们把Frozen=True 传递给dataclass() 装饰器。这使得Python对象是不可变的。

冻结的类也不能修改自己。例如,ImmutableDataClass 中的func() 函数也不能被修改。

试着运行下面的片段。

from dataclasses import dataclass

@dataclass(frozen=True)
class ImmutableDataClass:
    val1: str = "This value"
    val2: int = 0

    def func(self, newval):
        self.val2 = newval


obj = ImmutableDataClass()
print(obj.val1)

obj.func(12)
print(obj.func)

总结

在本教程中,我们已经介绍了如何使用数据类来减少开发以数据为中心的对象所需的垃圾代码量。