以前有寫過一篇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