Python - 数据类构建器

数据类构建器概述

class Coordinate:
  def __init__(self, lat, lon):
    self.lat = lat
    self.lon = lon

Coordinate 类的作用是保存经纬度属性,为__init__方法编写样板代码容易让人感到枯燥,尤其是属性较多的时候。想想看,每一个属性都要写3次。更糟糕的是,样板代码并没有给我们提供Python 对象都有的基本功能

>>> moscow = Corrdinate(55.76,37.62)
>>> moscow  # 莫斯科
<__main__.Corrdinate object at 0x000001DA7AC0B040>  # 1
>>> location = Corrdinate(55.76,37.62)
>>> location == moscow  # 2
False
>>> (location.lat,location.lon) == (moscow.lat,moscow.lon) # 3
True
  • 1: 继承自object 的__repr__ 作用不大
  • 2:== 没有意义,因为继承自object的__eq__方法比较对象的ID
  • 3:想要比较两个地理位置的经纬度,只能一一比较各个属性。

下面使用nammedtuple 构建Coordinate 类。namedtuple 使一个工厂方法,使用指定的名称和字段构建 tupple 子类

>>> from collections import namedtuple
>>> Corrdinate = namedtupe('Corrdinate','lat lon')
>>> issubclass(Corrdinate,tuple)
True
>>> moscow = Corrdinate(55.756,37.617)
>>> moscow
Corrdinate(lat=55.756, lon=37.617)  # 1
>>> moscow == Corrdinate(lat=55.756,lon=37.617) # 2
True
>>>
  • 1: 有用的__repr__
  • 2: 有意义的__eq__

新出现的typing.NamedTuple 具有一样的功能,不过可以为各个字段添加类型注解

>>> import typing
>>> Corrdinate = typing.NamedTuple('Corrdinate',[('lat',float),('lon',float)]) # Corrdinate = typing.NamedTuple('Corrdinate',lat=float,lon=float)。这种写法也可以可读性更高
>>> issubclass(Corrdinate,tuple)
True
>>> typing.get_type_hints(Corrdinate)
{'lat': <class 'float'>, 'lon': <class 'float'>}

从Python 3.6 开始,typing.NamedTuple 也可以在class 语句中使用,类型注解按 "PEP256 - Syntax For Variable Annotations" 标准编写。这样写出的代码可读性更高,而且方便
覆盖方法或添加新方法,下面再次定义了Corrdinate 类,经纬度属性均为float 类型,同时自定义了__str__方法。

from typing import NamedTuple

class Corrdinate(NamedTuple):
    lat: float
    lon: float

    def __str__(self):
        ns = 'N' if self.lat >=0 else 'S'
        we = 'E' if self.lon >= 0 else 'W'
        return f'{abs(self.lat):.1f}°{ns},{abs(self.lon):.1f}°{we}'

在typing.NamedTuple生成的 init 方法中,字段参数的顺序与在class语句中出现的顺序相同

与typing.NamdTuple 一样dataclass 装饰器也支持使用PEP 526句法来声明实例属性。dataclass 装饰器读取变量注解,自动为构建的类生成方法。如下例子:

from dataclasses import dataclass

@dataclass(frozen = True)
class Corrdinate:
    lat: float
    lon: float

    def __str__(self):
        ns = 'N' if self.lat >=0 else 'S'
        we = 'E' if self.lon >= 0 else 'W'
        return f'{abs(self.lat):.1f}°{ns},{abs(self.lon):.1f}°{we}'

1.可变实例

3个数据类构建起之间的主要区别在于,collections.namedtuple 和 typing.NamedTuple 构建的类是tuple 的子类,因此实例是不可变的。@datacalss 默认构造可变的类。不过@dataclass 装饰器接受一个关键字参数frozen,指定frozen=True,初始化实例之后,如果为字段赋值,则抛出异常

2.class 句法
只有typing.NamedTuple 和 dataclass 支持常规的class语句句法,方便为构建的类添加方法和文档字符串

3.构造字典
两种具名元组都提供了构造dict对象的实例方法(_dict()),可根据数据类实例的字段构造字典。dataclasses 模块也提供了构造字典的函数。即dataclasses.asdict.

namedtuple

具名元组(namedtuple) 是 python 标准库 collections 中的工厂函数。它接受两个参数,第一个参数表示类的名称,
第二个参数是类的字段名。后者可以是可迭代对象,也可以是空格隔开的字符串

>>> from collections import namedtuple
# 构建一个City类
>>> City = namedtuple('City','name country poplution')
# 实例化对象
>>> tokyo = City('Tokyo','JP',36.933)
>>> tokyo
City(name='Tokyo', country='JP', poplution=36.933)
# 访问实例属性
>>> tokyo.poplution
36.933
>>> tokyo[1]
'JP'

