Python是强类型的吗?

Pac*_*ane 219 python weak-typing strong-typing

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

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

bob = 1
bob = "bob"
Run Code Online (Sandbox Code Playgroud)

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

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

Fre*_*Foo 334

Python是强有力的动态类型.

  • 类型意味着值的类型不会以意外的方式改变.只包含数字的字符串不会神奇地变成一个数字,就像Perl中可能发生的那样.每种类型的更改都需要显式转换.
  • 动态类型意味着运行时对象(值)具有类型,而不是静态类型,其中变量具有类型.

至于你的例子

bob = 1
bob = "bob"
Run Code Online (Sandbox Code Playgroud)

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

与旧的C语言形成鲜明对比,后者是弱的,静态类型的,因此指针和整数几乎可以互换.(在许多情况下,现代ISO C需要转换,但默认情况下我的编译器对此仍然宽容.)

我必须补充一点,强类型和弱类型更像是一个连续统一体而不是布尔选择.C++具有比C更强的类型(需要更多转换),但是类型系统可以通过使用指针强制转换来破坏.

动态语言(如Python)中类型系统的强度实际上取决于其原语和库函数如何响应不同类型.例如,+重载是因为它适用于两个数字两个字符串,但不是字符串和数字.这是在+实现时所做的设计选择,但从语言的语义开始并不是必需的.实际上,当您+在自定义类型上重载时,可以将其隐式转换为数字:

def to_number(x):
    """Try to convert x to a number."""
    if x is None:
        return 0
    # more special cases here
    else:
        return float(x)  # works for numbers and strings

class Foo(object):
    def __add__(self, other):
        other = to_number(other)
        # now do the addition
Run Code Online (Sandbox Code Playgroud)

(我所知道的唯一一种完全强类型,也就是严格类型的语言是Haskell,其中类型是完全不相交的,只有通过类型类才可以控制形式的重载.)

  • 不太确定这是否是一个反例:事物可以评估为布尔值,但它们不会突然"变成"布尔值.这几乎就像有人隐含地调用像as_boolean(<value>)这样的东西,它与对象本身的类型不一样,对吧? (22认同)
  • 我不经常看到的一个例子,但我认为显示Python不是完全强类型是很重要的,所有评估为boolean的东西:http://docs.python.org/release/2.5.2/lib /truth.html (18认同)
  • 在布尔上下文中不是一个反例,因为实际上没有任何东西被转换为"True"或"False".但数字促销怎么样?"1.0 + 2"在Python中与在Perl或C中一样好用,即使"1.0"+ 2`没有.我同意@jbrendel这不是一个隐式转换,它只是重载 - 但同样意义上,Perl也没有进行任何隐式转换.如果函数没有声明参数类型,则无法进行隐式转换. (13认同)
  • 考虑*强*类型的更好方法是在对变量执行操作时类型很重要.如果类型不是预期的,那么抱怨的语言是**强类型**(python/java),而不是**弱的类型**(javascript)**动态类型**语言(python)是允许变量类型在运行时更改的那些,而**静态类型的**语言(java)在声明变量后不允许这样做. (12认同)
  • @ gsingh2011真实是有用的,而不是*弱的打字*本身,但一个偶然的`如果isValid(值) - 1`可以泄漏.布尔值被强制转换为整数,然后将其作为truthy值进行求值.`False - 1`变得真实,而'True - 1`变得虚假,导致一个令人尴尬的困难的两层逐个错误调试.从这个意义上讲,python是*大多数*强类型; 类型强制通常不会导致逻辑错误. (2认同)

aba*_*ert 51

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


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

char sz[] = "abcdefg";
int *i = (int *)sz;
Run Code Online (Sandbox Code Playgroud)

在具有32位整数的小端平台上,这将i成为数字0x64636261和数组的数组0x00676665.实际上,您甚至可以将指针自身转换为整数(大小合适):

intptr_t i = (intptr_t)&sz;
Run Code Online (Sandbox Code Playgroud)

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

char *spam = (char *)0x12345678
spam[0] = 0;
Run Code Online (Sandbox Code Playgroud)

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

传统的Lisp允许类似的hackery; 在某些平台上,双字浮点数和缺点单元格是相同的类型,你可以将一个传递给一个期望另一个的函数,它会"工作".

今天的大多数语言并不像C和Lisp那样弱,但其中许多语言仍然有些漏洞.例如,任何具有未经检查的"向下倾斜"的OO语言,*这是类型泄漏:你实际上是告诉编译器"我知道我没有给你足够的信息来知道这是安全的,但我很确定它是,"当类型系统的整个点是编译器总是有足够的信息来知道什么是安全的.

*检查向下转换不会使语言的类型系统变弱,因为它将检查移动到运行时.如果确实如此,那么子类型多态(也就是虚拟或全动态函数调用)将会违反类型系统,我认为没有人想这么说.

