Python是强类型语言吗?

345

我看到过一些链接说Python是一种强类型语言。

然而,我认为在强类型语言中你不能这样做:

bob = 1
bob = "bob"

我原以为强类型语言不允许在运行时更改类型。也许我的定义过于简单或错误。

那么,Python是强类型语言还是弱类型语言?

13个回答

532

Python的类型是强类型和动态类型。

  • 强类型指的是一个值的类型不会在意外情况下发生改变。例如,一个只包含数字的字符串不会像Perl中一样自动变成数字类型。每次类型转换都需要明确地进行。
  • 动态类型意味着运行时的对象(值)有一个类型,而不是像静态类型那样变量有一个类型。

至于你的例子:

bob = 1
bob = "bob"
这是因为变量没有类型限制,可以命名任何对象。在bob=1之后,您会发现type(bob)返回int,但在bob="bob"之后,它返回str。(请注意type是一个常规函数,它评估其参数,然后返回值的类型。)
与旧版C语言的弱静态类型不同,指针和整数基本上是可互换的。 (现代ISO C在许多情况下需要转换,但默认情况下我的编译器仍然很宽松。)
我必须补充说,强类型与弱类型更像是一个连续体而不是一个布尔选择。C ++比C具有更强的类型(需要更多的转换),但使用指针强制转换可以破坏类型系统。
在像Python这样的动态语言中,类型系统的强度实际上取决于其基元和库函数对不同类型的响应方式。例如,+被重载,使其适用于两个数字或两个字符串,但不适用于字符串和数字。这是实现+时做出的设计选择,但并不是根据语言语义的必要性。事实上,当您在自定义类型上重载+时,可以使其将任何内容隐式转换为数字。
def to_number(x):
    """Try to convert function argument to float-type object."""
    try: 
        return float(x) 
    except (TypeError, ValueError): 
        return 0 

class Foo:
    def __init__(self, number): 
        self.number = number

    def __add__(self, other):
        return self.number + to_number(other)

Foo的实例可以添加到其他对象中:

>>> a = Foo(42)
>>> a + "1"
43.0
>>> a + Foo
42
>>> a + 1
43.0
>>> a + None
42

请注意,虽然强类型的Python允许将intfloat类型的对象相加,并返回float类型的对象(例如:int(42) + float(1)返回43.0),但是由于Haskell中存在类型不匹配的问题,如果尝试执行以下操作(42 :: Integer) + (1 :: Float),Haskell会报错。这使得Haskell成为一种严格类型的语言,其中类型完全不相交,并且只有通过类型类进行控制形式的重载。


28
有一个例子我并不经常看到,但我认为它很重要,可以展示Python并非完全强类型,那就是所有被评估为布尔值的事物:http://docs.python.org/release/2.5.2/lib/truth.html - gsgx
36
不太确定这是否是反例:物品可以被评估为布尔值,但它们并不会突然“变成”布尔值。这就好像有人会隐式地调用类似于as_boolean(<value>)的东西,这与对象本身的类型改变不同,对吗? - jbrendel
23
在布尔上下文中的“真值”并不是反例,因为实际上没有将任何内容转换为TrueFalse。但数字提升呢?在Python中,“1.0 + 2”和在Perl或C中一样都可以正常工作,即使“'1.0'+2”不行。我同意@jbrendel的看法,这并不是真正的隐式转换,而是重载——但同样地,Perl也没有进行任何隐式转换。如果函数没有声明参数类型,则隐式转换就没有发生的地方。 - abarnert
26
更好的理解强类型的方式是,当对一个变量执行操作时,类型很重要。如果类型与预期不符,会发出警告的语言是强类型(例如Python/Java),而不会发出警告的则是弱类型(例如JavaScript)。动态类型语言(例如Python)允许在运行时更改变量的类型,而静态类型语言(例如Java)一旦声明变量就不允许更改其类型。 - keios
4
@gsingh2011,“Truthiness”本身并不是“弱类型”,它很有用,但是一个偶然的if isValid(value) - 1可能会出现问题。布尔型被转换为整数,然后被评估为“真值”。 False - 1变成了“真值”,而True - 1则变成了“假值”,导致一个令人尴尬的、难以调试的两层偏移量错误。从这个意义上讲,Python基本上是“强类型”的;类型转换通常不会导致逻辑错误。 - Aaron3468
显示剩余7条评论

