前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >啃书-- 流畅的python 第五章 数据类构建器

啃书-- 流畅的python 第五章 数据类构建器

原创
作者头像
逸澄
发布2025-02-10 17:12:24
发布2025-02-10 17:12:24
7700
代码可运行
举报
文章被收录于专栏:啃书啃书
运行总次数:0
代码可运行

数据类构建器

主要介绍以下三种构建方法:

  1. collections.namedtuple
  2. typing.NameTuple
  3. @dataclass

1. collections.namedtuple

构建:

1) namedtuple创建:一个类名和一个字段名称列表。后一个参数可以是产生字符串的可迭代对象,也可以是一整个以空格分隔的字符串

2) 可以通过_make()方法创建类实例

3) 不能使用class 句法创建实例 ;不能直接添加类方法(可以绕一步);

构建字典:

collections.namedtuple._asdict() :

1) 返回namedtuple实例构建的dict对象 ;

2) 通过json.dumps(instance._asdict()) 可以dump json

访问字段:

1) _field() 返回类的字段名tuple , 注意这里不能是实例._fields , 而是 类名._fields

2) 通过实例.name 和索引范围字段

设置字段默认值:

通过default 字段设置:namedtuple('classname','attribute1 attribute2 ...',default = val1,val2,...)

代码语言:python
代码运行次数:0
复制
from collections import namedtuple
coordinate = namedtuple('cordinate','lat lon')  
moscow = coordinate(12.345,16.789)
moscow_new = coordinate(12.345,16.789)
print(moscow == moscow_new) #可以直接比较两个namedtuple instance 是否相等, 只用class 构建的类比较的是两个instance 的id而不是值;

City = namedtuple('City','name country population coordinates')
tokyo = city('Tokyo', 'JP', 36.933, (35.689722, 139.691667))  #构建方法1
print(tokyo)

#构建方法2
nxt = coordinate(33.4565,84.572)
nxt_data = ('shanghai','CH',100,nxt)
shanghai = City._make(nxt_data)
print(shanghai)

# 构建字典
print(shanghai._asdict())

#构建json
import json
json.dumps(shanghai._asdict())

#访问字段
print(city._fields) 
print(shanghai.coordinates)
print(shanghai[-1])

# 设置字段默认值
Point = namedtuple('Point','x y z',defaults=[0,1,2])
a = Point()
b = Point(1)
c = Point(1,2)
print(a,b,c)
代码语言:txt
复制
True
city(name='Tokyo', country='JP', population=36.933, coordinates=(35.689722, 139.691667))
City(name='shanghai', country='CH', population=100, coordinates=cordinate(lat=33.4565, lon=84.572))
{'name': 'shanghai', 'country': 'CH', 'population': 100, 'coordinates': cordinate(lat=33.4565, lon=84.572)}
('name', 'country', 'population', 'coordinates')
cordinate(lat=33.4565, lon=84.572)
cordinate(lat=33.4565, lon=84.572)
Point(x=0, y=1, z=2) Point(x=1, y=1, z=2) Point(x=1, y=2, z=2)

2. typing.NamedTuple

typing.NamedTuple 的主要功能在类型注解,与collections.namedtuple 的区别在于:

  1. 必须声明每个属性的类型
  2. 可以用class 句法实现,可以添加class 方法

类型注解:

  1. 功能 : python本身执行时完全不管类型, 主要服务于第三方类型检查工具 , 如PyCharm IDE 内置的类型检查器,进行静态代码检查。
  2. 变量注解的基本句法:var_name : some_type
代码语言:python
代码运行次数:0
复制
from typing import NamedTuple
class Point(NamedTuple):
    x:float
    y:float
    def __str__(self):
        if self.x>0 and self.y>0:
            return 'First quadrant'
        elif self.x<0 and self.y>0:
             return 'Second quadrant'
a = Point(8.8,9.0)
print(a)
代码语言:txt
复制
First quadrant

