298

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

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

bob = 1
bob = "bob"

我认为强类型语言在运行时不接受类型更改。也许我对强/弱类型的定义是错误的(或过于简单化)。

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

4

12 回答 12

451

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)returns 43.0)。另一方面,由于类型之间的不匹配,如果尝试以下操作,Haskell 会抱怨(42 :: Integer) + (1 :: Float)。这使得 Haskell 成为一种严格类型化的语言,其中类型完全不相交,并且只能通过类型类实现受控形式的重载。

于 2012-07-04T12:19:26.613 回答
79

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


弱类型意味着允许访问底层表示。在 C 中,我可以创建一个指向字符的指针,然后告诉编译器我想将它用作指向整数的指针:

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

在具有 32 位整数的 little-endian 平台上,这构成i了一个数字数组0x646362610x00676665. 事实上,您甚至可以将指针本身转换为整数(具有适当的大小):

intptr_t i = (intptr_t)&sz;

当然,这意味着我可以覆盖系统中任何地方的内存。*

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

* 当然,现代操作系统使用虚拟内存和页面保护,所以我只能覆盖我自己的进程的内存,但 C 本身并没有提供这种保护,任何曾经在 Classic Mac OS 或 Win16 上编写过代码的人都可以告诉你。

传统的 Lisp 允许类似的黑客行为;在某些平台上,双字浮点数和 cons 单元格是同一类型,您只需将一个传递给期望另一个的函数,它就会“工作”。

今天的大多数语言并不像 C 和 Lisp 那样弱,但其中许多仍然有些漏洞。例如,任何具有未经检查的“向下转换”* 的 OO 语言都是类型泄漏:您实际上是在告诉编译器“我知道我没有给您足够的信息来知道这是安全的,但我很确定它是,”当类型系统的全部意义在于编译器总是有足够的信息来知道什么是安全的。

* 检查的向下转换不会仅仅因为它将检查移动到运行时而使语言的类型系统变得更弱。如果确实如此,那么子类型多态性(也称为虚拟或完全动态函数调用)将同样违反类型系统,我认为没有人愿意这么说。

在这个意义上,很少有“脚本”语言是弱的。即使在 Perl 或 Tcl 中,您也不能获取字符串并将其字节解释为整数。* 但值得注意的是,在 CPython 中(对于许多语言的许多其他解释器也是如此),如果您真的坚持不懈,您可用于ctypes加载libpython、将对象id转换为 aPOINTER(Py_Object)并强制类型系统泄漏。这是否会使类型系统变弱取决于您的用例——如果您尝试实现语言内受限执行沙箱以确保安全,则必须处理这些类型的转义……</p>

* 您可以使用struct.unpack读取字节之类的函数,并根据“C 如何表示这些字节”构建一个新的 int,但这显然不是泄漏的;甚至 Haskell 也允许这样做。


同时,隐式转换与弱或泄漏类型系统确实不同。

每种语言,甚至是 Haskell,都具有将整数转换为字符串或浮点数的函数。但是有些语言会自动为你做一些转换——例如,在 C 中,如果你调用一个需要 a 的函数float,并且你将它传递给int,它会为你转换。这肯定会导致错误,例如意外溢出,但它们与从弱类型系统中获得的错误类型不同。而且C在这里并没有真正变弱。您可以在 Haskell 中添加一个 int 和一个浮点数,甚至可以将一个浮点数连接到一个字符串,您只需要更明确地执行此操作。

对于动态语言,这非常模糊。在 Python 或 Perl 中没有“需要浮点数的函数”之类的东西。但是有一些重载函数用不同的类型做不同的事情,并且有一种强烈的直觉感觉,例如,将一个字符串添加到其他东西是“一个需要字符串的函数”。从这个意义上说,Perl、Tcl 和 JavaScript 似乎做了很多隐式转换("a" + 1给你"a1"),而 Python 做的少得多("a" + 1引发异常,但1.0 + 1给你2.0*)。很难将这种意义转化为正式的术语——为什么不应该有一个+接受字符串和 int 的 a,而显然还有其他函数,比如索引,这样做呢?

* 实际上,在现代 Python 中,这可以用 OO 子类型来解释,因为这isinstance(2, numbers.Real)是真的。我认为2Perl 或 JavaScript 中的字符串类型的实例没有任何意义……尽管在 Tcl 中,它实际上是,因为一切都是字符串的实例。


最后,还有另一个完全正交的“强”与“弱”类型的定义,其中“强”意味着强大/灵活/富有表现力。

例如,Haskell 允许您定义数字、字符串、此类型的列表或从字符串到此类型的映射的类型,这是表示可以从 JSON 解码的任何内容的完美方式。没有办法在 Java 中定义这样的类型。但至少 Java 有参数(泛型)类型,因此您可以编写一个函数,它接受一个 T 的 List 并知道元素的类型是 T;其他语言,例如早期的 Java,迫使您使用对象列表并向下转换。但至少 Java 允许您使用自己的方法创建新类型;C 只允许您创建结构。BCPL 甚至没有。依此类推,直到组装,唯一的类型是不同的位长度。