85

我认为所有现有答案都忽略了一些重要问题。


弱类型意味着允许访问底层表示。在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 也允许类似的 hackery。
大多数现代语言不像 C 和 Lisp 那样脆弱,但其中许多仍然存在缺陷。例如,任何具有未经检查的“向下转换”的面向对象语言都会有一个类型漏洞:你基本上是在告诉编译器,“我知道我没有给你足够的信息来知道这是安全的,但我很确定它是安全的”,而类型系统的整个目的就是让编译器始终具备足够的信息来知道什么是安全的。
非常少量的“脚本”语言在这方面是脆弱的。即使在 Perl 或 Tcl 中,你也不能将字符串作为整数来解释。但值得注意的是,在 CPython(以及其他许多语言的类似解释器)中,如果你真的很坚持,你可以使用 ctypes 加载 libpython,将对象的 id 强制转换为 POINTER(Py_Object),并强制泄漏类型系统。是否让类型系统变得脆弱取决于你的用例——如果你尝试实现一种语言内受限制的执行沙盒以确保安全性,那么你必须处理这些逃逸问题。
与此同时,隐式转换真的是与弱或脆弱类型系统有所不同的事情。 每种语言,甚至包括 Haskell,都具有将整数转换为字符串或浮点数的函数。但有些语言会自动为你执行其中一些转换,例如在 C 中,如果调用一个想要 float 的函数,并且你传入 int,它会为你进行转换。这肯定会导致错误,例如意外的溢出,但这不是从弱类型系统中获得的相同类型的错误。C 在这里并没有变得更加脆弱;你可以在 Haskell 中添加 int 和 float,甚至将 float 连接到字符串中,你只需要更加明确地表述它。
对于动态语言来说,这非常模糊不清。在 Python 或 Perl 中,不存在“想要 float 的函数”。但是有重载函数,对于不同的类型做出不同的操作,而且有很强的直觉感觉,例如将字符串添加到其他东西上就是“想要字符串的函数”。在这个意义上,Perl、Tcl 和 JavaScript 看起来会执行许多隐式转换("a" + 1 会给你 "a1"),而 Python 执行的则较少("a" + 1 会引发异常,但 1.0 + 1 会给你 2.0)。只是很难用正式的术语来表达这种感觉——为什么不应该有一个将字符串和 int 一起使用的 +,当然还有其他函数,例如索引,可以使用呢?
最后,还有另一个与“强”和“弱”类型无关的定义,其中“强”意味着强大/灵活/富于表现力。 例如,Haskell 允许定义一个类型,该类型可以是数字、字符串、此类型的列表或从字符串到此类型的映射,这是表示可以从 JSON 解码的任何内容的完美方式。在 Java 中没有办法定义这样的类型。但至少 Java 具有参数化(泛型)类型,因此您可以编写一个接受

4
这个答案很棒!可惜它在列表底部沉寂了这么久。 - LeoR
4
针对你的C语言示例,我有一个小评论:char sz[] 并不是指向字符的指针,而是字符数组,在赋值操作中会退化为指针。 - majkel.mk
在2021年,这仍然是一个出色的答案! - Michael Lee

83

简述;

Python语言的类型是动态的,所以你可以将一个字符串变量更改为整数(在静态语言中不行)。

x = 'somestring'
x = 50

Python的类型是强类型的,因此您无法合并类型:

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

在弱类型的 Javascript 中,发生了这种情况...

 'foo'+3 = 'foo3'

关于类型推断

