Python是否強類型?



Answers

我認為所有現有答案都錯過了一些重要問題。

弱類型意味著允許訪問底層表示。 在C中,我可以創建一個指向字符的指針,然後告訴編譯器我想用它作為指向整數的指針:

char sz[] = "abcdefg";
int *i = (int *)sz;

在具有32位整數的小端平台上,這使得i成為數字0x646362610x00676665的數組。 實際上,你甚至可以把指針本身轉換為整數(具有適當的大小):

intptr_t i = (intptr_t)&sz;

當然這意味著我可以覆蓋系統中任何地方的內存。*

char *spam = (char *)0x12345678
spam[0] = 0;

*當然現代操作系統使用虛擬內存和頁面保護,所以我只能覆蓋我自己的進程內存,但是C本身並沒有提供這種保護,因為任何曾經編碼過的人,比如Classic Mac OS或Win16都可以告訴你。

傳統的Lisp允許類似的駭客問題; 在一些平台上,雙字浮動和黑格單元是相同的類型,你可以將一個函數傳遞給一個函數,期望另一個函數,它將“工作”。

今天的大多數語言都不如C和Lisp那麼脆弱,但其中許多語言仍然有點漏洞。 例如,任何OO語言都有一個未經檢查的“downcast”,*這是一種類型洩漏:你基本上告訴編譯器“我知道我沒有給你足夠的信息來知道這是安全的,但我很確定它是,“當一個類型系統的全部重點是編譯器總是有足夠的信息來知道什麼是安全的。

*選中downcast不會使語言的類型系統更加脆弱,因為它會將檢查移動到運行時。 如果是這樣,那麼子類型多態(又名虛擬或全動態函數調用)將與類型系統相同,我不認為有人想這樣說。

在這個意義上,很少有“腳本”語言很弱。 即使在Perl或Tcl中,也不能接受一個字符串,只是將其字節解釋為一個整數。*但值得注意的是,在CPython中(對許多其他語言的解釋器也是如此),如果你真的執著,可以使用ctypes加載libpython ,將對象的id轉換為POINTER(Py_Object) ,並強制類型系統洩漏。 這是否會導致類型系統變得不穩定取決於您的用例 - 如果您試圖實施語言上受限制的執行沙箱以確保安全性,則必須處理這些類型的轉義符......

*您可以使用像struct.unpack這樣的函數來讀取字節並從“C將如何表示這些字節”中構建一個新的int,但這顯然不是洩漏; 即使Haskell也允許這樣做。

同時,隱式轉換與弱或洩漏類型系統實際上是不同的。

每一種語言,甚至Haskell,都具有將整數轉換為字符串或浮點數的功能。 但是有些語言會自動為你做這些轉換 - 例如在C語言中,如果你調用一個需要float的函數,並且你用int傳遞它,它會被轉換。 這肯定會導致錯誤,例如意外溢出,但它們不是從弱類型系統獲得的同類錯誤。 C在這裡並沒有真正的弱點, 你可以在Haskell中添加一個int和一個float,或者甚至將一個float連接到一個字符串,你只需要更明確地做到這一點。

而動態語言,這是非常模糊的。 在Python或Perl中沒有“需要浮動的函數”這樣的東西。 但是有一些重載的函數會對不同的類型做不同的事情,並且有一種強烈的直覺,例如,將字符串添加到別的東西上是“需要字符串的函數”。 從這個意義上講,Perl,Tcl和JavaScript似乎做了很多隱式轉換( "a" + 1給你"a1" ),而Python做的很少( "a" + 1會引發異常,但是1.0 + 1確實會給你2.0 *)。 很難將這種感覺放入正式的術語中 - 為什麼不應該有一個接受一個字符串和一個int的+ ,當有明顯的其他函數(如索引)時呢?

*實際上,在現代Python中,可以用OO子類型來解釋,因為isinstance(2, numbers.Real)是真實的。 我認為沒有任何意義,其中2是Perl或JavaScript中的字符串類型的實例...儘管在Tcl中,它實際上是,因為所有東西都是字符串的實例。

最後,還有另一種完全正交的“強”與“弱”打字的定義,其中“強”意味著強大/靈活/表現力。