除了从普通元组继承来的属性之外,具名元组还有一些自己专有的属性:

# 获得类的字段名
>>> City._fields
('name', 'country', 'poplution')
>>> bj_data = ('beijing','china','36.666')
# 接收一个可迭代对象新建一个city对象
>>> bj = City._make(bj_data)
# 以dict形式返回属性和值
>>> bj._asdict()
{'name': 'beijing', 'country': 'china', 'poplution': '36.666'}

# Python 3.7 开始namedtuple接受defaults关键字参数,值为一个产生N项的可迭代对象,为从右数的N个字段指定默认值
>>> City = namedtuple('City','name country poplution',defaults=['1111'])
>>> City('shenzhen','china')
City(name='shenzhen', country='china', poplution='1111')

NamedTuple

from typing import NamedTuple

class Coordinate(NamedTuple):
    lat:float  # 1
    lon:float 
    reference: str = 'WGS84' # 2
  • 1 每个实例字段都要注解类型
  • 2 实例字段reference 注解了类型,还指定了默认值。

使用 typing.NameTuple 构建的类,拥有的方法并不比collections.namedtuple 生成的更多,而且同样也从tuple 继承方法。
唯一的区别是多了类属性__annotations__,在运行时,Python完全忽略该属性

类型提示入门

类型提示(也叫类型注解)声明函数参数、返回值、变量和属性的预期类型

关于类型提示,首先你要知道,Python字节码编译器和解释器根本不强制你提供类型信息

运行时没有作用

from typing import NamedTuple

class Coordinate(NamedTuple):
    lat:float  # 1
    lon:float
    reference: str = 'WGS84' # 2

c = Coordinate('str1','str2')
print(c) # Coordinate(lat='str1', lon='str2', reference='WGS84')

运行后,发现并没有报错也不会发出警告

类型提示主要为第三方类型检查工具提供支持,例如Mypy 和 Pycahrm IDE内置的类型检查器
这些静态分析工具,在“静态”状态下检查Python 源码,并不运行代码

如下,安装mypy 后检查 Coordinate,可以看到有提示信息:

变量注解的语法

基本语法如下:
var_name: some_type

最常使用以下类型:

  • 一个具体类,例如:str 或 Student
  • 一个参数化容器类,例如: list[int],tuple[str,float]等。
  • typing.Optional,例如Optional[str],声明一个字段类型可以str 或 None

指定初始化值:
var_name: some_type = a_value

变量注解的意义

类型提示在运行时没有作用。然而 Python 在导入时(加载模块时)会读取类型提示,构建__annotations__字典
供typing.NamedTuple和@dataclass使用,增强类功能

# demo3.py
class DemoPlainClass:
    a: int   # 1
    b: float = 1.1  # 2
    c = 'spam'  # 3

演示:

>>> from demo3 import DemoPlainClass
>>> DemoPlainClass.__annotations__
{'a': <class 'int'>, 'b': <class 'float'>}
>>> DemoPlainClass.a 
Traceback (most recent call last):                               
  File "<stdin>", line 1, in <module>                            
AttributeError: type object 'DemoPlainClass' has no attribute 'a'
>>> DemoPlainClass.b
1.1
>>> DemoPlainClass.c
'spam'

1.a出现在__annotations__中,但被抛弃了,因为该类没有名为a的属性
2.b作为注解记录在案,而且是一个类属性,值为1.1
3.c是普通的类属性,没有注解

注意,特殊属性__annotations__由解释器创建,记录源码中出现的类型提示,即使是普通的类。a只作为注解存在,不是类属性,因为没有绑定值。b和c存储为类属性,因为它们绑定了值。 这3个属性都不出现在DemoPlainClass的实例中。使用 o= DemoPlainClass() 创建一个对象,o.a 抛出AttributeErroer,而o.b和o.c 检索类属性,值分别为1.1 和 'spam',行为与一个常规的Python对象相同

模式匹配类实例

类模式通过类型和属性(可选)匹配类实例。类模式的匹配对象可以是任何类的实例,而不仅仅是数据类的实例

类模式有3中变体:简单类模式、关键字类模式和位置类模式

简单类模式

关键字类模式

import typing

class City(typing.NamedTuple):
    continent: str
    name: str
    country: str

cities = [
    City('Asia','Tokyo','JP'),
    City('Asia','Delhi','IN'),
    City('North America','Mexico City','MX'),
    City('North America','New York','US'),
    City('South America','Sao Paulo','BR')
]

def match_asian_cities():
    results = []
    for city in cities:
        match city:
            case City(continent='Asia'):
                results.append(city)
    return results

print(match_asian_cities())

#out: [City(continent='Asia', name='Tokyo', country='JP'), City(continent='Asia', name='Delhi', country='IN')]
posted @   chuangzhou  阅读(155)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示