在这个意义上,很少有"脚本"语言很弱.即使在Perl或Tcl中,你也不能接受一个字符串,只是把它的字节解释为一个整数.*但是值得注意的是,在CPython中(对于许多语言的许多其他解释器来说也是如此),如果你真的很执着,那么你可以ctypes用来加载libpython,将对象强制转换id为a POINTER(Py_Object),并强制类型系统泄漏.这是否会使类型系统变弱取决于您的使用案例 - 如果您尝试实现一个语言内限制执行沙箱以确保安全性,那么您必须处理这些类型的转义......

*您可以使用类似函数struct.unpack来读取字节并从"C将如何表示这些字节"中构建一个新的int,但这显然不会泄漏; 甚至Haskell也允许这样做.


同时,隐式转换实际上与弱或泄漏类型系统不同.

每种语言,甚至是Haskell,都具有将整数转换为字符串或浮点数的功能.但是有些语言会自动为你做一些转换 - 例如,在C中,如果你调用一个需要a的函数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具有参数(通用)类型,因此您可以编写一个函数,该函数采用List of T并知道元素是T类型的; 其他语言,如早期Java,强迫您使用对象列表和向下转换.但至少Java允许您使用自己的方法创建新类型; C只允许您创建结构.BCPL甚至都没有.依此类推到汇编,其中唯一的类型是不同的位长.

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

那么,Python在哪里适合该频谱?这有点棘手.在许多情况下,duck typing允许你模拟你在Haskell中可以做的所有事情,甚至一些你不能做的事情; 确定,错误是在运行时而不是编译时捕获的,但它们仍然被捕获.但是,有些情况下鸭子打字不充分.例如,在Haskell中,您可以告诉一个空的int列表是一个int列表,因此您可以决定减少+该列表应返回0*; 在Python中,空列表是一个空列表; 没有类型信息可以帮助您决定减少+它应该做什么.

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

  • 这个答案太棒了!遗憾的是,它在列表的底部已经萎靡不振了. (3认同)
  • 只是对您的 C 示例的一点评论:`char sz[]` 不是指向 char 的指针,它是 char 数组,并且在赋值中它衰减为指针。 (2认同)

Mar*_*ers 34

您将'强类型''动态类型'混淆.

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

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


Lev*_*von 24

根据这个维基Python文章,Python是动态和强类型的(也提供了一个很好的解释).

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

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


Ada*_*hes 15

TLDR;

Python的输入是动态的,因此您可以将int变量更改为字符串

x = 'somestring'
x = 50
Run Code Online (Sandbox Code Playgroud)

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

'x' + 3 --> TypeError: cannot concatenate 'str' and 'int' objects
Run Code Online (Sandbox Code Playgroud)

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

 'x'+3 = 'x3'
Run Code Online (Sandbox Code Playgroud)

关于类型推断

Java强制您显式声明对象类型

int x = 50
Run Code Online (Sandbox Code Playgroud)

Kotlin使用推理来实现它是一个int

x = 50
Run Code Online (Sandbox Code Playgroud)

但由于两种语言都使用静态类型,x因此无法更改int.这两种语言都不允许动态变化

x = 50
x = 'now a string'
Run Code Online (Sandbox Code Playgroud)

  • 无论如何,您的答案实际上比上述答案更简洁易懂。 (5认同)
  • JavaScript 开发人员在这里,最好的解释! (3认同)

Way*_*ner 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'
Run Code Online (Sandbox Code Playgroud)

JavaScript中的以下内容:

var x = 3    
var y = '4'
alert(x + y) //Produces "34"
Run Code Online (Sandbox Code Playgroud)

这是弱打字和强打字之间的区别.弱类型会自动尝试从一种类型转换为另一种类型,具体取决于上下文(例如Perl).强类型永远不会隐式转换.

您的困惑在于对Python如何将值绑定到名称(通常称为变量)的误解.

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

bob = 1
bob = "bob"
bob = "An Ex-Parrot!"
Run Code Online (Sandbox Code Playgroud)

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

>>> def spam():
...     print("Spam, spam, spam, spam")
...
>>> spam_on_eggs = spam
>>> spam_on_eggs()
Spam, spam, spam, spam
Run Code Online (Sandbox Code Playgroud)

进一步阅读:

https://en.wikipedia.org/wiki/Dynamic_dispatch

和稍微相关但更高级的:

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

  • @oneloop不一定是真的,只是组合浮点数和整数的行为是明确定义的,并导致浮点数.你也可以在python中做"3"*4`.结果当然是"3333".你不会说它正在转换任何一件事.当然,这可能只是在争论语义. (2认同)

pep*_*epr 6

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

任何赋值操作都意味着将无类型引用赋值给指定的对象 - 即通过原始和新的(计数)引用共享对象.

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

换句话说,变量(技术上)没有类型 - 如果想要精确的话,根据变量类型进行思考是没有意义的.但引用会自动解除引用,我们实际上会考虑目标对象的类型.


小智 6

术语"强类型"没有明确的定义.

因此,该术语的使用取决于您与谁说话.

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

强类型不仅排除转换(例如,"自动"从整数转换为字符串).它排除了分配(即改变变量的类型).

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

Foo = 1 Foo ="1"

在强类型语言中,程序员可以"依靠"一种类型.

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

UINT64 kZarkCount;

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