2024-04-26 12:22:18 发布
网友
我看到一些链接说Python是一种强类型语言。
但是,我认为在强类型语言中,您不能这样做:
bob = 1 bob = "bob"
我以为强类型语言不接受运行时的类型更改。也许我对强/弱类型的定义是错误的(或者过于简单化)。
那么,Python是强类型语言还是弱类型语言?
Python是强动态类型的。
以你为例
这是因为变量没有类型;它可以命名任何对象。在bob=1之后,您会发现type(bob)返回int,但是在bob="bob"之后,它返回str。(注意type是一个正则函数,因此它计算其参数,然后返回值的类型。)
bob=1
type(bob)
int
bob="bob"
str
type
与之相比,C语言的旧方言是弱静态类型的,因此指针和整数几乎可以互换。(现代的ISO C在很多情况下都需要转换,但我的编译器在默认情况下对此还是很宽容的。)
我必须补充的是,强类型和弱类型更多的是一个连续的而不是布尔选择。C++的类型比C(更需要转换)更强,但是可以通过使用指针转换来颠覆类型系统。
动态语言(如Python)中类型系统的强度实际上取决于其原语和库函数如何响应不同的类型。E、 例如,+被重载,因此它在两个数字或两个字符串上工作,而不是一个字符串和一个数字。这是在实现+时所做的设计选择,但实际上并不是遵循语言语义的必要条件。实际上,当您在自定义类型上重载+时,可以使它隐式地将任何内容转换为数字:
+
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
(我知道的唯一一种完全强类型(也称为严格类型)的语言是Haskell,其中类型是完全不相交的,通过类型类只能有一种受控的重载形式。)
你把'strongly typed'和'dynamically typed'搞混了。
我不能通过添加字符串'12'来更改1的类型,但我可以选择在变量中存储的类型,并在程序运行时更改该类型。
'12'
1
与动态类型相反的是静态类型;变量类型的声明在程序的生存期内不会改变。强类型的反面是弱类型;值的类型可以在程序的生存期内更改。
有一些重要的问题,我认为所有现有的答案都错过了。
弱类型意味着允许访问底层表示。在C语言中,我可以创建一个指向字符的指针,然后告诉编译器我想将它用作指向整数的指针:
char sz[] = "abcdefg"; int *i = (int *)sz;
在一个32位整数的小endian平台上,这将i变成一个由数字0x64636261和0x00676665组成的数组。实际上,您甚至可以将指针本身强制转换为整数(大小适当):
i
0x64636261
0x00676665
intptr_t i = (intptr_t)&sz;
当然,这意味着我可以覆盖系统中任何地方的内存。*
char *spam = (char *)0x12345678 spam[0] = 0;
*当然,现代操作系统使用虚拟内存和页面保护,所以我只能覆盖自己进程的内存,但C本身并没有提供这样的保护,任何曾经在经典Mac OS或Win16上编写代码的人都可以告诉您。
传统的Lisp允许类似的黑客攻击;在某些平台上,双字浮点数和cons单元格是同一类型的,您只需将其中一个传递给期望另一个的函数,它就会“工作”。
今天的大多数语言并没有C和Lisp那么弱,但是它们中的许多仍然有点漏洞百出。例如,任何有未检查的“downcast”的OO语言,*这是一个类型泄漏:你实际上是在告诉编译器“我知道我没有给你足够的信息来知道这是安全的,但是我很确定它是安全的”,当类型系统的整个要点是编译器总是有足够的信息来知道什么是安全的。
*选中的向下转换不会因为将检查移动到运行时而使语言的类型系统变得更弱。如果是的话,那么子类型多态性(也称为虚拟或完全动态函数调用)将是对类型系统的同样违反,我认为没有人想这么说。
很少有“脚本”语言在这个意义上是弱的。即使在Perl或Tcl中,也不能接受字符串,而只能将其字节解释为整数。*但值得注意的是,在Cpyhon中(对于许多语言的其他解释器也是如此),如果您是真正的持久解释器,则可以使用ctypes加载libpython,将对象的id转换为POINTER(Py_Object),并强制类型系统泄漏。这是否会使类型系统变弱取决于您的用例如果您试图实现语言内受限的执行沙盒以确保安全,那么您必须处理这些类型的转义
ctypes
libpython
id
POINTER(Py_Object)
*您可以使用类似于struct.unpack的函数读取字节,并根据“C如何表示这些字节”构建一个新的int,但这显然不是泄漏的;甚至Haskell也允许这样做。
struct.unpack
同时,隐式转换与弱类型或泄漏类型的系统是不同的。
每种语言,甚至是Haskell,都有函数将整数转换为字符串或浮点数。但是有些语言会自动为你做一些转换——例如,在C语言中,如果你调用一个需要float的函数,然后你把它传入int,它就会为你转换。这肯定会导致错误,例如,意外溢出,但它们与从弱类型系统获得的错误不同。而且C在这里并不弱;您可以在Haskell中添加一个in t和一个float,甚至将一个float连接到一个字符串,您只需要更显式地执行它。
float
对于动态语言,这是相当模糊的。在Python或Perl中没有“需要浮点运算的函数”。但是有一些重载函数用不同的类型做不同的事情,并且有一种强烈的直觉感觉,例如,向其他对象添加字符串就是“需要字符串的函数”。从这个意义上讲,Perl、Tcl和JavaScript似乎做了很多隐式转换("a" + 1给您提供了"a1"),而Python做的更少("a" + 1引发了一个异常,但是1.0 + 1给您提供了2.0)。很难用正式的术语来表达这个意思当显然还有其他函数(比如索引)时,难道不应该有一个带字符串和int的+函数吗?
"a" + 1
"a1"
1.0 + 1
2.0
*实际上,在现代Python中,这可以用OO子类型来解释,因为isinstance(2, numbers.Real)是真的。我不认为2在Perl或JavaScript中是字符串类型的实例……尽管在Tcl中,它实际上是,因为所有都是字符串的实例。
isinstance(2, numbers.Real)
2
最后,还有另一个完全正交的“强”与“弱”类型的定义,其中“强”意味着强大/灵活/富有表现力。
例如,Haskell允许您定义一个类型,它是一个数字、一个字符串、一个此类型的列表,或者一个从字符串到此类型的映射,这是一种完美的方式来表示任何可以从JSON解码的内容。在Java中没有办法定义这样的类型。但至少Java有参数(泛型)类型,所以您可以编写一个函数,它接受一个T列表,并且知道元素是T类型的;其他语言,比如早期的Java,强迫您使用一个Object和downcast列表。但至少Java允许您用自己的方法创建新类型;C只允许您创建结构。BCPL甚至没有这个。一直到汇编,这里唯一的类型是不同的位长度。
因此,从这个意义上说,Haskell的类型系统比现代Java的强,后者比早期的Java强,后者比C的强,后者比BCPL的强
那么,Python在哪些方面适合这个范围呢?这有点棘手。在许多情况下,duck类型允许您模拟Haskell中可以执行的所有操作,甚至可以模拟一些您不能执行的操作;当然,错误是在运行时捕获的,而不是在编译时捕获的,但它们仍然会被捕获。然而,在有些情况下,duck类型是不够的。例如,在Haskell中,可以知道一个空的int列表是一个int列表,因此可以决定在该列表上减少+应该返回0*;在Python中,一个空列表是一个空列表;没有类型信息可以帮助您决定在其上减少+应该做什么。
*事实上,Haskell不允许这样做;如果调用reduce函数,而该函数不接受空列表中的起始值,则会得到一个错误。但是它的类型系统足够强大,你可以让它工作,而Python的则不行
Python是强动态类型的。
以你为例
这是因为变量没有类型;它可以命名任何对象。在
bob=1
之后,您会发现type(bob)
返回int
,但是在bob="bob"
之后,它返回str
。(注意type
是一个正则函数,因此它计算其参数,然后返回值的类型。)与之相比,C语言的旧方言是弱静态类型的,因此指针和整数几乎可以互换。(现代的ISO C在很多情况下都需要转换,但我的编译器在默认情况下对此还是很宽容的。)
我必须补充的是,强类型和弱类型更多的是一个连续的而不是布尔选择。C++的类型比C(更需要转换)更强,但是可以通过使用指针转换来颠覆类型系统。
动态语言(如Python)中类型系统的强度实际上取决于其原语和库函数如何响应不同的类型。E、 例如,
+
被重载,因此它在两个数字或两个字符串上工作,而不是一个字符串和一个数字。这是在实现+
时所做的设计选择,但实际上并不是遵循语言语义的必要条件。实际上,当您在自定义类型上重载+
时,可以使它隐式地将任何内容转换为数字:(我知道的唯一一种完全强类型(也称为严格类型)的语言是Haskell,其中类型是完全不相交的,通过类型类只能有一种受控的重载形式。)
你把'strongly typed'和'dynamically typed'搞混了。
我不能通过添加字符串
'12'
来更改1
的类型,但我可以选择在变量中存储的类型,并在程序运行时更改该类型。与动态类型相反的是静态类型;变量类型的声明在程序的生存期内不会改变。强类型的反面是弱类型;值的类型可以在程序的生存期内更改。
有一些重要的问题,我认为所有现有的答案都错过了。
弱类型意味着允许访问底层表示。在C语言中,我可以创建一个指向字符的指针,然后告诉编译器我想将它用作指向整数的指针:
在一个32位整数的小endian平台上,这将
i
变成一个由数字0x64636261
和0x00676665
组成的数组。实际上,您甚至可以将指针本身强制转换为整数(大小适当):当然,这意味着我可以覆盖系统中任何地方的内存。*
*当然,现代操作系统使用虚拟内存和页面保护,所以我只能覆盖自己进程的内存,但C本身并没有提供这样的保护,任何曾经在经典Mac OS或Win16上编写代码的人都可以告诉您。
传统的Lisp允许类似的黑客攻击;在某些平台上,双字浮点数和cons单元格是同一类型的,您只需将其中一个传递给期望另一个的函数,它就会“工作”。
今天的大多数语言并没有C和Lisp那么弱,但是它们中的许多仍然有点漏洞百出。例如,任何有未检查的“downcast”的OO语言,*这是一个类型泄漏:你实际上是在告诉编译器“我知道我没有给你足够的信息来知道这是安全的,但是我很确定它是安全的”,当类型系统的整个要点是编译器总是有足够的信息来知道什么是安全的。
*选中的向下转换不会因为将检查移动到运行时而使语言的类型系统变得更弱。如果是的话,那么子类型多态性(也称为虚拟或完全动态函数调用)将是对类型系统的同样违反,我认为没有人想这么说。
很少有“脚本”语言在这个意义上是弱的。即使在Perl或Tcl中,也不能接受字符串,而只能将其字节解释为整数。*但值得注意的是,在Cpyhon中(对于许多语言的其他解释器也是如此),如果您是真正的持久解释器,则可以使用
ctypes
加载libpython
,将对象的id
转换为POINTER(Py_Object)
,并强制类型系统泄漏。这是否会使类型系统变弱取决于您的用例如果您试图实现语言内受限的执行沙盒以确保安全,那么您必须处理这些类型的转义*您可以使用类似于
struct.unpack
的函数读取字节,并根据“C如何表示这些字节”构建一个新的int,但这显然不是泄漏的;甚至Haskell也允许这样做。同时,隐式转换与弱类型或泄漏类型的系统是不同的。
每种语言,甚至是Haskell,都有函数将整数转换为字符串或浮点数。但是有些语言会自动为你做一些转换——例如,在C语言中,如果你调用一个需要
float
的函数,然后你把它传入int
,它就会为你转换。这肯定会导致错误,例如,意外溢出,但它们与从弱类型系统获得的错误不同。而且C在这里并不弱;您可以在Haskell中添加一个in t和一个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有参数(泛型)类型,所以您可以编写一个函数,它接受一个T列表,并且知道元素是T类型的;其他语言,比如早期的Java,强迫您使用一个Object和downcast列表。但至少Java允许您用自己的方法创建新类型;C只允许您创建结构。BCPL甚至没有这个。一直到汇编,这里唯一的类型是不同的位长度。
因此,从这个意义上说,Haskell的类型系统比现代Java的强,后者比早期的Java强,后者比C的强,后者比BCPL的强
那么,Python在哪些方面适合这个范围呢?这有点棘手。在许多情况下,duck类型允许您模拟Haskell中可以执行的所有操作,甚至可以模拟一些您不能执行的操作;当然,错误是在运行时捕获的,而不是在编译时捕获的,但它们仍然会被捕获。然而,在有些情况下,duck类型是不够的。例如,在Haskell中,可以知道一个空的int列表是一个int列表,因此可以决定在该列表上减少
+
应该返回0*;在Python中,一个空列表是一个空列表;没有类型信息可以帮助您决定在其上减少+
应该做什么。*事实上,Haskell不允许这样做;如果调用reduce函数,而该函数不接受空列表中的起始值,则会得到一个错误。但是它的类型系统足够强大,你可以让它工作,而Python的则不行
相关问题 更多 >
编程相关推荐