摘要:大家好,上一期受到了朋友的啟發(fā),這一期我主要記錄一下我的的學(xué)習(xí)過(guò)程。
Dataclasses
大家好,上一期受到了朋友的啟發(fā),這一期我主要記錄一下我的Dataclasses的學(xué)習(xí)過(guò)程。
之前簡(jiǎn)單回顧了attrs的用法,這一期來(lái)看更簡(jiǎn)潔的自帶寫(xiě)類神器:dataclasses,需要注意,版本要大于等于Python 3.7
官方文檔鏈接: Data Classes
下面直接來(lái)看例子:
from dataclasses import dataclass @dataclass class Position: name: str lon: float lat: float
可以發(fā)現(xiàn),主要起作用的是裝飾符@dataclass ,需要注意,如果想要使用dataclass,需要Python 3.7或更高版本
使用dataclass的好處是可以節(jié)省書(shū)寫(xiě)__init()__等一些常用的實(shí)例方法
這里創(chuàng)建一個(gè)Position類,用來(lái)顯示一個(gè)地點(diǎn)的位置
name:地點(diǎn)的名字
lon:經(jīng)度
lat:緯度
新建一個(gè)實(shí)例來(lái)看看:
>>> pos = Position("Oslo", 10.8, 59.9) >>> print(pos) Position(name="Oslo", lon=10.8, lat=59.9) >>> pos.lat 59.9 >>> print(f"{pos.name} is at {pos.lat}°N, {pos.lon}°E") Oslo is at 59.9°N, 10.8°E
除了這種方法,還要一種類似創(chuàng)建namedtuple的方式也可以:
from dataclasses import make_dataclass Position = make_dataclass("Position", ["name", "lat", "lon"])默認(rèn)值
讓我們看看如何給類的屬性添加默認(rèn)值:
from dataclasses import dataclass @dataclass class Position: name: str lon: float = 0.0 lat: float = 0.0
效果和普通的類設(shè)定初始值的效果是一樣的:
>>> Position("Null Island") Position(name="Null Island", lon=0.0, lat=0.0) >>> Position("Greenwich", lat=51.8) Position(name="Greenwich", lon=0.0, lat=51.8) >>> Position("Vancouver", -123.1, 49.3) Position(name="Vancouver", lon=-123.1, lat=49.3)輸入提示
大家可以發(fā)現(xiàn)我們的Positon類規(guī)定了三個(gè)屬性的類型:
name:str
lon:float
lat:float
現(xiàn)在如果想要開(kāi)放限制,允許任意的數(shù)據(jù)類型,可以這么做:
from dataclasses import dataclass from typing import Any @dataclass class WithoutExplicitTypes: name: Any value: Any = 42
這樣運(yùn)行的時(shí)候不會(huì)報(bào)錯(cuò),哪怕瞎傳參:
>>> Position(3.14, "pi day", 2018) Position(name=3.14, lon="pi day", lat=2018)添加一個(gè)方法
現(xiàn)在我們想要計(jì)算兩個(gè)地點(diǎn)的距離,可以參考如下公式:
根據(jù)這個(gè)公式為類添加一個(gè).distance_to()方法
from dataclasses import dataclass from math import asin, cos, radians, sin, sqrt @dataclass class Position: name: str lon: float = 0.0 lat: float = 0.0 def distance_to(self, other): r = 6371 # Earth radius in kilometers lam_1, lam_2 = radians(self.lon), radians(other.lon) phi_1, phi_2 = radians(self.lat), radians(other.lat) h = (sin((phi_2 - phi_1) / 2)**2 + cos(phi_1) * cos(phi_2) * sin((lam_2 - lam_1) / 2)**2) return 2 * r * asin(sqrt(h))
實(shí)驗(yàn)一下:
>>> oslo = Position("Oslo", 10.8, 59.9) >>> vancouver = Position("Vancouver", -123.1, 49.3) >>> oslo.distance_to(vancouver) 7181.7841229421165更加靈活的應(yīng)用
目前為止我們已經(jīng)看到了dataclass的基礎(chǔ)用法,現(xiàn)在我們看看根據(jù)實(shí)際需要,有哪些其他靈活的應(yīng)用方式。
現(xiàn)在創(chuàng)建兩個(gè)類,紙牌類和牌庫(kù)類,紙牌類的屬性包括花色和數(shù)字,牌庫(kù)是List類型,包含紙牌類的一些實(shí)例
from dataclasses import dataclass from typing import List @dataclass class PlayingCard: rank: str suit: str @dataclass class Deck: cards: List[PlayingCard]
現(xiàn)在向牌庫(kù)添加紅桃Q和黑桃A的操作可以這樣:
>>> queen_of_hearts = PlayingCard("Q", "Hearts") >>> ace_of_spades = PlayingCard("A", "Spades") >>> two_cards = Deck([queen_of_hearts, ace_of_spades]) Deck(cards=[PlayingCard(rank="Q", suit="Hearts"), PlayingCard(rank="A", suit="Spades")])
現(xiàn)在我們可以創(chuàng)建一套完整的撲克牌牌庫(kù),注意這里使用了符號(hào)表示花色,建議在實(shí)際環(huán)境中改換為字符串:
RANKS = "2 3 4 5 6 7 8 9 10 J Q K A".split() SUITS = "? ? ? ?".split() def make_french_deck(): return [PlayingCard(r, s) for s in SUITS for r in RANKS] >>> make_french_deck() [PlayingCard(rank="2", suit="?"), PlayingCard(rank="3", suit="?"), ... PlayingCard(rank="K", suit="?"), PlayingCard(rank="A", suit="?")]
理論上,我們可以把這個(gè)方法作為Deck類的初始變量,但是根本不行,因?yàn)樗勺儯?/p>
from dataclasses import dataclass from typing import List @dataclass class Deck: # Will NOT work cards: List[PlayingCard] = make_french_deck()
面對(duì)這種情況,dataclass的解決方案是使用default_factory函數(shù)作為field的參數(shù)來(lái)指明:
from dataclasses import dataclass, field from typing import List @dataclass class Deck: cards: List[PlayingCard] = field(default_factory=make_french_deck)
現(xiàn)在就沒(méi)有任何問(wèn)題了:
>>> Deck() Deck(cards=[PlayingCard(rank="2", suit="?"), PlayingCard(rank="3", suit="?"), ... PlayingCard(rank="K", suit="?"), PlayingCard(rank="A", suit="?")])field
現(xiàn)在簡(jiǎn)單總結(jié)一下dataclass中使用field涉及到的關(guān)鍵參數(shù):
default: Default value of the field
default_factory: Function that returns the initial value of the field
init: Use field in .__init__() method? (Default is True.)
repr: Use field in repr of the object? (Default is True.)
compare: Include the field in comparisons? (Default is True.)
hash: Include the field when calculating hash()? (Default is to use the same as for compare.)
metadata: A mapping with information about the field
最后這個(gè)metadata有點(diǎn)像前端h5中的那個(gè),就是可以為類的一個(gè)屬性添加一個(gè)額外的描述信息:
from dataclasses import dataclass, field @dataclass class Position: name: str lon: float = field(default=0.0, metadata={"unit": "degrees"}) lat: float = field(default=0.0, metadata={"unit": "degrees"})
可以發(fā)現(xiàn),傳遞的是一個(gè)dict,現(xiàn)在可以使用fields來(lái)查看一個(gè)屬性的附加信息了:
>>> from dataclasses import fields >>> fields(Position) (Field(name="name",type=描述類(str,repr),...,metadata={}), Field(name="lon",type= ,...,metadata={"unit": "degrees"}), Field(name="lat",type= ,...,metadata={"unit": "degrees"})) >>> lat_unit = fields(Position)[2].metadata["unit"] >>> lat_unit "degrees"
這里指的就是常用的repr(obj)和str(obj)
先看一下剛才的Deck()描述:
>>> Deck() Deck(cards=[PlayingCard(rank="2", suit="?"), PlayingCard(rank="3", suit="?"), PlayingCard(rank="4", suit="?"), PlayingCard(rank="5", suit="?")...
有些過(guò)長(zhǎng)了,讓我們用傳統(tǒng)的str或者repr來(lái)表達(dá),首先從紙牌類PlayingCard開(kāi)始:
from dataclasses import dataclass @dataclass class PlayingCard: rank: str suit: str def __str__(self): return f"{self.suit}{self.rank}"
>>> ace_of_spades = PlayingCard("A", "?") >>> ace_of_spades PlayingCard(rank="A", suit="?") >>> print(ace_of_spades) ?A
現(xiàn)在看上去好多了,現(xiàn)在再自定義下牌庫(kù)類Deck的描述:
from dataclasses import dataclass, field from typing import List @dataclass class Deck: cards: List[PlayingCard] = field(default_factory=make_french_deck) def __repr__(self): cards = ", ".join(f"{c!s}" for c in self.cards) return f"{self.__class__.__name__}({cards})"
這回看上去舒服多了:
>>> Deck() Deck(?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?J, ?Q, ?K, ?A, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?J, ?Q, ?K, ?A, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?J, ?Q, ?K, ?A, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?J, ?Q, ?K, ?A)不可修改的Dataclass
其實(shí)這里和FrozenSet有點(diǎn)像,無(wú)非在裝飾器中添加了一個(gè)參數(shù)frozen=True:
from dataclasses import dataclass @dataclass(frozen=True) class Position: name: str lon: float = 0.0 lat: float = 0.0
在這樣一個(gè)frozen data class中,我們不能隨意賦值:
>>> pos = Position("Oslo", 10.8, 59.9) >>> pos.name "Oslo" >>> pos.name = "Stockholm" dataclasses.FrozenInstanceError: cannot assign to field "name"繼承
使用dataclass的繼承也比較簡(jiǎn)單,和普通類的繼承沒(méi)有太大區(qū)別:
from dataclasses import dataclass @dataclass class Position: name: str lon: float lat: float @dataclass class Capital(Position): country: str
>>> Capital("Oslo", 10.8, 59.9, "Norway") Capital(name="Oslo", lon=10.8, lat=59.9, country="Norway")
這里可以發(fā)現(xiàn),創(chuàng)建Capital類的實(shí)例時(shí)會(huì)自動(dòng)繼承了父類的屬性,我們只需要額外加入country這個(gè)新屬性就可以了
假如父類初始化時(shí)有默認(rèn)值:
from dataclasses import dataclass @dataclass class Position: name: str lon: float = 0.0 lat: float = 0.0 @dataclass class Capital(Position): country: str = "Unknown" lat: float = 40.0簡(jiǎn)單優(yōu)化
可以用我之前提到的slot方法進(jìn)行優(yōu)化:
from dataclasses import dataclass @dataclass class SimplePosition: name: str lon: float lat: float @dataclass class SlotPosition: __slots__ = ["name", "lon", "lat"] name: str lon: float lat: float總結(jié)
這次我記錄了dataclass的基礎(chǔ)用法,是參考了別人的文章,如果想要了解更多,還是要到官方文檔去看
我的其他原創(chuàng)文章已經(jīng)放到了Github上,如果感興趣的朋友可以去看看,鏈接如下:
Python 精品練習(xí)題100道
Python 實(shí)用技巧匯總
Python Pandas教程
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/45219.html
摘要:它的目標(biāo)就是在不減慢你編程速度的前提下,幫助你來(lái)編寫(xiě)簡(jiǎn)潔而又正確的代碼。對(duì)于這種情況,我們就需要有條件來(lái)控制某些屬性不能為非法值。所以,一定要在里面某個(gè)錯(cuò)誤。 使用attrs解放雙手 大家好,這一期我想和大家分享一個(gè)OOP編程的高效神器:attrs庫(kù) 首先我們來(lái)介紹下 attrs 這個(gè)庫(kù),其官方的介紹如下: attrs 是這樣的一個(gè) Python 工具包,它能將你從繁綜復(fù)雜的實(shí)現(xiàn)上解脫...
摘要:面向?qū)ο缶幊讨镁幊淌鞘裁创蠹液茫鳛樾“祝罱鼘W(xué)習(xí)了很多編程的知識(shí),因?yàn)槟X容量有限,特此一一按照學(xué)習(xí)順序記錄下來(lái),如果哪里有錯(cuò)誤,還請(qǐng)大神盡快指出,以免誤導(dǎo)他人。。。繼承也允許把一個(gè)派生類的對(duì)象作為一個(gè)基類對(duì)象對(duì)待。 Python面向?qū)ο缶幊讨?OOP編程是什么 大家好,作為小白,最近學(xué)習(xí)了很多Python OOP編程的知識(shí),因?yàn)槟X容量有限,特此一一按照學(xué)習(xí)順序記錄下來(lái),如果哪里有...
摘要:類的方法概覽首先回顧一下常見(jiàn)的三種方法實(shí)例接口方法類方法靜態(tài)方法標(biāo)準(zhǔn)書(shū)寫(xiě)方式如下我們最常用的其實(shí)就是普通的接口方法,其他兩個(gè)需要用類似裝飾器的寫(xiě)法來(lái)標(biāo)注。類方法接受一個(gè)作為參數(shù),它是指向本身的,并不是所創(chuàng)建的實(shí)例。 類的方法概覽 首先回顧一下Python OOP常見(jiàn)的三種方法: instance method 實(shí)例/接口方法 class method 類方法 static...
摘要:時(shí)代,如果需要手動(dòng)繼承,如多態(tài)多態(tài)是指,不同的子類對(duì)象調(diào)用相同的父類方法,會(huì)產(chǎn)生多態(tài)多樣結(jié)果的編程特性。 參考:黑馬程序員教程 - Python基礎(chǔ) 面向?qū)ο?OOP三大特性,且三個(gè)特性是有順序的: 封裝 繼承 多態(tài) 封裝 指的就是把現(xiàn)實(shí)世界的事務(wù),封裝、抽象成編程里的對(duì)象,包括各種屬性和方法。這個(gè)一般都很簡(jiǎn)單,不需要多講。 唯一要注意的就是:推薦從小往大開(kāi)始封裝、開(kāi)發(fā)類。比如手槍...
閱讀 606·2021-10-08 10:20
閱讀 1495·2021-09-23 11:22
閱讀 3223·2019-08-30 15:55
閱讀 1603·2019-08-28 18:25
閱讀 1866·2019-08-28 18:14
閱讀 1240·2019-08-26 11:37
閱讀 2902·2019-08-26 10:18
閱讀 2428·2019-08-23 18:39