所以,从这个意义上说,Haskell 的类型系统比现代 Java 强,现代 Java 比早期 Java 强,比 C 强,比 BCPL 强。

那么,Python 在哪里适合这个范围呢?这有点棘手。在很多情况下,duck typing 允许你模拟你在 Haskell 中可以做的所有事情,甚至是一些你不能做的事情;当然,错误是在运行时而不是编译时捕获的,但它们仍然会被捕获。但是,在某些情况下,duck 打字是不够的。例如,在 Haskell 中,您可以判断一个空的整数列表是一个整数列表,因此您可以决定对该列表进行归约+应该返回 0*;在 Python 中,空列表是一个空列表;没有类型信息可以帮助您决定减少+它应该做什么。

* 事实上,Haskell 不允许你这样做;如果您调用的 reduce 函数在空列表中不采用起始值,则会出现错误。但它的类型系统足够强大,您可以完成这项工作,而 Python 的则不然。

于 2014-08-10T18:15:36.297 回答
52

TLDR;

Python 类型是动态的,因此您可以字符串变量更改为 int(在静态语言中您不能)

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'
于 2017-07-27T19:01:36.480 回答
44

您将'strongly typed''dynamically typed'混淆了。

我无法1通过添加 string来更改类型'12',但我可以选择存储在变量中的类型并在程序运行时更改它。

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

于 2012-07-04T12:18:04.053 回答
23

根据这篇wiki Python文章 Python 是动态类型和强类型的(也提供了很好的解释)。

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

这个 SO 问题可能很有趣:动态类型语言与静态类型语言和这篇关于类型系统的维基百科文章提供了更多信息

于 2012-07-04T12:18:28.343 回答
9

术语“强类型”没有明确的定义。

因此,该术语的使用取决于您与谁交谈。

我不考虑任何语言,其中变量的类型既没有显式声明,也没有静态类型化为强类型。

强类型不仅排除了转换(例如,“自动”从整数转换为字符串)。它排除了赋值(即更改变量的类型)。

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

富 = 1 富 = "1"

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

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

UINT64 kZarkCount;

并且他或她知道 20 行之后,kZarkCount 仍然是 UINT64(只要它出现在同一个块中) - 无需检查中间代码。

于 2015-09-13T05:20:17.657 回答
8

已经回答过几次了,但是 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://en.wikipedia.org/wiki/Dynamic_dispatch

以及稍微相关但更高级的:

http://effbot.org/zone/call-by-object.htm

于 2012-07-04T12:34:56.593 回答
7

Python 变量存储对表示值的目标对象的无类型引用。

任何赋值操作都意味着将无类型引用分配给已分配对象——即对象通过原始引用和新(计数)引用共享。

值类型绑定到目标对象,而不是引用值。(强)类型检查在执行具有值的操作时完成(运行时)。

换句话说,变量(从技术上讲)没有类型——如果想要准确的话,从变量类型的角度来思考是没有意义的。但是引用是自动取消引用的,我们实际上是根据目标对象的类型来考虑的。

于 2012-07-04T13:15:58.470 回答
1

我刚刚发现了一种非常简洁的记忆方法:

动态/静态类型表达式;强/弱类型值。

于 2019-05-08T13:38:19.337 回答
0

我想,这个简单的例子你应该解释强类型和动态类型之间的差异:

>>> tup = ('1', 1, .1)
>>> for item in tup:
...     type(item)
...
<type 'str'>
<type 'int'>
<type 'float'>
>>>

爪哇:

public static void main(String[] args) {
        int i = 1;
        i = "1"; //will be error
        i = '0.1'; // will be error
    }
于 2012-07-04T13:35:19.137 回答
0

现有的答案大多同意 Python 是一种强类型语言,因为它不会将值从一种类型隐式转换为另一种类型。这些答案提到了将字符串添加到整数以支持此主张的情况;"foo" + 3在 Python 中引发 a TypeError,而在Javascript(通常被认为是一种弱类型语言)中,数字3被隐式转换为字符串然后连接起来,因此结果是 string "foo3"

但在其他一些情况下,Python确实会进行隐式类型转换:

# implicit conversion from int to float
1 + 1.0

# implicit conversion from list to bool
if []: pass

相比之下,F#(通常被认为是一种强类型语言)不允许以下两种情况:

  1 + 1.0;;
  ----^^^
error FS0001: The type 'float' does not match the type 'int'

  if [] then 1 else 2;;
  ---^^
error FS0001: This expression was expected to have type bool but here has type 'a list  

所以实际上并没有严格的“强类型”和“弱类型”语言的二分法,而是我们可以说 Python 比 Javascript 的强类型更强,但不如 F# 强类型。

于 2021-11-30T21:48:21.687 回答
-4
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....

上述情况会在很长一段时间内在大型系统中造成无法维护的代码的噩梦。随心所欲地称呼它,但是“动态”更改变量类型的能力只是一个坏主意……

于 2013-08-02T17:40:55.457 回答