Python是强类型的吗?


234

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

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

bob = 1
bob = "bob"

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

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

Answers:


358

Python是强大的动态类型。

  • 类型意味着值的类型不会以意外的方式改变。仅包含数字的字符串不会神奇地变成数字,这在Perl中可能会发生。类型的每次更改都需要显式转换。
  • 动态类型意味着运行时对象(值)具有类型,而静态类型则是变量具有类型。

至于你的例子

bob = 1
bob = "bob"

这是可行的,因为变量没有类型。它可以命名任何对象。之后bob=1,您会发现type(bob)返回int,但是之后bob="bob",它将返回str。(请注意,这type是一个常规函数,因此它将计算其参数,然后返回值的类型。)

与此相比,C的较早的方言是弱类型的静态类型,因此指针和整数几乎可以互换。(现代ISO C在很多情况下都需要转换,但是默认情况下,我的编译器对此仍然宽容。)

我必须补充一点,强类型vs.弱类型键入更多是一个连续的过程,而不是布尔选择。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成为严格类型化的语言,其中类型完全不相交,并且只能通过类型类控制重载形式。


18
我不经常看到的一个例子,但我认为很重要的一点是,要证明Python不是完全强类型的,所有要评估为布尔值的东西都是:docs.python.org/release/2.5.2/lib/truth.html
gsingh2011 2013年

25
不确定这是否是一个反例:事物可以评估为布尔值,但是它们不会突然“变成”布尔值。几乎就像有人隐式调用了as_boolean(<value>)之类的东西,这与更改对象本身的类型不同,对吗?
jbrendel 2014年

15
在布尔值上下文中保持真实不是反例,因为实际上没有任何内容转换为TrueFalse。但是号码推广呢?1.0 + 2即使在Python中,它在Python中的效果也与在Perl或C中"1.0" + 2一样好。我同意@jbrendel的观点,它实际上不是隐式转换,只是重载-但从同样的意义上讲,Perl也不进行任何隐式转换。如果函数没有声明参数类型,则没有地方进行隐式转换。
2014年

13
考虑类型的一种更好的方法是,在对变量执行操作时,类型很重要。如果类型不符合预期,则抱怨的语言是强类型的(python / java),不是抱怨的语言是弱类型的(javascript) 动态类型的语言(python)是允许变量类型在以下位置更改的语言一旦声明了变量,静态类型的语言(java)就不允许这样做。
kashif

2
@ gsingh2011真实性很有用,它本身并不是弱类型,但偶然性if isValid(value) - 1可能会泄漏。布尔值被强制为整数,然后将其评估为真实值。False - 1变得真实,True - 1变得虚假,从而导致难以调试的难于解决的两层一对一错误。从这个意义上说,python通常强类型的。类型强制通常不会导致逻辑错误。
Aaron3468 '16

57

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


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

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

在具有32位整数的little-endian平台上,这使i数字0x64636261和组成一个数组0x00676665。实际上,您甚至可以将指针自身转换为整数(适当大小):

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,将对象投射idPOINTER(Py_Object)并强制类型系统泄漏。是否使类型系统变弱取决于您的用例,如果您要实施语言限制的执行沙箱以确保安全性,则必须处理此类转义……

*您可以使用一个函数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的函数呢?

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


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

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

因此,从这个意义上讲,Haskell的类型系统要强于现代Java,后者要强于早期的Java,强于C,强于BCPL。

那么,Python在该范围内适合什么呢?有点棘手。在许多情况下,鸭子输入可以让您模拟在Haskell中可以做的所有事情,甚至可以模拟一些您做不到的事情。当然,错误是在运行时捕获的,而不是在编译时捕获的,但是仍然被捕获。但是,在某些情况下,鸭子的类型还不够。例如,在Haskell中,您可以说一个空的整数列表就是一个整数列表,因此您可以决定减少+该列表应返回0 *;在Python中,空列表是空列表;没有类型信息可以帮助您确定+应减少的数量。

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


3
这个答案很棒!可耻的是,它在名单的底部徘徊了这么长时间。
LeoR 2015年

1
只是对您的C示例进行一点注释:char sz[]不是char的指针,它是char的数组,在赋值中它会衰减为指针。
majkel.mk

39

您将“强类型”“动态类型”混淆了。

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

动态类型的反义词是静态类型。变量类型声明在程序生存期内不会更改。强类型的反义词是弱类型。的类型可以在程序的生存期内更改。


链接中的描述是强类型的:“通常,强类型的语言在编译时具有更严格的键入规则,这意味着在编译过程中更容易发生错误和异常。” 暗示Python是一种弱类型的语言...,维基是错误的吗?
下雨

1
@s̮̦̩e̝͓c̮͔̞ṛ̖̖e̬̣̦t̸͉̥̳̼:完全没有暗示。Python在编译时有严格的输入规则,创建的每个对象只有一种类型。而且,“通常”并不意味着任何意思,它只是意味着Python是一个例外。
马丁·彼得


18

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'

我不知道Javascript的详细信息,但是'x' + 3可能会operator+在后台超载并进行类型转换?
下雨

3
无论如何,您的答案实际上比上述答案更简洁,更容易理解。
下雨

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


1
几年后-另一个有用且相关的资源:youtu.be/_AEJHKGk9ns
韦恩·沃纳

强类型与弱类型与3 +'4'之类的表达式的结果类型无关。在此示例中,JavaScript与Python一样强大。
qznc 2015年

@qznc Javasript有多么强大?我不认为这与结果类型有任何关系,确实我明确声明弱类型会自动尝试从一种类型转换为另一种类型
韦恩·沃纳

2
@oneloop不一定是正确的,只是组合浮点数和整数的行为是明确定义的,并导致浮点数。您也可以"3"*4在python中进行。结果当然是"3333"。您不会说它正在转换任何东西。当然那可能只是在争论语义学。
韦恩·沃纳

1
@oneloop不一定是真的,因为Python产生float了的组合,float并且int它隐式地转换了类型。float和int之间存在自然的关系,实际上,类型继承说明了这一点。我想您可能会认为Javascript会考虑'3'+4并认为'e'+4它们都是良好定义的操作,就像Python认为3.0 + 4是良好定义的一样,但是到那时,实际上并没有强类型或弱类型之类的东西,只是(un)defined操作。
韦恩·沃纳

6

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

任何分配操作都意味着将无类型的引用分配给所分配的对象-即,该对象是通过原始引用和新的(计数的)引用共享的。

值类型绑定到目标对象,而不是参考值。当执行带有值的操作(运行时)时,将进行(强)类型检查。

换句话说,变量(从技术上来说)没有类型-如果要精确地考虑变量类型,这是没有意义的。但是引用会自动取消引用,实际上我们是根据目标对象的类型来考虑的。


6

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

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

我不考虑任何没有明确声明变量类型或静态类型都不是强类型的语言。

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

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

Foo = 1 Foo =“ 1”

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

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

UINT64 kZarkCount;

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


1

我刚刚发现了一种精妙的简洁方式来记忆它:

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


0

我认为,这个简单的例子应该说明强类型和动态类型之间的区别:

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

Java的

public static void main(String[] args) {
        int i = 1;
        i = "1"; //will be error
        i = '0.1'; // will be error
    }

您的python代码演示了动态类型,而Java演示了静态类型。一个更好的例子是$ var ='2'+ 1 //结果是3
erichlf

@ivleph,我同意。也可以这样写:“ a” * 3 ==“ aaa”
Dmitry Zagorulkin 2015年

-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....

在长时间内,以上内容将在大型系统中造成无法维护的代码的噩梦。称它为您想要的东西,但是“动态”更改变量类型的能力只是一个坏主意...

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.