3. @dataclass

@dataclass 是 Python 的一个装饰器,用于自动生成类的一些常用方法,如 __init____repr____eq__ 等。不需要显式地定义这些方法,dataclass 会根据类的字段自动生成。

from dataclass import dataclass

@dataclass(*,init =True, repr=True,eq=True,order=False,unsafe_hash=False,frozen=False)

* 表示后面的都是关键字参数;

参数说明

参数

作用

默认值

备注

init

生成__init__

True

如果用户自己实现了 init,则忽略该参数

repr

生成 __repr__

True

如果用户自己实现了 __repr__,则忽略该参数

eq

生成 __eq__

True

如果用户自己实现了 _eq_,则忽略该参数

order

生成__lt____le____gt____ge__

False

设为 True 时,如果 eq=False,或者自行定义或继承其他用于比较的方法, 则抛出异常

unsafe_hash

生成__hash__ False

False

语义复杂,有多个问题需要 注意,详见 dataclass 函 数的文档

frozen

让实例不可变

False

防止意外更改实例,相对安全,但不是绝对不可变

  1. 字段选项 1) 提供类型+默认值 : 注意 python规定,带默认值的param 后面不能有无默认值的param 出现,因此带默认值的param 后面所有的param 都必须有默认值。 2) 类属性通常作为实例属性的默认值,dataclass 也是如此。 default_factory 参数值可以是一个函数、一个类,或者其他可调用对象,在每次创建数据类的实例时调用(不带参数),构建默认值。这样每个实例都会有各自的函数/类/对象,而不是所有实例共享一个函数/类/对象
  2. 初始化后处理 1) 初始化用来给属性赋值(__init__); 添加 __post_init__ 方法,@dataclass 将在生成的 __init__ 后调用 __post_init__ 。 2) __post_init__ 的作用:执行验证; 根据其它字段计算一个字段的值; 3) typing.ClassVar :用来标注类变量(而非实例变量)的一种方式。它是 typing 模块中的一部分,提供了对类型注解的支持,特别是在面向对象编程中,帮助明确区分实例变量和类变量。 5) 仅做初始化的变量 InitVar: 有时候不希望某个属性出现在所有实例对象中,例如定义一个Company 类, 其中Company 的name 从某个数据库database 中获得,但不希望这个database 出现在所有的实例对象中。则一般会使用InitVar 放在__init__ 中,在__post_init__ 处理后使用。 注意:InitVar 变量应该传递给 __post_init__ 方法,因此__post_init__ 需要接收self 和 InitVar变量; 注意不能从类中访问InitVar ,因为InitVar 不会成为类的属性,估cls.self. 是访问不到的
代码语言:python
代码运行次数:0
复制
from dataclasses import dataclass
@dataclass
class point: 
    x:float = 0.0 # 1)提供类型+默认值
    y:float = 0.0 
    def __str__(self):
        if self.x>0 and self.y>0:
            return f'{self.x},{self.y} First quadrant'
        elif self.x<0 and self.y>0:
             return f'{self.x},{self.y} Second quadrant'
        elif self.x == 0.0 and self.y == 0.0:
            return  f'{self.x},{self.y}'
    
pointA = point()
print(pointA)
        
代码语言:txt
复制
0.0,0.0
代码语言:python
代码运行次数:0
复制
from dataclasses import dataclass,field
@dataclass
class ClubMember:
    name:str 
    guests:list = field(default_factory=list) # 2.类属性用作实例属性, 用field 注册 list , 不能直接写成guests:list =[]
                                              # 每个实例都会有各自的list,而不是所有实例共享一个list
    visitors:list[str] = field(default_factory=list) # 3. 说明list的类型 是str
    athlete:bool =field(default=False,repr=False) 

