国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

受 Rust 啟發,是時候改變 Python 編程方式了

開發 前端
Rust 并沒有構造函數。相反,人們傾向于使用普通函數來創建(最好是正確初始化的)結構體實例。在 Python 中,沒有構造函數重載的概念,所以如果你需要以多種方式構造一個對象,通常會產生一個帶有許多參數的方法,這些參數以不同的方式用于初始化,并不能一起使用。

近年來,Rust因安全性受到科技公司青睞。其他主流語言能否借鑒Rust的思想?

在Rust中,錯誤使用接口會導致編譯錯誤。在Python中,雖然錯誤代碼仍能運行,但使用類型檢查器(如pyright)或帶類型分析的IDE(如PyCharm)可以獲得快速反饋,發現潛在問題。

本文中,Python 中引入了 Rust 的一些理念:盡量使用類型提示,遵循“非法狀態不可表示”原則。無論是長期維護的程序還是一次性腳本,我都這樣做,因為后者往往會變成前者,而這種方法讓程序更易理解和修改。

本文將展示一些應用此方法的Python示例,雖然不算高深,但記錄下來或許有用。

類型提示

首先要盡可能使用類型提示,尤其是在函數說明和類屬性中。當我看到這樣的函數說明。

def find_item(records, check):

從函數說明本身來看,我完全不知道其中發生了什么:是列表、字典還是數據庫連接?是布爾值還是函數?函數的返回值是什么?如果失敗會發生什么?是拋出異常還是返回某個值?要找到這些問題的答案,我要么必須讀取函數的主體(通常還要遞歸讀取它調用的其他函數的主體,這非常煩人),要么只能讀取它的文檔(如果有的話)。雖然文檔中可能包含有關函數的有用信息,但不一定要使用文檔來回答前面的問題。許多問題都可以通過內置機制(即類型提示)來回答。

def find_item(
    records: List[Item],
    check: Callable[[Item], bool]
) -> Optional[Item]:

寫函數說明要花更多時間嗎?是的。

但這有問題嗎?沒有,除非我打字速度慢到每分鐘只能敲幾個字,但這很少見。明確寫出類型能讓我更清楚地思考函數到底提供了什么接口,以及如何讓接口更嚴格,避免調用者用錯。有了清晰的函數說明,我一眼就能知道怎么用這個函數、需要傳什么參數、返回值是什么。而且,和文檔注釋不同,文檔注釋容易過時,但類型檢查器會在類型變化時提醒我更新調用代碼。如果我想了解某個東西的類型,直接看就行,非常直觀。

當然,我也不是死板的人。如果一個參數的類型提示要嵌套五層,我通常會放棄,改用簡單但不那么精確的類型。根據我的經驗,這種情況很少見。如果真的遇到,那可能是代碼設計有問題——如果一個參數既可以是數字、字符串、字符元組,又可以是字典映射字符串到整數,那可能意味著你需要重構和簡化代碼了。

使用數據類而非元組或字典

使用類型提示只是一方面,它只是描述了函數的接口,第二步是盡可能準確地 “鎖定 ”這些接口。一個典型的例子是從函數返回多個值(或單個復雜值),懶惰而快速的方法是返回一個元組:

def find_person(…) -> Tuple[str, str, int]:

我們知道要返回三個值,但它們是什么?第一個字符串是人名嗎?第二個是姓氏嗎?數字是年齡、位置還是社保號?這種編碼方式很不透明,除非看函數內部,否則根本不知道它代表什么。

如果想改進,可以返回一個字典:

def find_person(...) -> Dict[str, Any]:
    ...
    return {
        "name": ...,
        "city": ...,
        "age": ...
    }

現在,我們至少能知道返回的屬性是什么,但還是得看函數內部才能確定。某種程度上,類型變得更糟了,因為我們甚至不知道屬性的數量和類型。而且,當函數變化時,比如字典的鍵被重命名或刪除,類型檢查器很難發現,調用者只能通過運行-崩潰-修改的繁瑣循環來調整代碼。

正確的解決方案是返回一個強類型的對象,并帶有命名的參數。在Python中,這意味著要創建一個類。我猜很多人用元組或字典是因為定義一個類(還得給它起名字)比直接返回數據麻煩得多。但從Python 3.7開始(或者用polyfill包支持更早的版本),有了更簡單的解決方案:dataclasses

@dataclasses.dataclass
class City:
    name: str
    zip_code: int

@dataclasses.dataclass
class Person:
    name: str
    city: City
    age: int

def find_person(...) -> Person:

