Python强types?

我遇到过说Python是强types语言的链接。

但是,我认为在强types的语言,你不能这样做:

bob = 1 bob = "bob" 

我认为强types语言在运行时不接受types转换。 也许我有一个错误的(或太简单)强/弱types的定义。

那么,Python是强types还是弱types?

Python是强有力的dynamictypes。

  • types意味着值的types不会突然改变。 一个只包含数字的string不会神奇地变成一个数字,就像在Perl中可能发生的那样。 每种types的变化都需要一个明确的转换。
  • dynamictypes意味着运行时对象(值)有一个types,而不是静态types,其中variables有一个types。

至于你的例子

 bob = 1 bob = "bob" 

这是可行的,因为variables没有types; 它可以命名任何对象。 在bob=1 ,你会发现这个type(bob)返回int ,但是在bob="bob" ,它返回str 。 (请注意, type是一个常规函数,因此它将评估其参数,然后返回该值的types。)

与C的较老的方言相比较,它们是弱的,静态types的,所以指针和整数几乎是可以互换的。 (现代的ISO C在许多情况下需要转换,但我的编译器在默认情况下仍然是宽大的。)

我必须补充一点,强types和弱types比布尔型select更具有连续性。 C ++比C有更强的types(需要更多的转换),但是types系统可以通过使用指针转换来破坏。

dynamic语言(如Python)中types系统的强度实际上取决于其基元和库函数如何响应不同types。 例如, +被重载,因此它可以在两个数字两个string上工作,而不是一个string和一个数字。 这是在+实现时做出的deviseselect,但是从语言的语义上来看,并不是必须的。 实际上,当你在自定义types上重载+时,你可以隐式地把任何东西转换成一个数字:

 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 