@dataclass
class HackerClubMember(ClubMember):
    all_handles = set() #类属性 , 所有实例共享
    handle:str = ''     #实例字段
    def __post_init__(self):
        cls = self.__class__  #获取实例所属的类
        if self.handle == '': 
            self.handle = self.name.split()[0]  #如果是空字符串,则设置为name 的第一部分
        if self.handle in cls.all_handles: #如果self.handle 在cls.handle 中
            raise ValueError(f'handle {self.handle} already exists')
        cls.all_handles.add(self.handle) # 把新的handle 添加到cls.all_handles 中
mem1 = HackerClubMember('li haw')
print(mem1)
#mem2 = HackerClubMember('li ai') # raise error , 'li' already exists
mem2 =  HackerClubMember('li ai',handle='liu') 
代码语言:txt
复制
HackerClubMember(name='li haw', guests=[], visitors=[], handle='li')
代码语言:python
代码运行次数:0
复制
from dataclasses import dataclass
from typing import ClassVar  #从typing 中引入ClassVar
 
@dataclass
class Company:
    name:ClassVar[str] = 'tengxun'
    mem:ClassVar[list[str]] = []  #mem : 类属性,是str-list ,初始值为空list
    def add_mem(self,mem_name):
        cls = self.__class__  #获取当前实例的类
        cls.mem.append(mem_name) #类属性增加内容
Company1 = Company()
Company1.add_mem(mem_name = 'ma')
for item in iter('zhang wang li zhao'.split()):
    Company1.add_mem(item)
print(Company1.mem)
代码语言:txt
复制
['ma', 'zhang', 'wang', 'li', 'zhao']
代码语言:python
代码运行次数:0
复制
from dataclasses import dataclass,InitVar
from typing import ClassVar,Tuple

database = ('TechNova Solutions','BlueSky Innovations','Quantum Dynamics','Pinnacle Enterprises')
@dataclass
class company:
    name:str = None #实例属性
    database:InitVar[tuple[str]] = None  
    mem_num:ClassVar[int] = 0 #类属性
    # def __post_init__(self):             error : 
    #     cls = self.__class__
    #     if self.name is None and cls.database is not None:
    #         self.name = cls.database[0]   error : database 不是类属性,访问不到
    def __post_init__(self,database):
        cls = self.__class__
        if self.name is None and database is not None:
            self.name = database[0]
            

company1 = company(database=database)
print(company1)
            
代码语言:txt
复制
company(name='TechNova Solutions')

4. 模式匹配 类实例

  1. 简单类模式 case [str(name),_,_,(float(lat),float(lon))]: 第一项必须为str 实例 最后一项必须是二元组,两项均为 float 实例。 class(name) 这种模式只适用于:bytes dict float frozenset int list set str tuple
  2. 关键字类模式
  3. 位置类模式
代码语言:python
代码运行次数:0
复制
import typing
class City(typing.NamedTuple):
    continet: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', 'São Paulo', 'BR'),
]
result_Asia = []
result_America = []
#关键字匹配
for city in cities:
    match city:
        case City(continet = 'Asia'): # 注意不是 City(continet == 'Asia') ,
                                      # city 匹配  City(continet = 'Asia')  ,continet的属性值='Asia'
            result_Asia.append(city)
        case City(continet ='North America' ,country ='cc'): #匹配2个属性
            result_America.append(city)
    
print(result_Asia)
print(result_America)       

#位置类匹配
result_pos = []
for city in cities:
    match city:
        case City('Asia',_,Country):
            result_pos.append(city)
            print(Country)
print(result_pos)
代码语言:txt
复制
    [City(continet='Asia', name='Tokyo', country='JP'), City(continet='Asia', name='Delhi', country='IN')]
    []
    JP
    IN
    [City(continet='Asia', name='Tokyo', country='JP'), City(continet='Asia', name='Delhi', country='IN')]

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 数据类构建器
    • 1. collections.namedtuple
    • 2. typing.NamedTuple
    • 3. @dataclass
    • 4. 模式匹配 类实例
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档