簡體   English   中英

Python 如何鍵入一個返回self的方法?

[英]Python how to type anotate a method that returns self?

假設我有一個實現方法鏈接的 class:

from __future__ import annotations

class M:
    def set_width(self, width: int)->M:
        self.width = width
        return self

    def set_height(self, height: int)->M:
        self.height = height
        return self

我可以這樣使用它:

box = M().set_width(5).set_height(10)

這可行,但如果我有一個子類 M3D:

class M3D(M):
    def set_depth(self, depth: int) -> M3D:
        self.depth = depth
        return self

現在我不能這樣做:

cube = M3D().set_width(2).set_height(3).set_depth(5)

我在 mypy 中收到以下錯誤:

_test_typeanotations.py:21: error: "M" has no attribute "set_depth"; maybe "set_width"

因為set_width()返回一個沒有方法set_depthM 我已經看到建議為每個子類覆蓋set_width()set_height()以指定正確的類型,但這將是為每個方法編寫的大量代碼。 必須有一個更簡單的方法。

這也與特殊方法有關,例如__enter__傳統上返回self ,因此最好有一種方法來指定它而無需在子類中提及它。

這是使用 inheritance 的任何語言中的經典問題。 語言的處理方式不同:

  • 在 C++ 中,您將在調用set_height之前set_depth的結果
  • 在 Java 中,您可以使用與 C++ 相同的強制轉換,或者使用 IDE 來生成一堆覆蓋方法,並且只能手動更改覆蓋方法中的類型。

Python 是動態類型語言,所以沒有強制轉換指令。 所以你有3種可能的方式:

  • 勇敢的方式:重寫所有相關方法以調用基方法並在返回注釋中聲明新類型
  • 我不在乎的方式:注釋控制只給出警告。 如您所知,這條線很好,您可以忽略警告
  • 不要打擾我的方式:注釋在 Python 中是可選的,注釋控制通常可以通過特殊注釋暫停。 在這里知道沒有問題,因此您可以安全地暫停該指令或該方法的類型控制。

以下只是我的看法。

如果可能的話,我會避免不打擾的方式,因為如果您將在代碼中留下警告,那么如果有新的警告,您將不得不在每次更改后進行控制。

我不會僅僅為了擺脫警告而重寫方法。 畢竟 Python 是一種動態類型語言,甚至允許鴨子類型。 如果我知道代碼是正確的,我會避免添加無用的代碼(DRY 和 KISS 原則)

因此,我將假設暫停注釋控件的注釋是出於某種原因而發明的並使用它們(我所說的在這里不會打擾我)。

在 Python 3.11 及其更高版本中,您將能夠執行此操作:

from typing import Self

class M:
    def set_width(self, width: int) -> Self:
        self.width = width
        return self

經過大量的研究和實驗,我找到了一種在 mypy 中有效的方法,盡管 Pycham 有時仍然會猜錯類型。

訣竅是使self成為類型 var:

from __future__ import annotations

import asyncio
from typing import TypeVar

T = TypeVar('T')


class M:
    def set_width(self: T, width: int)->T:
        self.width = width
        return self

    def set_height(self: T, height: int)->T:
        self.height = height
        return self

    def copy(self)->M:
        return M().set_width(self.width).set_height(self.height)


class M3D(M):
    def set_depth(self: T, depth: int) -> T:
        self.depth = depth
        return self

box = M().set_width(5).set_height(10) # box has correct type
cube = M3D().set_width(2).set_height(3).set_depth(5) # cube has correct type
attemptToTreatBoxAsCube = M3D().copy().set_depth(4) # Mypy gets angry as expected

最后一行在 mypy 中特別有效,但set_depth有時仍會自動完成 set_depth ,即使.copy()實際上返回M ,即使在M3D上調用也是如此。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM