Answers:
Python是强大的动态类型。
至于你的例子
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是完全精细与添加类型的对象int
和float
类型的对象,并返回float
(例如,int(42) + float(1)
返回43.0
)。另一方面,由于类型之间的不匹配,如果尝试以下操作,Haskell将抱怨(42 :: Integer) + (1 :: Float)
。这使Haskell成为严格类型化的语言,其中类型完全不相交,并且只能通过类型类控制重载形式。
True
或False
。但是号码推广呢?1.0 + 2
即使在Python中,它在Python中的效果也与在Perl或C中"1.0" + 2
一样好。我同意@jbrendel的观点,它实际上不是隐式转换,只是重载-但从同样的意义上讲,Perl也不进行任何隐式转换。如果函数没有声明参数类型,则没有地方进行隐式转换。
if isValid(value) - 1
可能会泄漏。布尔值被强制为整数,然后将其评估为真实值。False - 1
变得真实,True - 1
变得虚假,从而导致难以调试的难于解决的两层一对一错误。从这个意义上说,python通常是强类型的。类型强制通常不会导致逻辑错误。
我认为所有现有答案都遗漏了一些重要问题。
弱类型意味着允许访问基础表示。在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
,将对象投射id
到POINTER(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则不能。
char sz[]
不是char的指针,它是char的数组,在赋值中它会衰减为指针。
我无法1
通过添加字符串来更改类型'12'
,但可以选择存储在变量中的类型并在程序运行时进行更改。
动态类型的反义词是静态类型。变量类型的声明在程序生存期内不会更改。强类型的反义词是弱类型。值的类型可以在程序的生存期内更改。
根据该Wiki Python文章,Python是动态且强类型的(也提供了很好的解释)。
也许您正在考虑使用静态类型的语言,这些类型的语言在程序执行期间不能更改,并且在编译期间进行类型检查以检测可能的错误。
这样的SO问题可能很有趣:动态类型语言与静态类型语言,此有关Type Systems的 Wikipedia文章提供了更多信息
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'
'x' + 3
可能会operator+
在后台超载并进行类型转换?
它已经被回答过几次了,但是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
和稍微相关但更高级的:
"3"*4
在python中进行。结果当然是"3333"
。您不会说它正在转换任何东西。当然那可能只是在争论语义学。
我认为,这个简单的例子应该说明强类型和动态类型之间的区别:
>>> 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
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....
在长时间内,以上内容将在大型系统中造成无法维护的代码的噩梦。称它为您想要的东西,但是“动态”更改变量类型的能力只是一个坏主意...