摘要:大家好,上一期受到了朋友的啟發(fā),這一期我主要記錄一下我的的學習過程。
Dataclasses
大家好,上一期受到了朋友的啟發(fā),這一期我主要記錄一下我的Dataclasses的學習過程。
之前簡單回顧了attrs的用法,這一期來看更簡潔的自帶寫類神器:dataclasses,需要注意,版本要大于等于Python 3.7
官方文檔鏈接: Data Classes
下面直接來看例子:
from dataclasses import dataclass @dataclass class Position: name: str lon: float lat: float
可以發(fā)現(xiàn),主要起作用的是裝飾符@dataclass ,需要注意,如果想要使用dataclass,需要Python 3.7或更高版本
使用dataclass的好處是可以節(jié)省書寫__init()__等一些常用的實例方法
這里創(chuàng)建一個Position類,用來顯示一個地點的位置
name:地點的名字
lon:經(jīng)度
lat:緯度
新建一個實例來看看:
>>> 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"])默認值
讓我們看看如何給類的屬性添加默認值:
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ī)定了三個屬性的類型:
name:str
lon:float
lat:float
現(xiàn)在如果想要開放限制,允許任意的數(shù)據(jù)類型,可以這么做:
from dataclasses import dataclass from typing import Any @dataclass class WithoutExplicitTypes: name: Any value: Any = 42
這樣運行的時候不會報錯,哪怕瞎傳參:
>>> Position(3.14, "pi day", 2018) Position(name=3.14, lon="pi day", lat=2018)添加一個方法
現(xiàn)在我們想要計算兩個地點的距離,可以參考如下公式:
根據(jù)這個公式為類添加一個.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))
實驗一下:
>>> 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ù)實際需要,有哪些其他靈活的應(yīng)用方式。
現(xiàn)在創(chuàng)建兩個類,紙牌類和牌庫類,紙牌類的屬性包括花色和數(shù)字,牌庫是List類型,包含紙牌類的一些實例
from dataclasses import dataclass from typing import List @dataclass class PlayingCard: rank: str suit: str @dataclass class Deck: cards: List[PlayingCard]
現(xiàn)在向牌庫添加紅桃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)建一套完整的撲克牌牌庫,注意這里使用了符號表示花色,建議在實際環(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="?")]
理論上,我們可以把這個方法作為Deck類的初始變量,但是根本不行,因為它可變:
from dataclasses import dataclass from typing import List @dataclass class Deck: # Will NOT work cards: List[PlayingCard] = make_french_deck()
面對這種情況,dataclass的解決方案是使用default_factory函數(shù)作為field的參數(shù)來指明:
from dataclasses import dataclass, field from typing import List @dataclass class Deck: cards: List[PlayingCard] = field(default_factory=make_french_deck)
現(xiàn)在就沒有任何問題了:
>>> Deck() Deck(cards=[PlayingCard(rank="2", suit="?"), PlayingCard(rank="3", suit="?"), ... PlayingCard(rank="K", suit="?"), PlayingCard(rank="A", suit="?")])field
現(xià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
最后這個metadata有點像前端h5中的那個,就是可以為類的一個屬性添加一個額外的描述信息:
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),傳遞的是一個dict,現(xiàn)在可以使用fields來查看一個屬性的附加信息了:
>>> 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="?")...
有些過長了,讓我們用傳統(tǒng)的str或者repr來表達,首先從紙牌類PlayingCard開始:
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)在再自定義下牌庫類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
其實這里和FrozenSet有點像,無非在裝飾器中添加了一個參數(shù)frozen=True:
from dataclasses import dataclass @dataclass(frozen=True) class Position: name: str lon: float = 0.0 lat: float = 0.0
在這樣一個frozen data class中,我們不能隨意賦值:
>>> pos = Position("Oslo", 10.8, 59.9) >>> pos.name "Oslo" >>> pos.name = "Stockholm" dataclasses.FrozenInstanceError: cannot assign to field "name"繼承
使用dataclass的繼承也比較簡單,和普通類的繼承沒有太大區(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類的實例時會自動繼承了父類的屬性,我們只需要額外加入country這個新屬性就可以了
假如父類初始化時有默認值:
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簡單優(yōu)化
可以用我之前提到的slot方法進行優(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 精品練習題100道
Python 實用技巧匯總
Python Pandas教程
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/45219.html
摘要:它的目標就是在不減慢你編程速度的前提下,幫助你來編寫簡潔而又正確的代碼。對于這種情況,我們就需要有條件來控制某些屬性不能為非法值。所以,一定要在里面某個錯誤。 使用attrs解放雙手 大家好,這一期我想和大家分享一個OOP編程的高效神器:attrs庫 首先我們來介紹下 attrs 這個庫,其官方的介紹如下: attrs 是這樣的一個 Python 工具包,它能將你從繁綜復(fù)雜的實現(xiàn)上解脫...
摘要:面向?qū)ο缶幊讨镁幊淌鞘裁创蠹液茫鳛樾“祝罱鼘W習了很多編程的知識,因為腦容量有限,特此一一按照學習順序記錄下來,如果哪里有錯誤,還請大神盡快指出,以免誤導(dǎo)他人。。。繼承也允許把一個派生類的對象作為一個基類對象對待。 Python面向?qū)ο缶幊讨?OOP編程是什么 大家好,作為小白,最近學習了很多Python OOP編程的知識,因為腦容量有限,特此一一按照學習順序記錄下來,如果哪里有...
摘要:類的方法概覽首先回顧一下常見的三種方法實例接口方法類方法靜態(tài)方法標準書寫方式如下我們最常用的其實就是普通的接口方法,其他兩個需要用類似裝飾器的寫法來標注。類方法接受一個作為參數(shù),它是指向本身的,并不是所創(chuàng)建的實例。 類的方法概覽 首先回顧一下Python OOP常見的三種方法: instance method 實例/接口方法 class method 類方法 static...
摘要:時代,如果需要手動繼承,如多態(tài)多態(tài)是指,不同的子類對象調(diào)用相同的父類方法,會產(chǎn)生多態(tài)多樣結(jié)果的編程特性。 參考:黑馬程序員教程 - Python基礎(chǔ) 面向?qū)ο?OOP三大特性,且三個特性是有順序的: 封裝 繼承 多態(tài) 封裝 指的就是把現(xiàn)實世界的事務(wù),封裝、抽象成編程里的對象,包括各種屬性和方法。這個一般都很簡單,不需要多講。 唯一要注意的就是:推薦從小往大開始封裝、開發(fā)類。比如手槍...
閱讀 600·2021-10-08 10:20
閱讀 1490·2021-09-23 11:22
閱讀 3214·2019-08-30 15:55
閱讀 1583·2019-08-28 18:25
閱讀 1857·2019-08-28 18:14
閱讀 1230·2019-08-26 11:37
閱讀 2893·2019-08-26 10:18
閱讀 2420·2019-08-23 18:39