有些语言(如Java)强制要求你显式声明对象类型。

int x = 50;

其他像Kotlin一样,只是从值本身推断出它是一个int

x = 50

但由于两种语言都使用了 静态 类型,x 无法从一个 int 类型变为另一种类型。任何一种语言都不能允许进行 动态 变化。

x = 50
x = 'now a string'

我不了解Javascript的细节,但'x' + 3可能是通过operator+重载来实现类型转换的吧? - VimNing
16
无论如何,你的回答实际上比上面的回答更简明易懂。 - VimNing
3
JavaScript开发者在此,最好的解释! - Wenfang Du
1
可以说,int / int = float 的存在(以及一般情况下,int 几乎可以在任何 float 可以使用的地方使用)使得 Python3 在类型上略显薄弱。 - o11c

46

您把强类型动态类型搞混了。

我不能通过添加字符串'12'来改变1的类型,但是我可以选择在程序运行时存储什么类型,并更改其类型。

静态类型的相反是动态类型;变量类型的声明在程序生命周期内不会改变。弱类型的相反是强类型;的类型在程序生命周期内可以改变。


链接中的描述是强类型的:“通常,强类型语言在编译时具有更严格的类型规则,这意味着在编译期间更容易发生错误和异常。” 这意味着Python是一种弱类型语言... 那么维基百科是错的吗? - VimNing
1
@s̮̦̩e̝͓c̮͔̞ṛ̖̖e̬̣̦t̸͉̥̳̼:这一点并不是暗示Python是弱类型语言。Python在编译时有严格的类型规则,每个创建的对象只有一个类型。而“通常”并不意味着任何事情,它只是意味着Python是一个例外。 - Martijn Pieters

23

根据这篇维基 Python文章,Python既是动态类型的,又是强类型的(也提供了很好的解释)。

也许您正在思考静态类型语言,在这些语言中,类型在程序执行期间无法更改,并且类型检查在编译时进行以检测可能的错误。

这个Stack Overflow问题可能会有所帮助:动态类型语言与静态类型语言,并且这篇维基百科文章类型系统提供了更多信息。


11

这个问题已经被回答过几次了,但是Python是一种强类型语言:

>>> x = 3
>>> y = '4'
>>> print(x+y)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'

以下是JavaScript的内容:
var x = 3    
var y = '4'
alert(x + y) //Produces "34"

这就是弱类型和强类型之间的区别。弱类型会根据上下文自动尝试将一种类型转换为另一种类型(例如 Perl),而强类型则绝不会隐式转换。

你的困惑在于对 Python 如何将值绑定到名称(通常称为变量)的方式有误解。

在 Python 中,名称没有类型,因此你可以执行以下操作:

bob = 1
bob = "bob"
bob = "An Ex-Parrot!"

名称可以绑定到任何东西:

>>> def spam():
...     print("Spam, spam, spam, spam")
...
>>> spam_on_eggs = spam
>>> spam_on_eggs()
Spam, spam, spam, spam

更多阅读材料:

https://zh.wikipedia.org/wiki/动态调用

和略微相关但更高级的:

https://www.jianshu.com/p/91bf0f3c946f


1
几年后 - 另一个有用且相关的资源:https://youtu.be/_AEJHKGk9ns - Wayne Werner
强类型与弱类型与表达式的结果类型无关,比如3+'4'。对于这个例子来说,JavaScript和Python一样强大。 - qznc
2
@oneloop 这并不一定是真的,只是将浮点数和整数组合的行为是明确定义的,并且结果是一个浮点数。在Python中,您也可以执行"3"*4。当然,结果是"3333"。你不能说它转换任何一件事。当然,这可能只是争论语义。 - Wayne Werner
1
@oneloop 并不一定是因为 Python 将 floatint 结合起来产生 float 就意味着它在隐式转换类型。浮点数和整数之间存在自然关系,事实上,类型层次结构也明确说明了这一点。我想你可以争辩说 Javascript 认为 '3'+4'e'+4 都是定义良好的操作,就像 Python 认为 3.0 + 4 是定义良好的一样,但此时就不存在强类型或弱类型的概念,只有(未)定义的操作。 - Wayne Werner
1
在那种情况下,我认为我会划定的界限是,任何有 TypeError(或等价物)的语言都是强类型语言。 - Wayne Werner
显示剩余3条评论

