以前有写过一篇python元类的笔记,元类主要做用就是在要建立的类中使用参数metaclass=YourMetaclass
调用自定义的元类,这样就能够为全部调用了这个元类的类添加相同的属性了。html
有须要查看连接:https://mp.weixin.qq.com/s/gmR_pggxeitPWKmnbo1uswpython
本篇笔记主要是对dataclass
的特性做了解和对参考文章的总结摘要,完整文章地址:https://realpython.com/python-data-classes/redis
用docker拉个python:3.7的镜像做为实验环境mongodb
>>> 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'
。docker
当不肯定参数到底用哪一种类型,或能够是多种类型时则能够用下面的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)
>>> 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
后有几个优点(不限于此):api
__init__
函数,只需定义参数及参数类型便可。dataclass
的类须要再添加__repr__
函数显示才友好。(看下面的例子)==
判断出是否与类实例相等,而未使用dataclass
的类须要再添加__eq__
函数才能判断。(看下面的例子)3 不使用dataclass装饰器实现数据类相同功能scrapy
>>> 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
不一样参数rank
和suit
而生成的类PlayingCard
调用列表。这样就生成了13牌的4种花色的全部值。ui
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规定非默认参数必须在默认参数前面。初始化__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