雖然還是得給類起名字,但除此之外,這種方式非常簡潔,而且所有屬性都有類型注解。

通過這個數據類,函數的返回值變得非常明確。當我調用這個函數并處理返回值時,IDE的自動補全功能會顯示屬性的名稱和類型。這聽起來可能很小,但對我來說,這是提高效率的一大優勢。此外,當代碼重構或屬性變化時,IDE和類型檢查器會提醒我,并顯示需要修改的地方,而不需要運行程序。對于一些簡單的重構(比如屬性重命名),IDE甚至可以自動完成這些更改。更重要的是,通過明確命名的類型,我可以建立一個共享的詞匯表(比如PersonCity),并與其他函數和類共用。

代數數據類型

Rust 有一個大多數主流語言缺乏的強大功能:代數數據類型(ADT)。它能明確描述數據的形狀。比如處理數據包時,可以枚舉所有可能的類型并為每種類型分配不同字段:

enum Packet {
    Header { protocol: Protocol, size: usize },
    Payload { data: Vec<u8> },
    Trailer { data: Vec<u8>, checksum: usize }
}

通過模式匹配,可以處理每種情況,編譯器會檢查是否遺漏了任何可能:

fn handle_packet(packet: Packet) {
    match packet {
        Packet::Header { protocol, size } => ...,
        Packet::Payload { data } | Packet::Trailer { data, ... } => println!("{data:?}")
    }
}

ADT 能確保無效狀態不可表示,避免運行時錯誤。它在靜態類型語言中特別有用,尤其是當需要統一處理一組類型時。如果沒有 ADT,通常需要用接口或繼承來實現。如果類型集是封閉的,ADT 和模式匹配是更好的選擇。

在 Python 這樣的動態類型語言中,雖然不需要為類型集設置共享名稱,但類似 ADT 的結構仍然有用。比如可以用聯合類型:

@dataclass
class Header:
    protocol: Protocol
    size: int

@dataclass
class Payload:
    data: str

@dataclass
class Trailer:
    data: str
    checksum: int

Packet = Header | Payload | Trailer  # Python 3.10+

Packet 類型可以表示 HeaderPayloadTrailer。雖然這些類沒有明確的標識符來區分,但可以通過 isinstance 或模式匹配來處理:

def handle_packet(packet: Packet):
    match packet:
        case Header(protocol, size): print(f"header {protocol} {size}")
        case Payload(data): print("payload {data}")
        case Trailer(data, checksum): print(f"trailer {checksum} {data}")
        case _: assert False

在 Rust 中,遺漏情況會導致編譯錯誤,而在 Python 中需要用 assert False 來處理意外數據。

聯合類型的好處是它在類之外定義,減少了代碼耦合。同一個類可以用于多個聯合類型:

Packet = Header | Payload | Trailer
PacketWithData = Payload | Trailer

聯合類型對自動序列化也非常有用。比如使用 pyserde 庫,可以輕松序列化和反序列化聯合類型:

import serde

Packet = Header | Payload | Trailer
@dataclass
class Data:
    packet: Packet

serialized = serde.to_dict(Data(packet=Trailer(data="foo", checksum=42)))
# {'packet': {'Trailer': {'data': 'foo', 'checksum': 42}}}

deserialized = serde.from_dict(Data, serialized)
# Data(packet=Trailer(data='foo', checksum=42))

聯合類型還可以用于版本化配置,保持向后兼容性:

Config = ConfigV1 | ConfigV2 | ConfigV3

通過反序列化,可以讀取所有舊版本的配置格式。

使用 NewType

在 Rust 中,定義不添加任何新行為的數據類型很常見,但這些數據類型用于指定其他常見數據類型(如整數)的域和預期用途。這種模式被稱為 NewType,例如 Python 中也有這種模式:

class Database:
    def get_car_id(self, brand: str) -> int:
    def get_driver_id(self, name: str) -> int:
    def get_ride_info(self, car_id: int, driver_id: int) -> RideInfo:

db = Database()car_id = db.get_car_id("Mazda")
driver_id = db.get_driver_id("Stig")
info = db.get_ride_info(driver_id, car_id)

發現錯誤?

函數 get_ride_info 的參數位置顛倒了。由于汽車 ID 和駕駛員 ID 都是簡單整數,因此類型是正確的,盡管函數調用在語義上是錯誤的。

我們可以通過使用 NewType 為不同類型的 ID 定義不同的類型來解決這個問題:

from typing import NewType
from typing import NewType

# Define a new type called "CarId", which is internally an `int`
CarId = NewType("CarId", int)

# Ditto for "DriverId"
DriverId = NewType("DriverId", int)

class Database:
    def get_car_id(self, brand: str) -> CarId:
    def get_driver_id(self, name: str) -> DriverId:
    def get_ride_info(self, car_id: CarId, driver_id: DriverId) -> RideInfo:

db = Database()
car_id = db.get_car_id("Mazda")
driver_id = db.get_driver_id("Stig")

# Type error here -> DriverId used instead of CarId and vice-versa
info = db.get_ride_info(<error>driver_id</error>, <error>car_id</error>)

這是一個非常簡單的模式,可以幫助捕捉那些難以發現的錯誤,尤其是在處理許多不同類型的 ID 和某些指標混合在一起時。

使用構造函數

Rust 并沒有構造函數。相反,人們傾向于使用普通函數來創建(最好是正確初始化的)結構體實例。在 Python 中,沒有構造函數重載的概念,所以如果你需要以多種方式構造一個對象,通常會產生一個帶有許多參數的方法,這些參數以不同的方式用于初始化,并不能一起使用。

相反,我喜歡創建具有明確名稱的 “構造函數”,這樣就可以清楚地知道對象是如何構造的,以及是通過哪些數據構造的:

class Rectangle: 
    @staticmethod
    def from_x1x2y1y2(x1: float, ...) -> "Rectangle":
    
    @staticmethod
    def from_tl_and_size(top: float, left: float, width: float, height: float) -> "Rectangle":

這樣做可以使對象的構造更加清晰,不允許用戶傳遞無效數據,并能更清楚地表達構造對象的意圖。

寫在最后

總之,我確信我的 Python 代碼中還有更多的 “完整模式”,但以上是我目前能想到的全部。歡迎討論!

責任編輯:武曉燕 來源: 數據STUDIO
相關推薦

2018-10-18 09:58:41

物聯網IOT數字化

2020-08-11 08:55:42

VSCode開發代碼

2016-12-29 11:18:05

2017-04-18 18:59:04

2022-03-02 09:49:14

Rust編程語言

2023-10-19 15:25:40

2019-08-27 08:45:10

Python編程語言代碼

2017-09-15 18:16:56

人工智能Python

2017-02-17 07:46:29

2018-08-21 05:12:10

2024-01-02 07:34:38

CentOSLinuxRedhat

2021-10-28 19:10:51

RustPythonjs

2024-04-07 00:00:01

TypeScript語言REST

2013-06-05 13:49:41

EclipseIntelliJ

2015-06-15 11:05:13

DCIM數據中心

2022-07-06 23:28:53

元宇宙Web3.0

2021-10-09 14:35:20

物聯網IOT人工智能

2019-11-27 14:27:33

編程語言PythonJava

2021-09-24 09:15:19

Windowsfx 1LinuxWindows 11

2013-07-11 15:24:09

App Store應用商店外媒
點贊
收藏

51CTO技術棧公眾號