(我知道的唯一完全强types的语言,也就是严格types,就是Haskell,types完全不相交,只有受控的重载types可以通过types类来实现。

有一些重要的问题,我认为所有现有的答案已经错过了。


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

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

在一个32位整数的小端平台上,这使i成为一个数组0x646362610x00676665 。 实际上,你甚至可以把指针本身转换为整数(具有合适的大小):

 intptr_t i = (intptr_t)&sz; 

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

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

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

传统的Lisp允许类似的骇客; 在一些平台上,双字浮动和反单元是相同的types,你可以通过一个函数来期待另一个,它会“工作”。

今天的大多数语言都不如C和Lisp那么弱,但是其中许多语言仍然有点漏洞。 例如,任何OO语言都有一个未经检查的“downcast”,*这是一个types泄漏:你基本上告诉编译器“我知道我没有给你足够的信息来知道这是安全的,但我很确定它是,“当一个types系统的全部重点是编译器总是有足够的信息来知道什么是安全的。

*检查downcast不会使语言的types系统更弱,只是因为它将检查移动到运行时。 如果是这样,那么子types多态(又名虚拟或全dynamic函数调用)将是types系统的相同的违反,我不认为有人想这样说。

在这个意义上,很less有“脚本”语言是弱的。 即使在Perl或Tcl中,也不能取一个string,而只是把它的字节解释为一个整数。但是值得注意的是,在CPython中(对许多其他语言的解释器也是如此),如果你真的执着,可以使用ctypes加载libpython ,将对象的id转换为POINTER(Py_Object) ,强制types系统泄漏。 这是否会使types系统变弱取决于你的使用情况 – 如果你正在试图实现一个语言上受限制的执行沙箱来确保安全,你必须处理这些types的转义…

*你可以使用像struct.unpack这样的函数来读取字节,并用“C如何表示这些字节”来构build一个新的int,但这显然不是泄漏; 即使是Haskell也可以。


同时,隐式转换实际上是与弱或泄漏types系统不同的东西。

每一种语言,甚至Haskell,都具有将整数转换为string或浮点数的function。 但是有些语言会自动为你做一些这样的转换,例如在C语言中,如果你调用一个需要float的函数,并且你用int传递它,它会被转换。 这肯定会导致错误,例如意外的溢出,但是它们不是从弱types系统获得的同types的错误。 而C在这里并没有真正的弱点。 你可以在Haskell中添加一个int和一个float,甚至可以将一个float连接到一个string,你只需要更明确的做到这一点。

而dynamic语言,这是非常模糊的。 Python或Perl中没有“需要float的函数”这样的东西。 但是有一些重载的函数会对不同的types做不同的事情,并且有一种强烈的直觉,例如,将string添加到别的东西上就是“需要string的函数”。 从这个意义上说,Perl,Tcl和JavaScript似乎做了很多隐式转换( "a" + 1给你"a1" ),而Python做的更less( "a" + 1引发exception,但是1.0 + 1确实给你2.0 *)。 为什么不应该有一个接受一个string和一个整数的+ ,当有明显的其他function,比如索引,呢?

*实际上,在现代Python中,可以用OO子types来解释,因为isinstance(2, numbers.Real)是真的。 我不认为有什么意义,其中2是在Perl或JavaScript中的stringtypes的实例…虽然在Tcl,它实际上,因为一切都是一个string的实例。


最后,还有另一个完全正交的“强”与“弱”打字的定义,其中“强”意味着强大/灵活/performance力。

例如,Haskell可以让你定义一个types,它是一个数字,一个string,这种types的列表,或者一个从string到这种types的映射,这是一种完美的方式来表示任何可以从JSON解码的东西。 没有办法在Java中定义这种types。 但至lessJava有参数(generics)types,所以你可以编写一个函数来获取T的List,并知道这些元素是Ttypes的; 其他语言,比如早期的Java,就迫使你使用对象列表并向下转换。 但是至lessJava可以让你用自己的方法创build新的types; C只能让你创build结构。 BCPL甚至没有这个。 等到assembly,只有types是不同的位长度。

所以,从这个意义上讲,Haskell的types系统比现代的Java更强大,比早期的Java更强大,比C的强于BCPL。

那么,Python在哪些方面适合这个频谱呢? 这有点棘手。 在很多情况下,鸭子打字可以让你模拟你在Haskell中可以做的所有事情,甚至是你不能做的一些事情。 当然,错误是在运行时而不是编译时被捕获的,但是它们仍然被捕获。 但是,有些情况下,鸭子打字是不够的。 例如,在Haskell中,你可以知道一个空的int列表是一个int列表,所以你可以决定在列表上减less+应该返回0 *。 在Python中,一个空列表是一个空列表; 没有types信息可以帮助您决定应该怎么做。

*实际上,Haskell不会让你这样做; 如果您调用不在空列表上使用起始值的reduce函数,则会出现错误。 但是它的types系统是足够强大的,你可以使这个工作,Python的不是。

您将“强types”与“dynamictypes”混淆。

我不能通过添加string'12'来改变1的types,但是我可以select存储在一个variables中的types,并在程序运行期间改变它。

与dynamictypes相反的是静态types; variablestypes声明在程序的生命周期中不会改变。 强打字的反面是打字弱; 的types可以在程序的生命周期中改变。

根据这个wiki Python文章Python是dynamic和强types(也提供了一个很好的解释)。

也许你正在考虑在程序执行期间types不能改变的静态types语言,并且在编译期间发生types检查以检测可能的错误。

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

它已经被回答了几次,但是Python是一种强types语言:

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

这就是弱打字和强打字之间的区别。 弱types会根据上下文自动尝试从一种types转换为另一种types(例如Perl)。 强types从不隐式转换。

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

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

 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

一个Pythonvariables存储一个到目标对象的无types引用,表示值。

任何赋值操作都意味着将无types的引用赋值给赋值对象 – 即通过原始引用和新引用(计数)来共享该对象。

值types绑定到目标对象,而不是参考值。 (强)types检查是在执行该值的操作(运行时)时完成的。

换句话说,variables(技术上)没有types – 如果想要精确的话,用variablestypes来思考是没有意义的。 但引用会自动解除引用,我们实际上会根据目标对象的types进行思考。

“强打字”一词没有明确的定义。

因此,这个词的使用取决于你在说谁。

我不认为任何语言,其中variables的types不明确声明,或静态types为强types。

强types不仅仅是阻止转换(例如,“自动”从一个整数转换为一个string)。 它排除了分配(即,改变variables的types)。

如果下面的代码编译(解释),语言不是强types的:

Foo = 1 Foo =“1”

在强types语言中,程序员可以“依靠”一个types。

例如,如果一个程序员看到这个声明,

UINT64 kZarkCount;

他或她知道20行后,kZarkCount仍然是一个UINT64(只要它出现在同一个块),而不必检查干预代码。

TLDR;

Python的input是dynamic的,所以你可以改变一个intvariables为一个string

 x = 'somestring' x = 50 

Python的input很强,所以你不能合并types:

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

在弱types的Javascript这发生…

  'x'+3 = 'x3' 

关于types推断

Java迫使你明确地声明你的对象types

 int x = 50 

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

 x = 50 

但是因为两种语言都使用静态types,所以x不能从int改变。 这两种语言都不会允许像

 x = 50 x = 'now a string' 

我想,这个简单的例子应该解释强types和dynamictypes之间的差异:

 >>> 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 } 
 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 cx = 10 #add an attribute x inital value 10 cy = 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 ty = 6 # set ty to a number ry = 7 # set ry to a number print(f(r,t)) # call function designed to operate on testme objects ry = "I am ry" # redefine ry to be a string print(f(r,t)) #POW!!!! not good.... 

上述情况会在很长一段时间内在大系统中造成不可维护代码的噩梦。 调用它你想要的,但“dynamic”改变variablestypes的能力是一个坏主意……