簡體   English   中英

Python類型提示友好類型,約束可能的值

[英]Python type-hint friendly type that constrains possible values

我想要一個python類型提示友好的方式來創建一個具有約束值范圍的類型。

例如,基於類型strURL類型只接受看起來像“http”URL的字符串。

# this code is made up and will not compile
class URL(typing.NewType('_URL', str)):
    def __init__(self, value: str, *args, **kwargs):
        if not (value.startswith('http://') or value.startswith('https://')):
            raise ValueError('string is not an acceptable URL')

覆蓋內置的不可變類型效果很好

超越str ; http URL字符串

這是一個覆蓋str的示例。 這不需要typing模塊,但仍然可以使用類型提示。

這個str派生類聲明初始化的字符串看起來像一個http URL字符串。

class URL(str):
    def __new__(cls, *value):
        if value:
            v0 = value[0]
            if not type(v0) is str:
                raise TypeError('Unexpected type for URL: "%s"' % type(v0))
            if not (v0.startswith('http://') or v0.startswith('https://')):
                raise ValueError('Passed string value "%s" is not an'
                                 ' "http*://" URL' % (v0,))
        # else allow None to be passed. This allows an "empty" URL instance, e.g. `URL()`
        # `URL()` evaluates False

        return str.__new__(cls, *value)

這導致一個類只允許一些字符串。 否則,它的行為就像一個不可變的str實例。

# these are okay
URL()
URL('http://example.com')
URL('https://example.com')
URL('https://')

# these raise ValueError
URL('example')  # ValueError: Passed string value "example" is not an "http*://" URL
URL('')  # ValueError: Passed string value "" is not an "http*://" URL

# these evaluate as you would expect
for url in (URL(),  # 'False'
            URL('https://'),  # 'True'
            URL('https://example.com'),  # 'True'
           ):
    print('True') if url else print('False')

(更新:后來我找到了purl Python庫)

另一個例子,

重寫int ; 約束整數范圍Number

int派生類僅允許值19包括19

這也有一個特殊功能。 如果實例初始化為nothing( Number() ),則該值等於0 (此行為派生自int類)。 在這種情況下, __str__應該是'.' (計划要求)。

class Number(int):
    """integer type with constraints; part of a Sudoku game"""

    MIN = 1  # minimum
    MAX = 9  # maximum

    def __new__(cls, *value):
        if value:
            v0 = int(value[0])
            if not (cls.MIN <= v0 <= cls.MAX):
                raise ValueError('Bad value "%s" is not acceptable in'
                                 ' Sudoku' % (v0,))
        # else:
        #    allow None to be passed. This allows an "empty" Number instance that
        #    evaluates False, e.g. `Number()`

        return int.__new__(cls, *value)

    def __str__(self):
        """print the Number accounting for an "empty" value"""
        if self == 0:
            return '.'
        return int.__str__(self)

這可以確保盡快處理錯誤的輸入。 否則,它的行為就像一個int

# these are okay
Number(1)
Number(9)
Number('9')

# this will evaluate True, just like an int
Number(9) == int(9)
Number('9') == int(9)
Number('9') == float(9)

# this is okay, it will evaluate False
Number()
print('True') if Number() else print('False')  # 'False'

# these raise ValueError
Number(0)  # ValueError: Bad value "0" is not acceptable in Sudoku
Number(11)  # ValueError: Bad value "11" is not acceptable in Sudoku
Number('11')  # ValueError: Bad value "11" is not acceptable in Sudoku

而特殊的“功能”

print(Number(1)) # '1' (expected)
print(Number())  # '.' (special feature)




繼承不可變類型的技術來自這個SO答案

子類化內置類型可能導致一些奇怪的情況(考慮完全檢查type(...) is str代碼type(...) is str

這是一種純類型方法,類型安全並完全保留字符串的類型:

from typing import NewType

_Url = NewType('_Url', str)

def URL(s: str) -> _Url:
    if not s.startswith('https://'):
        raise AssertionError(s)
    return _Url(s)

print(type(URL('https://example.com')) is str)  # prints `True`

這里的方法“隱藏”運行時檢查一個函數,它看起來像一個api視角的構造函數,但實際上只是一個很小的類型 (我找不到規范的“微小類型”,這似乎只是我能找到的最好的資源)。

暫無
暫無

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

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