一区二区三区日韩在线观看| 久久精品成人一区二区三区蜜臀| 精华区一区二区三区| 黑人巨大精品欧美一区二区三区| 亚洲国产成人自拍| 国产在线视频2019最新视频| 91破解版在线看| 福利一区二区在线观看| 国产精品日韩在线观看| 中文字幕乱码在线播放| a级精品国产片在线观看| 国产美女久久精品| 亚洲老司机网| 日韩精品一二三四区| 1769在线观看| 99精品欧美一区二区三区综合在线| www.久久艹| 国产精品地址| 2023亚洲男人天堂| 国产一区二区主播在线| 一区二区三区毛片| 三级视频中文字幕| 91免费国产视频网站| 国产91xxx| 精品无码三级在线观看视频 | 国产日韩欧美成人| 国产精品极品在线观看| 欧美剧情片在线观看| 网友自拍视频在线| 欧美精品1区2区3区| 亚洲电影天堂av | 色鬼7777久久| 欧美中文字幕一区| 免费yellow网站| 一区二区三区四区精品在线视频| 人人做人人爽| 欧美喷水一区二区| segui88久久综合9999| 日韩福利视频在线观看| 国产欧美一区二区三区精品酒店| 亚洲少妇激情视频| 国产精品一区免费在线 | 欧美日韩性生活片| 中文字幕亚洲在| 午夜在线小视频| 尤物九九久久国产精品的分类 | 成人免费av| 欧洲成人免费视频| 欧美日韩视频| 免费黄色特级片| 欧美日韩国产麻豆| 亚洲三级电影| 91黄在线观看| 亚欧精品一区| 影音先锋中文字幕一区| 久久久成人精品一区二区三区| 国产欧美日产一区| 爱看av在线| 日本不卡高字幕在线2019| 精品在线观看视频| 在线观看黄av| 亚洲第一福利网| 农村少妇一区二区三区四区五区| 成人在线资源网址| 国产欧美一区二区三区在线看蜜臀 | 欧美男男tv网站在线播放| 国产999精品| 国产美女精品人人做人人爽 | 国产精品久久久久久久久免费高清| 久久中文精品视频| 国产在线精品一区二区| 亚乱亚乱亚洲乱妇| 国产精品爱啪在线线免费观看| 国产成人免费高清| 天堂中文8资源在线8| 97国产精品视频人人做人人爱| 国产91综合一区在线观看| 韩国版免费三体| 色在人av网站天堂精品| 久久99国产精品尤物| 欧美videos极品另类| 91老司机在线| 亚洲老妇xxxxxx| 久久97精品| 久久婷婷综合色| 久久久久久久久久久免费| av网站免费线看精品| 亚洲精品福利| 成人av免费在线看| 国产精品亚洲一区二区三区在线| 91精品视频免费| 亚久久调教视频| 一区二区三区国产福利| 影音先锋久久资源网| 国产精品久久久久77777| 蜜桃精品wwwmitaows| 国产三级精品网站| 亚洲综合久久久| 欧美r片在线| 亚洲午夜av久久乱码| 性网站在线看| 777午夜精品免费视频| 麻豆中文一区二区| 一区视频网站| 91精品久久久久久9s密挑| 国产欧美韩国高清| 91精品国产欧美一区二区18 | 黄色网址在线免费播放| 日韩电影中文字幕一区| 亚洲作爱视频| 四虎精品永久免费| 亚洲福利二区| 香蕉久久夜色| 日韩免费观看网站| 欧美日韩高清影院| 亚洲国产精品v| 99亚洲一区二区| 日韩视频在线直播| 福利视频一二区| 成人免费视频网| 欧美男插女视频| 欧美精品亚洲二区| 国产精品久久久久久久久免费樱桃| 欧美久久一级| 台湾色综合娱乐中文网| 99在线播放| xxxx69视频| 久久久久久久久久久久久久国产| 日本日本精品二区免费| 成人a在线观看| 色婷婷av一区二区三区久久| 欧美日本一区二区三区| 一区二区理论电影在线观看| 国产人成一区二区三区影院| 国产不卡视频一区| 日韩精品一二三四| 91p九色成人| 日本wwwwwwwzzzzz视频| 懂色av一区二区三区四区五区| 国产一区二区无遮挡| 久久久精品影院| 欧美视频一区二区三区| 国产成人免费av在线| 国产成人短视频在线观看| 综合亚洲自拍| 希岛爱理av一区二区三区| 亚洲国产日韩欧美在线| 欧美在线亚洲| 国内综合精品午夜久久资源| 欧美丰满日韩| 欧美人成在线| 国产欧美丝祙| 精品一区二区三区日韩| 国产精品一区二区黑丝| 成人性生交大片免费看中文| 99国产精品国产精品毛片| 亚洲国产精品av| 亚洲韩国精品一区| 国产成人午夜精品影院观看视频| 久久99精品久久只有精品| 国产99久久久精品| 国产视频不卡一区| 91成人在线免费观看| 日韩激情av在线播放| 国模精品视频一区二区三区| 亚洲午夜激情免费视频| 色多多国产成人永久免费网站| 亚洲国产精品999| 国语自产在线不卡| 久久99精品久久久久久久久久 | 精品盗摄一区二区三区| 欧美老女人性视频| 95av在线视频| 国产日产欧美一区二区| 视频国产一区二区三区| 欧美黑人激情| 国产盗摄一区二区| 国产欧美日韩影院| 丁香婷婷综合色啪| 在线观看亚洲精品视频| 久久久久久久久电影| 免费观看成人高| 亚洲高清成人影院| 久久精品 人人爱| 日韩av系列| 99精品欧美一区二区蜜桃免费| 日韩欧美中文免费| 欧美国产日本高清在线 | 欧美图区在线视频| 欧美亚洲国产视频小说| 国产一区二区网| 羞羞小视频视频| 精品欧美日韩精品| 欧美三级乱码| 久久99九九99精品| 欧美成人性战久久| 国产成人亚洲精品| 欧美成人精品欧美一级乱| 日本在线视频www鲁啊鲁| 粉嫩一区二区三区在线观看|