主頁 > 後端開發 > 使用 Mypy 檢查 30 萬行 Python 代碼,總結出 3 大痛點與 6 個技巧!

使用 Mypy 檢查 30 萬行 Python 代碼,總結出 3 大痛點與 6 個技巧!

2022-09-06 11:34:18 後端開發

作者:Charlie Marsh

譯者:豌豆花下貓@Python貓

英文:Using Mypy in production at Spring (https://notes.crmarsh.com/using-mypy-in-production-at-spring)

在 Spring ,我們維護了一個大型的 Python 單體代碼庫(英:monorepo),用上了 Mypy 最嚴格的配置項,實作了 Mypy 全覆寫,簡而言之,這意味著每個函式簽名都是帶注解的,并且不允許有隱式的 Any 轉換,

(譯注:此處的 Spring 并不是 Java 中那個著名的 Spring 框架,而是一家生物科技公司,專注于找到與年齡相關的疾病的療法,2022 年 3 月曾獲得比爾&梅琳達·蓋茨基金會 120 萬美元的資助,)

誠然,代碼行數是一個糟糕的衡量標準,但可作一個粗略的估計:我們的代碼倉有超過 30 萬行 Python 代碼,其中大約一半構成了核心的資料平臺,另一半是由資料科學家和機器學習研究員撰寫的終端用戶代碼,

我有個大膽的猜測,就這個規模而言,這是最全面的加了型別的 Python 代碼倉之一,

我們在 2019 年 7 月首次引入了 Mypy,大約一年后實作了全面的型別覆寫,從此成為了快樂的 Mypy 用戶,

幾周前,我跟 Leo Boytsov 和 Erik Bernhardsson 在 Twitter 上對 Python 型別有一次簡短的討論——然后我看到 Will McGugan 也對型別大加贊賞,由于 Mypy 是我們在 Spring 公司發布和迭代 Python 代碼的關鍵部分,我想寫一下我們在過去幾年中大規模使用它的經驗,

一句話總結:雖然采用 Mypy 是有代價的(前期和持續的投入、學習曲線等),但我發現它對于維護大型 Python 代碼庫有著不可估量的價值,Mymy 可能不適合于所有人,但它十分適合我,

Mypy 是什么?

(如果你很熟悉 Mypy,可跳過本節,)

Mypy 是 Python 的一個靜態型別檢查工具,如果你寫過 Python 3,你可能會注意到 Python 支持型別注解,像這樣:

def greeting(name: str) -> str:
    return 'Hello ' + name

Python 在 2014 年通過 PEP-484 定義了這種型別注解語法,雖然這些注解是語言的一部分,但 Python(以及相關的第一方工具)實際上并不拿它們來強制做到型別安全,

相反,型別檢查通過第三方工具來實作,Mypy 就是這樣的工具,Facebook 的 Pyre 也是這樣的工具——但就我所知,Mypy 更受歡迎(Mypy 在 GitHub 上有兩倍多的星星,它是 Pants 默認使用的工具),IntelliJ 也有自己的型別檢查工具,支持在 PyCharm 中實作型別推斷,這些工具都聲稱自己“兼容 PEP-484”,因為它們使用 Python 本身定義的型別注解,

(譯注:最著名的型別檢查工具還有谷歌的pytype 和微軟的pyright ,關于基本情況介紹與對比,可查閱這篇文章 )

換句話說:Python 認為自己的責任是定義型別注解的語法和語意(盡管 PEP-484 本身很大程度上受到了 Mypy 現有版本的啟發),但有意讓第三方工具來檢查這些語意,

請注意,當你使用像 Mypy 這樣的工具時,你是在 Python 本身之外運行它的——比如,當你運行mypy path/to/file.py 后,Mypy 會把推斷出的違規代碼都吐出來,Python 在運行時顯露但不利用那些型別注解,

(順便一提:在寫本文時,我了解到相比于 Pypy 這樣的專案,Mypy 最初有著非常不同的目標,那時還沒有 PEP-484(它的靈感來自 Mypy!),所以 Mypy 定義了自己的語法,與 Python 不同,并實作了自己的運行時(也就是說,Mypy 代碼是通過 Mypy 執行的),當時,Mypy 的目標之一是利用靜態型別、不可變性等來提高性能——而且明確地避開了與 CPython 兼容,Mypy 在 2013 年切換到兼容 Python 的語法,而 PEP-484 在 2015 年才推出,(“使用靜態型別加速 Python”的概念催生了 Mypyc,它仍然是一個活躍的專案,可用于編譯 Mypy 本身,))

在 Spring 集成 Mypy

我們在 2019 年 7 月將 Mypy 引入代碼庫(#1724),當首次發起提議時,我們有兩個主要的考慮:

  1. 雖然 Mypy 在 2012 年的 PyCon 芬蘭大會上首次亮相,并在 2015 年初發布了兼容 PEP-484 的版本,但它仍然是一個相當新的工具——至少對我們來說是這樣,盡管我們在一些相當大的 Python 代碼庫上作業過(在可汗學院和其它地方),但團隊中沒有人使用過它,
  2. 像其它增量型別檢查工具一樣(例如 Flow),隨著代碼庫的注解越來越多,Mypy 的價值會與時俱增,由于 Mypy 可以并且將會用最少的注解捕獲 bug,所以你在代碼庫上投入注解的時間越多,它就會變得越有價值,

盡管有所猶豫,我們還是決定給 Mypy 一個機會,在公司內部,我們有強烈偏好于靜態型別的工程師文化(除了 Python,我們寫了很多 Rust 和 TypeScript),所以,我們準備使用 Mypy,

我們首先型別化了一些檔案,一年后,我們完成了全部代碼的型別化(#2622),并升級到最嚴格的 Mypy 設定(最關鍵的是 disallow_untyped_defs ,它要求對所有函式簽名進行注解),從那時起,我們一直維護著這些設定,(Wolt 團隊有一篇很好的文章,他們稱之為“專業級的 Mypy 配置”,巧合的是,我們使用的正是這種配置,)

Mypy 配置:https://blog.wolt.com/engineering/2021/09/30/professional-grade-mypy-configuration/

反饋

總體而言:我對 Mypy 持積極的看法, 作為核心基礎設施的開發人員(跨服務和跨團隊使用的公共庫),我認為它極其有用,

我將在以后的任何 Python 專案中繼續使用它,

好處

Zulip 早在 2016 年寫了一篇漂亮的文章,內容關于使用 Mypy 的好處(這篇文章也被收入了 Mypy 官方檔案 中),

Zulip 博文:https://blog.zulip.com/2016/10/13/static-types-in-python-oh-mypy/#benefitsofusingmypy

我不想重述靜態型別的所有好處(它很好),但我想簡要地強調他們在帖子中提到的幾個好處:

  1. 改善可讀性:有了型別注解,代碼趨向于自描述(與檔案字串不同,這種描述的準確性可以靜態地強制執行),(英:self-documenting)
  2. 捕獲錯誤:是真的!Mypy 確實能找出 bug,從始至終,
  3. 自信地重構:這是 Mypy 最有影響力的一個好處,有了 Mypy 的廣泛覆寫,我可以自信地發布涉及數百甚至數千個檔案的更改,當然,這與上一條好處有關——我們用 Mypy 找出的大多數 bug 都是在重構時發現的,

第三點的價值怎么強調都不為過,毫不夸張地說,在 Mypy 的幫助下,我發布更改的速度快了十倍,甚至快了一百倍,

雖然這是完全主觀的,但在寫這篇文章時,我意識到:我信任 Mypy,雖然程度還不及,比如說 OCaml 編譯器,但它完全改變了我維護 Python 代碼的關系,我無法想象回到沒有注解的世界,

痛點

Zulip 的帖子同樣強調了他們在遷移 Mypy 時所經歷的痛點(與靜態代碼分析工具的互動,回圈匯入),

坦率地說,我在 Mypy 上經歷的痛點與 Zulip 文章中提到的不一樣,我把它們分成三類:

  1. 外部庫缺乏型別注解
  2. Mypy 學習曲線
  3. 對抗型別系統

讓我們來逐一回顧一下:

1. 外部庫缺乏型別注解

最重要的痛點是,我們引入的大多數第三方 Python 庫要么是無型別的,要么不兼容 PEP-561,在實踐中,這意味著對這些外部庫的參考會被決議為不兼容,這會大大削弱型別的覆寫率,

每當在環境里添加一個第三方庫時,我們都會在mypy.ini 里添加一個許可條目,它告訴 Mypy 要忽略那些模塊的型別注解(有型別或提供型別存根的庫,比較罕見):

[mypy-altair.*]
ignore_missing_imports = True

[mypy-apache_beam.*]
ignore_missing_imports = True

[mypy-bokeh.*]
ignore_missing_imports = True

...

由于有了這樣的安全出口,即使是隨便寫的注解也不會生效,例如,Mypy 允許這樣做:

import pandas as pd

def return_data_frame() -> pd.DataFrame:
    """Mypy interprets pd.DataFrame as Any, so returning a str is fine!"""
    return "Hello, world!"

除了第三方庫,我們在 Python 標準庫上也遇到了一些不順,例如,functools.lru_cache 盡管在 typeshed 里有型別注解,但由于復雜的原因,它不保留底層函式的簽名,所以任何用 @functools.lru_cache 裝飾的函式都會被移除所有型別注解,

例如,Mypy 允許這樣做:

import functools

@functools.lru_cache
def add_one(x: float) -> float:
    return x + 1

add_one("Hello, world!")

第三方庫的情況正在改善,例如,NumPy 在 1.20 版本中開始提供型別,Pandas 也有一系列公開的型別存根 ,但它們被標記為不完整的,(添加存根到這些庫是非常重要的,這是一個巨大的成就!)另外值得一提的是,我最近在 Twitter 上看到了 Wolt 的 Python 專案模板 ,它也默認包括型別,

所以,型別正在變得不再罕見,過去當我們添加一個有型別注解的依賴時,我會感到驚訝,有型別注解的庫還是少數,并未成為主流,

2. Mypy 學習曲線

大多數加入 Spring 的人沒有使用過 Mypy(寫過 Python),盡管他們基本知道并熟悉 Python 的型別注解語法,

同樣地,在面試中,候選人往往不熟悉typing 模塊,我通常在跟候選人作廣泛的技術討論時,會展示一個使用了typing.Protocol 的代碼片段,我不記得有任何候選人看到過這個特定的構造——當然,這完全沒問題!但這體現了 typing 在 Python 生態的流行程度,

所以,當我們招募團隊成員時,Mypy 往往是他們必須學習的新東西,雖然型別注解語法的基礎很簡單,但我們經常聽到這樣的問題:“為什么 Mypy 會這樣?”、“為什么 Mypy 在這里報錯?”等等,

例如,這是一個通常需要解釋的例子:

if condition:
	value: str = "Hello, world"
else:
  # Not ok -- we declared `value` as `str`, and this is `None`!
  value = https://www.cnblogs.com/pythonista/archive/2022/09/05/None

...

if condition:
	value: str ="Hello, world"
else:
  # Not ok -- we already declared the type of `value`.
  value: Optional[str] = None

...

# This is ok!
if condition:
	value: Optional[str] = "Hello, world"
else:
  value = https://www.cnblogs.com/pythonista/archive/2022/09/05/None

另外,還有一個容易混淆的例子:

from typing import Literal

def my_func(value: Literal['a', 'b']) -> None:
  ...

for value in ('a', 'b'):
	# Not ok -- `value` is `str`, not `Literal['a', 'b']`.
  my_func(value)

當解釋之后,這些例子的“原因”是有道理的,但我不可否認的是,團隊成員需要耗費時間去熟悉 Mypy,有趣的是,我們團隊中有人說 PyCharm 的型別輔助感徑訓不如在同一個 IDE 中使用 TypeScript 得到的有用和完整(即使有足夠的靜態型別),不幸的是,這只是使用 Mypy 的代價,

除了學習曲線之外,還有持續地注解函式和變數的開銷,我曾建議對某些“種類”的代碼(如探索性資料分析)放寬我們的 Mypy 規則——然而,團隊的感覺是注解是值得的,這件事很酷,

3. 對抗型別系統

在撰寫代碼時,我會盡量避免幾件事,以免導致自己與型別系統作斗爭:寫出我知道可行的代碼,并強迫 Mypy 接受,

首先是@overload ,來自typing 模塊:非常強大,但很難正確使用,當然,如果需要多載一個方法,我就會使用它——但是,就像我說的,如果可以的話,我寧可避免它,

基本原理很簡單:

@overload
def clean(s: str) -> str:
    ...

@overload
def clean(s: None) -> None:
    ...

def clean(s: Optional[str]) -> Optional[str]:
    if s:
        return s.strip().replace("\u00a0", " ")
    else:
        return None

但通常,我們想要做一些事情,比如“基于布林值回傳不同的型別,帶有默認值”,這需要這樣的技巧:

@overload
def lookup(
    paths: Iterable[str], *, strict: Literal[False]
) -> Mapping[str, Optional[str]]:
    ...


@overload
def lookup(
    paths: Iterable[str], *, strict: Literal[True]
) -> Mapping[str, str]:
    ...


@overload
def lookup(
    paths: Iterable[str]
) -> Mapping[str, Optional[str]]:
    ...


def lookup(
    paths: Iterable[str], *, strict: Literal[True, False] = False
) -> Any:
    pass

即使這是一個 hack——你不能傳一個boolfind_many_latest,你必須傳一個字面量 TrueFalse

同樣地,我也遇到過其它問題,使用 @typing.overload 或者@overload 、在類方法中使用@overload ,等等,

其次是TypedDict ,同樣來自typing 模塊:可能很有用,但往往會產生笨拙的代碼,

例如,你不能解構一個TypedDict ——它必須用字面量 key 構造——所以下方第二種寫法是行不通的:

from typing import TypedDict

class Point(TypedDict):
    x: float
    y: float

a: Point = {"x": 1, "y": 2}

# error: Expected TypedDict key to be string literal
b: Point = {**a, "y": 3}

在實踐中,很難用TypedDict物件做一些 Pythonic 的事情,我最終傾向于使用 dataclasstyping.NamedTuple 物件,

第三是裝飾器,Mypy 的 檔案 對保留簽名的裝飾器和裝飾器工廠有一個規范的建議,它很先進,但確實有效:

F = TypeVar("F", bound=Callable[..., Any])

def decorator(func: F) -> F:
    def wrapper(*args: Any, **kwargs: Any):
        return func(*args, **kwargs)

    return cast(F, wrapper)

@decorator
def f(a: int) -> str:
    return str(a)

但是,我發現使用裝飾器做任何花哨的事情(特別是不保留簽名的情況),都會導致代碼難以型別化或者充斥著強制型別轉換,

這可能是一件好事!Mypy 確實改變了我撰寫 Python 的方式:耍小聰明的代碼更難被正確地型別化,因此我盡量避免撰寫討巧的代碼,

(裝飾器的另一個問題是我前面提過的@functools.lru_cache :由于裝飾器最終定義了一個全新的函式,所以如果你不正確地注解代碼,就可能會出現嚴重而令人驚訝的錯誤,)

我對回圈匯入也有類似的感覺——由于要匯入型別作為注解使用,這就可能導致出現本可避免的回圈匯入(這也是 Zulip 團隊強調的一個痛點),雖然回圈匯入是 Mypy 的一個痛點但這通常意味著系統或代碼本身存在著設計缺陷,這是 Mypy 強迫我們去考慮的問題,

不過,根據我的經驗,即使是經驗豐富的 Mypy 用戶,在型別檢查通過之前,他們也需對本來可以正常作業的代碼進行一兩處更正,

(順便說一下:Python 3.10 使用ParamSpec 對裝飾器的情況作了重大的改進,)

提示與技巧

最后,我要介紹幾個在使用 Mypy 時很有用的技巧,

1. reveal_type

在代碼中添加reveal_type 可以讓 Mypy 在對檔案進行型別檢查時,顯示出變數的推斷型別,這是非常非常非常有用的,

最簡單的例子是:

# No need to import anything. Just call `reveal_type`.
# Your editor will flag it as an undefined reference -- just ignore that.
x = 1
reveal_type(x)  # Revealed type is "builtins.int"

當你處理泛型時,reveal_type 特別地有用,因為它可以幫助你理解泛型是如何被“填充”的、型別是否被縮小了,等等,

2. Mypy 作為一個庫

Mypy 可以用作一個運行時庫!

我們內部有一個作業流編排庫,看起來有點像 Flyte 或 Prefect,細節并不重要,但值得注意的是,它是完全型別化的——因此我們可以靜態地提升待運行任務的型別安全性,因為它們被鏈接在一起,

把型別弄準確是非常具有挑戰性的,為了確保它完好,不被意外的Any毒害,我們在一組檔案上寫了呼叫 Mypy 的單元測驗,并斷言 Mypy 拋出的錯誤能匹配一系列預期內的例外:

def test_check_function(self) -> None:
	  result = api.run(
	      [
	          os.path.join(
	              os.path.dirname(__file__),
	              "type_check_examples/function.py",
	          ),
	          "--no-incremental",
	      ],
	  )
	
	  actual = result[0].splitlines()
	  expected = [
	      # fmt: off
	      'type_check_examples/function.py:14: error: Incompatible return value type (got "str", expected "int")',  # noqa: E501
	      'type_check_examples/function.py:19: error: Missing positional argument "x" in call to "__call__" of "FunctionPipeline"',  # noqa: E501
	      'type_check_examples/function.py:22: error: Argument "x" to "__call__" of "FunctionPipeline" has incompatible type "str"; expected "int"',  # noqa: E501
	      'type_check_examples/function.py:25: note: Revealed type is "builtins.int"',  # noqa: E501
	      'type_check_examples/function.py:28: note: Revealed type is "builtins.int"',  # noqa: E501
	      'type_check_examples/function.py:34: error: Unexpected keyword argument "notify_on" for "options" of "Expression"',  # noqa: E501
	      'pipeline.py:307: note: "options" of "Expression" defined here',  # noqa: E501
	      "Found 4 errors in 1 file (checked 1 source file)",
	      # fmt: on
	  ]
	
	  self.assertEqual(actual, expected)

3. GitHub 上的問題

當搜索如何解決某個型別問題時,我經常會找到 Mypy 的 GitHub Issues (比 Stack Overflow 還多),它可能是 Mypy 型別相關問題的解決方案和 How-To 的最佳知識源頭,你會發現其核心團隊(包括 Guido)對重要問題的提示和建議,

主要的缺點是,GitHub Issue 中的每個評論僅僅是某個特定時刻的評論——2018 年的一個問題可能已經解決了,去年的一個變通方案可能有了新的最佳實踐,所以在查閱 issue 時,一定要把這一點牢記于心,

4. typing-extensions

typing 模塊在每個 Python 版本中都有很多改進,同時,還有一些特性會通過typing-extensions 模塊向后移植,

例如,雖然只使用 Python 3.8,但我們借助typing-extensions ,在前面提到的作業流編排庫中使用了3.10 版本的ParamSpec,(遺憾的是,PyCharm 似乎不支持通過typing-extensions 引入的ParamSpec 語法,并將其標記為一個錯誤,但是,還算好吧,)當然,Python 本身語法變化而出現的特性,不能通過typing-extensions 獲得,

5. NewType

typing 模塊中有很多有用的輔助物件,NewType 是我的最愛之一,

NewType 可讓你創建出不同于現有型別的型別,例如,你可以使用NewType 來定義合規的谷歌云存盤 URL,而不僅是str 型別,比如:

from typing import NewType

GCSUrl = NewType("GCSUrl", str)

def download_blob(url: GCSUrl) -> None:
    ...

# Incompatible type "str"; expected "GCSUrl"
download_blob("gs://my_bucket/foo/bar/baz.jpg")

# Ok!
download_blob(GCSUrl("gs://my_bucket/foo/bar/baz.jpg"))

通過向download_blob 的呼叫者指出它的意圖,我們使這個函式具備了自描述能力,

我發現 NewType對于將原始型別(如 strint )轉換為語意上有意義的型別特別有用,

6. 性能

Mypy 的性能并不是我們的主要問題,Mypy 將型別檢查結果保存到快取中,能加快重復呼叫的速度(據其檔案稱:“Mypy 增量地執行型別檢查,復用前一次運行的結果,以加快后續運行的速度”),

在我們最大的服務中運行 mypy,冷快取大約需要 50-60 秒,熱快取大約需要 1-2 秒,

至少有兩種方法可以加速 Mypy,這兩種方法都利用了以下的技術(我們內部沒有使用):

  1. Mypy 守護行程在后臺持續運行 Mypy,讓它在記憶體中保持快取狀態,雖然 Mypy 在運行后將結果快取到磁盤,但是守護行程確實是更快,(我們使用了一段時間的默認 Mypy 守護行程,但因共享狀態導致一些問題后,我禁用了它——我不記得具體細節了,)
  2. 共享遠程快取,如前所述,Mypy 在每次運行后都會將型別檢查結果快取到磁盤——但是如果在新機器或新容器上運行 Mypy(就像在 CI 上一樣),則不會有快取的好處,解決方案是在磁盤上預置一個最近的快取結果(即,預熱快取),Mypy 檔案概述了這個程序,但它相當復雜,具體內容取決于你自己的設定,我們最終可能會在自己的 CI 系統中啟用它——暫時還沒有去做,

結論

Mypy 對我們產生了很大的影響,提升了我們發布代碼時的信心,雖然采納它需要付出一定的成本,但我們并不后悔,

除了工具本身的價值之外,Mypy 還是一個讓人印象非常深刻的專案,我非常感謝維護者們多年來為它付出的作業,在每一個 Mypy 和 Python 版本中,我們都看到了對 typing模塊、注解語法和 Mypy 本身的顯著改進,(例如:新的聯合型別語法( X|Y)、 ParamSpecTypeAlias,這些都包含在 Python 3.10 中,)

原文發布于 2022 年 8 月 21 日,

作者:Charlie Marsh

譯者:豌豆花下貓@Python貓

英文:Using Mypy in production at Spring (https://notes.crmarsh.com/using-mypy-in-production-at-spring)

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/504292.html

標籤:其他

上一篇:Spring(三)——AOP

下一篇:Java---多執行緒入門

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more