编程语言
首页 > 编程语言> > python数据类

python数据类

作者:互联网

前言

之前有写过一篇python元类的笔记,元类主要作用就是在要创建的类中使用参数metaclass=YourMetaclass调用自定义的元类,这样就可以为所有调用了这个元类的类添加相同的属性了。

有需要查看链接:https://mp.weixin.qq.com/s/gmR_pggxeitPWKmnbo1usw

本篇笔记主要是对dataclass的特性作了解和对参考文章的总结摘要,完整文章地址:https://realpython.com/python-data-classes/

python数据类初识

用docker拉个python:3.7的镜像作为实验环境

  1. 使用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)
  1. 不使用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后有几个优势(不限于此):

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())
################# 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不同参数ranksuit而生成的类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

总结

公众号往期文章

python内置装饰器

python装饰器

scrapy-redis debug视频

scrapy-redis源码浅析

scrapy过滤重复数据和增量爬取

redis基础笔记

scrapy电影天堂实战(二)创建爬虫项目

scrapy电影天堂实战(一)创建数据库

scrapy基础笔记

在docker镜像中加入环境变量

笔记 | mongodb 入门操作

笔记 | python元类

笔记 | python2和python3使用super()

那些你在python3中可能没用到但应该用的东西

superset docker 部署

开机启动容器里面的程序

博客 | 三步部署hitchhiker-api

标签:__,...,name,python,rank,PlayingCard,suit,数据
来源: https://www.cnblogs.com/vickey-wu/p/11627486.html