Python 是强类型的吗?
- 2025-01-06 08:31:00
- admin 原创
- 128
问题描述:
我碰到过一些链接,说 Python 是一种强类型语言。
然而,我认为在强类型语言中你不能这样做:
bob = 1
bob = "bob"
我认为强类型语言不接受运行时类型更改。也许我对强/弱类型的定义是错误的(或过于简单)。
那么,Python 是强类型语言还是弱类型语言?
解决方案 1:
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 完全可以添加类型int
和的对象float
并返回类型为的对象float
(例如,int(42) + float(1)
返回43.0
)。另一方面,由于类型不匹配,如果尝试以下操作,Haskell 会报错(42 :: Integer) + (1 :: Float)
。这使得 Haskell 成为一种严格类型语言,其中类型完全不相交,并且只能通过类型类进行受控形式的重载。
解决方案 2:
我认为所有现有的答案都忽略了一些重要问题。
弱类型意味着允许访问底层表示。在 C 语言中,我可以创建一个指向字符的指针,然后告诉编译器我想将其用作指向整数的指针:
char sz[] = "abcdefg";
int *i = (int *)sz;
在具有 32 位整数的小端平台上,这将变成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 中,如果您调用需要 的函数float
,并将其传入int
,它会为您转换。这肯定会导致错误,例如意外溢出,但它们与弱类型系统中的错误不同。而且 C 在这里并没有真正变弱;您可以在 Haskell 中添加一个 int 和一个 float,甚至可以将 float 连接到字符串,只需更明确地执行即可。
对于动态语言来说,这一点相当模糊。在 Python 或 Perl 中,没有“需要浮点数的函数”这样的东西。但是,有些重载函数可以对不同类型的函数执行不同的操作,并且人们有一种强烈的直觉,例如,将字符串添加到其他内容中就是“需要字符串的函数”。从这个意义上讲,Perl、Tcl 和 JavaScript 似乎做了很多隐式转换("a" + 1
给你"a1"
),而 Python 做的要少得多("a" + 1
引发异常,但1.0 + 1
给你2.0
*)。很难用正式的术语来表达这种感觉——为什么不应该有一个+
接受字符串和 int 的函数,而显然还有其他函数(如索引)可以接受字符串和 int?
实际上,在现代 Python 中,这可以用 OO 子类型来解释,因为
isinstance(2, numbers.Real)
是真的。我认为在 Perl 或 JavaScript 中,字符串类型的实例没有任何意义2
……尽管在 Tcl 中,它实际上是字符串类型的实例,因为一切都是字符串的实例。
最后,还有另一个完全正交的“强”类型与“弱”类型的定义,其中“强”表示强大/灵活/富有表现力。
例如,Haskell 允许您定义一个类型,该类型是数字、字符串、此类型的列表或从字符串到此类型的映射,这是表示可以从 JSON 解码的任何内容的完美方式。在 Java 中没有办法定义这样的类型。但至少 Java 具有参数化(泛型)类型,因此您可以编写一个接受 T 列表的函数并知道元素的类型是 T;其他语言,例如早期的 Java,强制您使用 Object 列表并向下转换。但至少 Java 允许您使用自己的方法创建新类型;C 只允许您创建结构。而 BCPL 甚至没有这个。以此类推到汇编,其中唯一的类型是不同的位长度。
因此,从这个意义上来说,Haskell 的类型系统比现代 Java 的类型系统更强大,现代 Java 的类型系统比早期的 Java 的类型系统更强大,早期的 Java 的类型系统比 C 的类型系统更强大,早期的 Java 的类型系统比 BCPL 的类型系统更强大。
那么,Python 属于这个范围的哪个位置呢?这有点棘手。在许多情况下,鸭子类型允许您模拟在 Haskell 中可以做的所有事情,甚至一些您不能做的事情;当然,错误是在运行时而不是编译时捕获的,但它们仍然会被捕获。然而,有些情况下鸭子类型是不够的。例如,在 Haskell 中,您可以判断一个空的 int 列表是一个 int 列表,因此您可以决定对该+
列表进行归约应该返回 0*;在 Python 中,一个空列表就是一个空列表;没有类型信息可以帮助您决定对其进行归约+
应该做什么。
事实上,Haskell 不允许您这样做;如果您在空列表中调用不采用起始值的 Reduce 函数,则会出错。但它的类型系统足够强大,您可以实现这一点,而 Python 则不行。
解决方案 3:
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'
解决方案 4:
您混淆了“强类型”和“动态类型”。
1
我无法通过添加字符串来改变类型'12'
,但我可以选择在变量中存储的类型并在程序运行时更改它。
动态类型的对立面是静态类型;变量类型的声明在程序的生命周期内不会改变。强类型的对立面是弱类型;值的类型可以在程序的生命周期内改变。
解决方案 5:
根据这篇wiki Python文章,Python 是动态的和强类型的(也提供了很好的解释)。
也许您正在考虑静态类型语言,其中类型在程序执行期间不能改变,并且在编译时进行类型检查以检测可能的错误。
这个问题可能会引起人们的兴趣:动态类型语言与静态类型语言,这篇关于类型系统的维基百科文章提供了更多信息
解决方案 6:
这个问题已经被回答过几次了,但是 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
解决方案 7:
“强类型”一词没有明确的定义。
因此,该术语的使用取决于你与谁交谈。
我不认为任何语言中的变量类型没有明确声明,或者静态类型是强类型的。
强类型不仅阻止转换(例如,“自动”从整数转换为字符串),还阻止赋值(即更改变量的类型)。
如果以下代码编译(解释),则该语言不是强类型的:
Foo = 1 Foo =“1”
在强类型语言中,程序员可以“依赖”一种类型。
例如,如果程序员看到以下声明,
UINT64 kZarkCount;
并且他或她知道 20 行之后,kZarkCount 仍然是 UINT64(只要它出现在同一个块中)——而无需检查中间的代码。
解决方案 8:
Python 变量存储对代表值的目标对象的无类型引用。
任何赋值操作都意味着将无类型引用分配给已分配的对象 - 即该对象通过原始和新的(计数)引用共享。
值类型与目标对象绑定,而不是与引用值绑定。(强)类型检查是在执行值操作时(运行时)进行的。
换句话说,变量(技术上)没有类型——如果要准确的话,用变量类型来思考是没有意义的。但引用会自动取消引用,我们实际上是用目标对象的类型来思考。
解决方案 9:
我刚刚发现了一种非常简洁的记忆方法:
动态/静态类型表达式;强/弱类型值。
解决方案 10:
现有的答案大多同意 Python 是一种强类型语言,因为它不会隐式地将值从一种类型转换为另一种类型。这些答案提到了将字符串添加到整数的情况来支持这一说法;在 Python 中"foo" + 3
引发TypeError
,而在Javascript(通常被认为是一种弱类型语言)中,数字3
被隐式转换为字符串,然后连接起来,因此结果是字符串"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# 强类型。
解决方案 11:
我认为,这个简单的例子应该可以解释强类型和动态类型之间的区别:
>>> 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
}
解决方案 12:
Python 是强类型的,因为它没有未经检查的动态类型错误。换句话说,你不能违反它的类型系统。
定义
类型:值的集合。它部分地定义了其值的预期用途(行为)。它可以通过枚举来扩展指定,也可以通过谓词来内涵指定。
类型系统:系统检查值是否按预期使用,部分避免未定义行为。应避免未定义行为,因为它可能导致程序后期崩溃或数据无声丢失并产生不正确的结果。
类型语言:具有类型系统的语言。
类型错误:可通过类型系统检查的程序错误。
表达式:表示值的程序文本。
静态/动态类型:表达式的编译时/运行时类型。表达式的运行时类型是它所表示的值的类型。
静态/动态类型系统:类型系统检查静态/动态类型。
静态/动态类型语言:具有静态/动态类型系统的语言。
静态/动态类型错误:可通过静态/动态类型系统检查的程序错误。
弱类型/强类型语言:具有/不具有未经检查的动态类型错误的语言。静态类型、动态类型或两者均意味着强类型。
单态/多态表达式:具有单一动态类型/多种动态类型的表达式。单态表达式具有单一预期用途,多态表达式具有多种预期用途。
通用/临时多态表达式:实/虚拟多态表达式。实多态表达式表示具有多种类型的单个值,虚拟多态表达式表示具有单一类型的多个值。
参数/包含多态表达式:基于类型的泛型/类型的子类型的通用多态表达式(例如,C++ 表达式
&
表示单个T* (T&)
运算符值,其中T
是泛型/C++ 表达式std::exception
表示单个S
类值,其中S
是的泛型子类型std::exception
)。重载/强制多态表达式:基于表达式/值转换的临时多态表达式(例如,C++ 表达式
+
表示int (int&, int&)
和float (float&, float&)
运算符值/C++ 表达式3.5
表示float
和bool
值)。
参考
Cardelli (Luca),Wegner (Peter),《关于理解类型、数据抽象和多态性》,《计算概览》,第 17 卷,第 4 期,1985 年,第 471-523 页,DOI:https ://doi.org/10.1145/6041.6042 。
解决方案 13:
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....
上述情况将导致大型系统中的代码在很长一段时间内难以维护,这简直是一场噩梦。随便你怎么称呼它,但“动态”更改变量类型的能力只是一个坏主意……