10

"强类型"这个词没有明确的定义。

因此,使用这个术语是取决于你和谁在交流。

我认为任何变量类型不明确声明或静态类型化的语言都不是强类型语言。

强类型不仅会防止转换(例如,将整数“自动”转换为字符串)。它还会防止赋值(即,更改变量的类型)。

如果以下代码被编译(解释),则该语言不是强类型:

Foo = 1 Foo = "1"

在强类型语言中,程序员可以“信任”一种类型。

例如,如果程序员看到声明:

UINT64 kZarkCount;

并且他或她知道在后面的20行代码中,kZarkCount仍然是一个UINT64类型的变量(只要它出现在同一个块中)- 而不需要检查中间的代码。


8
Python变量存储指向表示值的目标对象的无类型引用。任何赋值操作都意味着将无类型引用分配给被分配的对象——即该对象通过原始和新的(计数)引用共享。值类型绑定到目标对象,而不是引用值。当对该值执行操作时(运行时),进行(强)类型检查。换句话说,变量(从技术上讲)没有类型——如果想要精确,以变量类型思考是没有意义的。但是引用会自动取消引用,我们实际上是以目标对象的类型来思考。

3
我刚刚发现了一个简单易记的方法:
动态/静态类型表达式;强/弱类型值。
"Original Answer" 的意思是 "最初的回答"。

2

Python是强类型语言,因为它没有未经检查的动态类型错误。换句话说,你不能违反它的类型系统。

定义

  • 类型:值的集合。它部分地定义了它的值的预期使用(行为)。它可以通过枚举来扩展或通过谓词在内涵上指定。
  • 类型系统:检查值是否按照预期使用的系统,部分避免未定义行为。应该避免未定义行为,因为它可能导致程序崩溃或数据丢失以及产生不正确的结果。
  • 类型化语言:具有类型系统的语言。
  • 类型错误:由类型系统可检查的程序错误。
  • 表达式:表示值的程序文本。
  • 静态/动态类型:表达式的编译时/运行时类型。表达式的运行时类型是它表示的值的类型。
  • 静态/动态类型系统:检查静态/动态类型的类型系统。
  • 静态/动态类型化语言:具有静态/动态类型系统的语言。
  • 静态/动态类型错误:由静态/动态类型系统可检查的程序错误。
  • 弱类型/强类型语言:具有/不具有未经检查的动态类型错误的语言。静态类型、动态类型或两者都意味着强类型。
  • 单态/多态表达式:具有单个动态类型/多个动态类型的表达式。单态表达式具有单一预期使用,而多态表达式具有多重预期使用。
  • 通用/特殊多态表达式:真实/虚拟多态表达式。真实多态表达式表示具有多个类型的单个值,而虚拟多态表达式表示具有单个类型的多个值。
  • 参数化/包容多态表达式:基于类型的泛型类型/子类型的通用多态表达式(例如,C++表达式&表示单个T* (T&)操作符值,其中T是一个泛型类型/C++表达式std::exception表示单个S类值,其中Sstd::exception的一个泛型子类型)。
  • 重载/强制多态表达式:基于表达式/值转换的特殊多态表达式(例如,C++表达式+表示int (int&, int&)float (float&, float&)操作符值/C++表达式3.5表示floatbool值)。

参考资料

Cardelli(Luca),Wegner(Peter),“理解类型、数据抽象和多态性”,计算机调查,1985年,第17卷,第4期,第471-523页,DOI:https://doi.org/10.1145/6041.6042


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接