例如,Haskell可以讓你定義一個類型,它是一個數字,一個字符串,這種類型的列表,或者一個從字符串到這種類型的映射,這是一種完美的方式來表示任何可以從JSON解碼的東西。 沒有辦法在Java中定義這種類型。 但是至少Java有參數類型(泛型),所以你可以編寫一個函數來獲取T的列表,並且知道這些元素是T類型的; 其他語言,比如早期的Java,強迫你使用對象列表並向下。 但至少Java可以讓你用自己的方法創建新的類型; C只允許你創建結構。 BCPL甚至沒有這個。 等到裝配,其中唯一的類型是不同的位長度。

因此,從這個意義上說,Haskell的類型系統比現代Java更強大,比早期的Java更強大,比C的強於BCPL。

那麼,Python在哪些方面適合該頻譜? 這有點棘手。 在許多情況下,鴨子打字可以讓你模擬你在Haskell中可以做的所有事情,甚至可以模擬一些你不能做的事情; 當然,錯誤是在運行時而不是編譯時被捕獲的,但它們仍然被捕獲。 但是,有些情況下鴨子打字不夠。 例如,在Haskell中,你可以知道一個空的int列表是一個整數列表,所以你可以決定在列表上減少+應該返回0 *。 在Python中,空列表是一個空列表; 沒有任何類型信息可以幫助您決定應該怎麼做。

*實際上,Haskell不會讓你這麼做; 如果您調用不包含空列表的起始值的reduce函數,則會出現錯誤。 但是它的類型系統足夠強大,你可以完成這個工作,而Python不是。

Question

我遇到過說Python是強類型語言的鏈接。

但是,我認為在強類型語言中你不能這樣做:

bob = 1
bob = "bob"

我認為強類型語言在運行時不接受類型改變。 也許我對強/弱類型有一個錯誤的(或者太簡單的)定義。

那麼,Python是強類型還是弱類型語言?




TLDR;

Python的輸入是Dynamic,所以你可以將一個int變量改為一個字符串

x = 'somestring'
x = 50

Python的輸入很強,所以你不能合併類型:

'x' + 3 --> TypeError: cannot concatenate 'str' and 'int' objects

在弱類型的Javascript這發生...

 'x'+3 = 'x3'

關於類型推斷

Java迫使你顯式聲明你的對像類型

int x = 50

Kotlin使用推理來實現它是一個int

x = 50

但是因為兩種語言都使用靜態類型,所以x不能從int更改。 這兩種語言都不會允許像這樣的動態變化

x = 50
x = 'now a string'



class testme(object):
    ''' A test object '''
    def __init__(self):
        self.y = 0

def f(aTestMe1, aTestMe2):
    return aTestMe1.y + aTestMe2.y




c = testme            #get a variable to the class
c.x = 10              #add an attribute x inital value 10
c.y = 4               #change the default attribute value of y to 4

t = testme()          # declare t to be an instance object of testme
r = testme()          # declare r to be an instance object of testme

t.y = 6               # set t.y to a number
r.y = 7               # set r.y to a number

print(f(r,t))         # call function designed to operate on testme objects

r.y = "I am r.y"      # redefine r.y to be a string

print(f(r,t))         #POW!!!!  not good....

上述情況會在很長一段時間內在大型系統中造成不可維護代碼的噩夢。 把它稱為你想要的,但是“動態”改變變量類型的能力只是一個壞主意......




一個Python變量存儲對錶示該值的目標對象的無類型引用。

任何賦值操作都意味著將無類型的引用賦值給賦值對象 - 即該對象通過原始引用和新引用(計數)共享。

值類型綁定到目標對象,而不是參考值。 (強)類型檢查在具有該值的操作被執行時(運行時間)完成。

換句話說,變量(技術上)沒有類型 - 如果想要精確的話,根據變量類型來思考是沒有意義的。 但引用會自動解除引用,我們實際上會根據目標對象的類型進行思考。




根據這篇wiki Python文章,Python是動態和強類型的(也提供了一個很好的解釋)。

也許你正在考慮在程序執行期間類型不能改變的靜態類型語言,並且在編譯期間進行類型檢查以檢測可能的錯誤。

這個問題可能會引起人們的興趣: 動態類型語言與靜態類型語言以及類型系統上的這篇維基百科文章提供了更多信息




Links