python数据类
前言
之前有写过一篇python元类的笔记,元类主要作用就是在要创建的类中使用参数metaclass=YourMetaclass调用自定义的元类,这样就可以为所有调用了这个元类的类添加相同的属性了。
本篇笔记主要是对dataclass的特性作了解和对参考文章的总结摘要,完整文章地址:https://realpython.com/python-data-classes/
python数据类初识
用docker拉个python:3.7的镜像作为实验环境
- 使用dataclass装饰器创建数据类
>>> from dataclasses import dataclass
>>> @dataclass
... class DataClassTest:
... first_name: str
... last_name: str
...
>>> p = DataClassTest('vickey', 'wu')
>>> p.first_name
'vickey'
>>> p.last_name
'wu'
>>> p
DataClassTest(first_name='vickey', last_name='wu')
>>> p == DataClassTest('vickey', 'wu')
True
>>> p.first_name = 'wiki'
>>> p
DataClassTest(first_name='wiki', last_name='wu')
从上面例子可以看到,如果使用dataclass装饰器来定义数据类,则必须声明参数类型,数据类默认可以修改参数的值类型,如果不希望更改则使用@dataclass(frozen=True)即可,这样上面的 参数值就不可更改了,更改会报错dataclasses.FrozenInstanceError: cannot assign to field 'first_name'。
当不确定参数到底用哪种类型,或可以是多种类型时则可以用下面的Any来声明
>>> from dataclasses import dataclass
>>> from typing import Any
>>> @dataclass
... class W:
... n:Any
... v: float = 18
...
>>> w = W('vickey')
>>> w
W(n='vickey', v=18)
>>> w = W(19)
>>> w
W(n=19, v=18)
- 不使用dataclass装饰器的普通类
>>> class RegularClassTest:
... def __init__(self, first_name, last_name):
... self.first_name = first_name
... self.last_name = last_name
...
>>> pp = RegularClassTest('vickey', 'wu')
>>> pp.first_name
'vickey'
>>> pp.last_name
'wu'
>>> pp
<__main__.RegularClassTest object at 0x7f5f66a49550>
>>> pp == RegularClassTest('vickey', 'wu')
False
从1和2两个例子对比可以看出,使用@dataclass后有几个优势(不限于此):
- 无需定义
__init__函数,只需定义参数及参数类型即可。 - 打印出来的对象描述信息更清晰了。而未使用
dataclass的类需要再添加__repr__函数显示才友好。(看下面的例子) - 实例化后的实例可以用
==判断出是否与类实例相等,而未使用dataclass的类需要再添加__eq__函数才能判断。(看下面的例子)
3 不使用dataclass装饰器实现数据类相同功能
>>> class RegularClassTest2:
... def __init__(self, first_name, last_name):
... self.first_name = first_name
... self.last_name = last_name
... def __repr__(self):
... return (f'{self.__class__.__name__}'
... f'(first_name={self.first_name!r}, last_name={self.last_name!r})')
... def __eq__(self, other):
... if other.__class__ is not self.__class__:
... return NotImplemented
... return (self.first_name, self.last_name) == (other.first_name, other.last_name)
...
>>> r = RegularClassTest2('2', '1')
>>> r
RegularCard(first_name='2', last_name='1')
>>> r == RegularClassTest2('2', '1')
True
>>>
通过在普通类中添加__repr__和__eq__就可以具有上面提到的数据类的第2,3个优势,但还是需要__init__函数。虽然上面提到不使用dataclass也可以达到部分效果,参考文章作者也说明了各自的好处与不足,感兴趣的童鞋查看原文,这里就不记录了。
数据类参数调用函数赋值
from dataclasses import dataclass, field
from typing import List
# 数据类rank参数为牌大小,suit为花色
@dataclass
class PlayingCard:
rank: str
suit: str
# 生成13牌的4种花色
RANKS = '2 3 4 5 6 7 8 9 10 J Q K A'.split()
SUITS = '♣ ♢ ♡ ♠'.split()
def make_french_deck():
print([PlayingCard(r, s) for s in SUITS for r in RANKS])
print('################## list generated by fuction make_french_deck')
return [PlayingCard(r, s) for s in SUITS for r in RANKS]
# 参考源码typing.List
# List(yourclass):https://docs.python.org/3/library/typing.html#typing.ForwardRef
# 使用field的default_factory调用参数名为make_french_deck的函数,这个函数会生成一个list,然后赋值参数cards
@dataclass
class Deck:
cards: List[PlayingCard] = field(default_factory=make_french_deck)
print('################# called class Deck with para cards')
print(Deck())
- output
################# called class Deck with para cards
[PlayingCard(rank='2', suit='♣'), PlayingCard(rank='3', suit='♣'), PlayingCard(rank='4', suit='♣'), PlayingCard(rank='5', suit='♣'), PlayingCard(rank='6', suit='♣'), PlayingCard(rank='7', suit='♣'), PlayingCard(rank='8', suit='♣'), PlayingCard(rank='9', suit='♣'), PlayingCard(rank='10', suit='♣'), PlayingCard(rank='J', suit='♣'), PlayingCard(rank='Q', suit='♣'), PlayingCard(rank='K', suit='♣'), PlayingCard(rank='A', suit='♣'), PlayingCard(rank='2', suit='♢'), PlayingCard(rank='3', suit='♢'), PlayingCard(rank='4', suit='♢'), PlayingCard(rank='5', suit='♢'), PlayingCard(rank='6', suit='♢'), PlayingCard(rank='7', suit='♢'), PlayingCard(rank='8', suit='♢'), PlayingCard(rank='9', suit='♢'), PlayingCard(rank='10', suit='♢'), PlayingCard(rank='J', suit='♢'), PlayingCard(rank='Q', suit='♢'), PlayingCard(rank='K', suit='♢'), PlayingCard(rank='A', suit='♢'), PlayingCard(rank='2', suit='♡'), PlayingCard(rank='3', suit='♡'), PlayingCard(rank='4', suit='♡'), PlayingCard(rank='5', suit='♡'), PlayingCard(rank='6', suit='♡'), PlayingCard(rank='7', suit='♡'), PlayingCard(rank='8', suit='♡'), PlayingCard(rank='9', suit='♡'), PlayingCard(rank='10', suit='♡'), PlayingCard(rank='J', suit='♡'), PlayingCard(rank='Q', suit='♡'), PlayingCard(rank='K', suit='♡'), PlayingCard(rank='A', suit='♡'), PlayingCard(rank='2', suit='♠'), PlayingCard(rank='3', suit='♠'), PlayingCard(rank='4', suit='♠'), PlayingCard(rank='5', suit='♠'), PlayingCard(rank='6', suit='♠'), PlayingCard(rank='7', suit='♠'), PlayingCard(rank='8', suit='♠'), PlayingCard(rank='9', suit='♠'), PlayingCard(rank='10', suit='♠'), PlayingCard(rank='J', suit='♠'), PlayingCard(rank='Q', suit='♠'), PlayingCard(rank='K', suit='♠'), PlayingCard(rank='A', suit='♠')]
################## list generated by fuction make_french_deck
Deck(cards=[PlayingCard(rank='2', suit='♣'), PlayingCard(rank='3', suit='♣'), PlayingCard(rank='4', suit='♣'), PlayingCard(rank='5', suit='♣'), PlayingCard(rank='6', suit='♣'), PlayingCard(rank='7', suit='♣'), PlayingCard(rank='8', suit='♣'), PlayingCard(rank='9', suit='♣'), PlayingCard(rank='10', suit='♣'), PlayingCard(rank='J', suit='♣'), PlayingCard(rank='Q', suit='♣'), PlayingCard(rank='K', suit='♣'), PlayingCard(rank='A', suit='♣'), PlayingCard(rank='2', suit='♢'), PlayingCard(rank='3', suit='♢'), PlayingCard(rank='4', suit='♢'), PlayingCard(rank='5', suit='♢'), PlayingCard(rank='6', suit='♢'), PlayingCard(rank='7', suit='♢'), PlayingCard(rank='8', suit='♢'), PlayingCard(rank='9', suit='♢'), PlayingCard(rank='10', suit='♢'), PlayingCard(rank='J', suit='♢'), PlayingCard(rank='Q', suit='♢'), PlayingCard(rank='K', suit='♢'), PlayingCard(rank='A', suit='♢'), PlayingCard(rank='2', suit='♡'), PlayingCard(rank='3', suit='♡'), PlayingCard(rank='4', suit='♡'), PlayingCard(rank='5', suit='♡'), PlayingCard(rank='6', suit='♡'), PlayingCard(rank='7', suit='♡'), PlayingCard(rank='8', suit='♡'), PlayingCard(rank='9', suit='♡'), PlayingCard(rank='10', suit='♡'), PlayingCard(rank='J', suit='♡'), PlayingCard(rank='Q', suit='♡'), PlayingCard(rank='K', suit='♡'), PlayingCard(rank='A', suit='♡'), PlayingCard(rank='2', suit='♠'), PlayingCard(rank='3', suit='♠'), PlayingCard(rank='4', suit='♠'), PlayingCard(rank='5', suit='♠'), PlayingCard(rank='6', suit='♠'), PlayingCard(rank='7', suit='♠'), PlayingCard(rank='8', suit='♠'), PlayingCard(rank='9', suit='♠'), PlayingCard(rank='10', suit='♠'), PlayingCard(rank='J', suit='♠'), PlayingCard(rank='Q', suit='♠'), PlayingCard(rank='K', suit='♠'), PlayingCard(rank='A', suit='♠')])
上面的例子是类Deck调用了类外的一个函数make_french_deck来生成一个类Deck的列表类型参数cards,这个列表由传入类PlayingCard不同参数rank和suit而生成的类PlayingCard调用列表。这样就生成了13牌的4种花色的所有值。
数据类的继承
from dataclasses import dataclass
@dataclass
class Position:
name: str
lon: float = 0.0
lat: float = 0.0
@dataclass
class Capital(Position):
# 因为父类参数有默认值,所以子类的参数必须定义默认值,否则报错
# country: str
country: str = 'Unknown'
# 可以在子类重新定义父类的参数默认值
lat: float = 40.0
- 如果父类参数有默认值,子类的所有参数必须定义默认值,否则报错:
TypeError: non-default argument 'country' follows default argument。报错原因相当于在子类初始化时def __init__(name: str, lon: float = 0.0, lat: float = 0.0, country: str):非默认参数没有在默认参数前面,因为python规定非默认参数必须在默认参数前面。 - 参数的顺序按照父类顺序,然后子类参数顺序。
总结
- 数据类是Python3.7的新特性之一。使用数据类就不必编写样板代码来为对象获得适当的
初始化__init__,表示__repr__,和比较__eq__。 - 数据类参数必须声明参数类型,参数可以使用函数赋值。
- 在继承时如果父类参数有定义默认值,则子类参数必须也要定义默认值,继承后的参数顺序为父类参数,然后到子类参数。
- 除此之外,数据类和普通类区别不大,数据类定义参数后像普通类一样定义实例方法,一样调用。
公众号往期文章
python内置装饰器
python装饰器
scrapy-redis debug视频
scrapy-redis源码浅析
scrapy过滤重复数据和增量爬取
redis基础笔记
scrapy电影天堂实战(二)创建爬虫项目
scrapy电影天堂实战(一)创建数据库
scrapy基础笔记
在docker镜像中加入环境变量
笔记 | mongodb 入门操作
笔记 | python元类
笔记 | python2和python3使用super()
那些你在python3中可能没用到但应该用的东西
superset docker 部署
开机启动容器里面的程序
博客 | 三步部署hitchhiker-api
python数据类的更多相关文章
- python数据类型和3个重要函数
Python中所有变量都是值的引用,也就说变量通过绑定的方式指向其值. 而这里说的不可变指的是值的不可变. 对于不可变类型的变量,如果要更改变量,则会创建一个新值,把变量绑定到新值上,而旧值如果没有被 ...
- python基础——类和实例
python基础——类和实例 面向对象最重要的概念就是类(Class)和实例(Instance),必须牢记类是抽象的模板,比如Student类,而实例是根据类创建出来的一个个具体的“对象”,每个对象都 ...
- 从C#到Python —— 4 类及面向对象
http://www.cnblogs.com/yanxy/archive/2010/04/04/c2p_4.html 如果你熟悉C#,那么对类(Class)和面向对象(Object Oriented) ...
- python元类分析
刚開始接触到Python新式类中的元类的概念的时候非常是纠结了下..不知道这是个啥东西... 用下面几个定义来说明吧: (1)Python中,类也是对象..仅仅只是这样的对象比較的特殊,他用于创建别的 ...
- Python基础-类
Python基础-类 @(Python)[python, python基础] 写在前面 如非特别说明,下文均基于Python3 摘要 本文重点讲述如何创建和使用Python类,绑定方法与非绑定方法的区 ...
- Python数据可视化——使用Matplotlib创建散点图
Python数据可视化——使用Matplotlib创建散点图 2017-12-27 作者:淡水化合物 Matplotlib简述: Matplotlib是一个用于创建出高质量图表的桌面绘图包(主要是2D ...
- 【数据科学】Python数据可视化概述
注:很早之前就打算专门写一篇与Python数据可视化相关的博客,对一些基本概念和常用技巧做一个小结.今天终于有时间来完成这个计划了! 0. Python中常用的可视化工具 Python在数据科学中的地 ...
- python数据格式化之pprint
python数据格式化之pprint 2017年06月17日 13:56:33 阅读数:2291 简介 pprint模块 提供了打印出任何Python数据结构类和方法. 模块方法: 1.class p ...
- python - class类 (四) 三大特性之一 :继承
继承: #继承 #什么时候用继承? # 1.当类之间有显著的不同,并且较小的类是较大的类的所需的组建时,用组合比较好. # 2.当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好 # ...
随机推荐
- Node.js实战12:fs模块高级技巧。
通过fs模块使用流 fs模块同样有流接口,如下例: var fs = require("fs"); var read_able = fs.createReadStream(&quo ...
- [19/05/05-星期日] JDBC(Java DataBase Connectivity,java数据库连接)_mysql基本知识
一.概念 (1).是一种开放源代码的关系型数据库管理系统(RDBMS,Relational Database Management System):目前有很多大公司(新浪.京东.阿里)使用: (2). ...
- Appium+Python之批量执行测试用例
思考:当存在多个脚本,每个脚本中有多条测试用例时,我们该如何批量执行呢?分析:首先创建2个测试用例脚本(.py文件),每个脚本有2条测试用例,然后批量执行全部测试用例 #Test_01.py # co ...
- SCUT - 37 - 手速帝CZK - 分块
https://scut.online/p/37 一开始想到要根号分块预处理,但是不太懂具体怎么写.想不到如此暴力. #include<bits/stdc++.h> using names ...
- 显式Mapping设置与常见参数介绍
原文:显式Mapping设置与常见参数介绍 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/ ...
- Vue slot-scope的理解(适合初学者)
百度上已经有很多的关于slot-scope的文章,但我感觉都是那些以前没学好,又回头学的人,他们都使用了.Vue文件,我觉得有点不适合初学者,所以我就写一篇适合初学者的. 先抛例程: <!DOC ...
- zip loader
GS.ZipAssetLoader = function(audioContext) { this.audioContext = audioContext, this.objLoader = new ...
- Puppeteer实现自动登录
Puppeteer是用JS对Chrome Dev Tools的实现,可以用来操作Chrome浏览器,适用于爬虫.自动化等领域. 以下是自己实现自动化登录的代码(基于ES6) const puppete ...
- Taro -- 使用 Redux 来进行全局变量的管理
前言 Redux是JavaScript 状态容器,提供可预测化的状态管理.一般来说,规模比较大的小程序,页面状态,数据缓存,需要管理的东西太多,这时候引入Redux可以方便的管理这些状态,同一数据,一 ...
- 基于firebird的数据转存
功能:使用于相同的表从一个数据库转存到另一数据库: 方式:直连fdb并加载django,引用django的model完成: 原因:1.select * from *** 返回的数有很多None,直接i ...