关于强类型:Python强类型化吗?

Is Python strongly typed?

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

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

1
2
bob = 1
bob ="bob"

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

那么,python是强类型还是弱类型语言?


python是强、动态类型的。

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

至于你的例子

1
2
bob = 1
bob ="bob"

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

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

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

在动态语言(如python)中,类型系统的强度实际上是由它的原语和库函数如何响应不同的类型决定的。例如,+被重载,这样它可以处理两个数字或两个字符串,但不能处理一个字符串和一个数字。这是在实现+时所做的设计选择,但从语言的语义上看,这并不是真正的必要性。实际上,当您在自定义类型上重载+时,可以使它隐式地将任何内容转换为数字:

1
2
3
4
5
6
7
8
9
10
11
12
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,其中类型完全不相交,只有一种受控的重载形式可以通过类型类实现。)


有一些重要的问题,我认为所有现有的答案都遗漏了。好的。

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

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

在一个32位整数的小endian平台上,这使得i成为一个由0x646362610x00676665组成的数字数组。实际上,您甚至可以将指针本身强制转换为整数(大小适当):好的。

1
intptr_t i = (intptr_t)&sz;

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

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

*当然,现代操作系统使用虚拟内存和页面保护,因此我只能覆盖自己进程的内存,但对于提供此类保护的C本身没有任何内容,因为任何曾经编码的人,比如说,Classic Mac OS或Win16都可以告诉您。好的。

传统的Lisp允许类似的黑客攻击;在某些平台上,双字浮点和cons单元是相同的类型,您可以将其中一个传递给一个期望另一个的函数,它将"起作用"。好的。

今天的大多数语言并没有C和Lisp那么弱,但其中许多语言仍然有些漏洞。例如,任何具有未经检查的"downcast"的OO语言,*都是类型泄漏:您本质上是在告诉编译器"我知道我没有给您足够的信息来知道这是安全的,但我很确定它是安全的",当类型系统的关键是编译器总是有足够的信息来知道什么是安全的时。好的。

>*选中的向下转换不会使语言的类型系统变得更弱,因为它将检查移动到运行时。如果是这样,那么子类型多态性(也称为虚拟或完全动态函数调用)将是对类型系统的相同违反,我认为没有人想这么说。好的。

在这个意义上,很少有"脚本"语言是弱的。即使在Perl或Tcl中,也不能取字符串并将其字节解释为整数。*但值得注意的是,在cpython中(和许多其他语言的解释器类似),如果您确实是持久的,则可以使用ctypes加载libpython,将对象的id强制转换为POINTER(Py_Object),并强制类型系统泄漏。这是否会使类型系统变得脆弱取决于您的用例如果您正试图实现一个语言内受限的执行沙盒来确保安全性,那么您必须处理这些类型的转义……好的。

*您可以使用像struct.unpack这样的函数来读取字节并构建一个新的int out of"how c would representate these bytes",但这显然不是泄漏的;即使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的+,当有明显的其他函数(如indexing)的时候?好的。

实际上,在现代python中,这可以用oo子类型来解释,因为isinstance(2, numbers.Real)是真的。我不认为2是Perl或JavaScript中字符串类型的实例……尽管在Tcl中,它实际上是,因为所有东西都是字符串的实例。好的。

最后,还有另一个完全正交的"强"和"弱"类型的定义,"强"表示强大/灵活/富有表现力。好的。

例如,Haskell允许您定义一个类型,它是一个数字、一个字符串、一个这种类型的列表,或者一个从字符串到这种类型的映射,这是一个完美的方法来表示任何可以从JSON解码的东西。在Java中没有定义这种类型的方法。但是至少Java有参数(泛型)类型,所以你可以编写一个函数,该函数接受T的列表,并且知道元素是T型;其他语言,如早期Java,强迫你使用对象列表和下拉列表。但是至少Java允许你用自己的方法创建新的类型;C只允许你创建结构。BCPL甚至没有。这样一直到组装,其中唯一的类型是不同的位长度。好的。

因此,在这个意义上,Haskell的类型系统比现代Java更强大,它比较早的Java强,比C的强,比BCPL强。好的。

那么,python在哪里适合这个范围呢?这有点棘手。在许多情况下,duck类型允许您模拟在haskell中可以做的一切,甚至一些您不能做的事情;当然,错误是在运行时捕获的,而不是在编译时捕获的,但它们仍然被捕获。然而,有些情况下,鸭子打字是不够的。例如,在haskell中,可以告诉您一个int的空列表是一个int的列表,因此您可以决定将+减去该列表应该返回0*;在python中,空列表是一个空列表;没有类型信息来帮助您决定将+减去它应该做什么。好的。

实际上,haskell不允许您这样做;如果您调用reduce函数,该函数不在空列表上获取起始值,则会得到一个错误。但是它的类型系统足够强大,您可以让它工作,而python的则不行。好的。好啊。


您将"强类型"与"动态类型"混淆。

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

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


根据这篇wiki python文章,python是动态的和强类型的(也提供了一个很好的解释)。

也许您考虑的是静态类型语言,在这种语言中,类型在程序执行期间无法更改,并且在编译时进行类型检查以检测可能的错误。

因此,这个问题可能会引起人们的兴趣:动态类型语言与静态类型语言,维基百科关于类型系统的文章提供了更多信息。


TLDR;

python的类型是动态的,因此可以将int变量更改为字符串

1
2
x = 'somestring'
x = 50

python类型很强,因此不能合并类型:

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

在弱类型的javascript中,会发生这种情况…

1
 'x'+3 = 'x3'

关于类型推理

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

1
int x = 50

科特林利用推理意识到这是一个int

1
x = 50

但由于这两种语言都使用静态类型,因此不能将xint更改为x。两种语言都不允许像

1
2
x = 50
x = 'now a string'


已经回答过几次了,但python是一种强类型语言:

1
2
3
4
5
6
>>> 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中的以下内容:

1
2
3
var x = 3    
var y = '4'
alert(x + y) //Produces"34"

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

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

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

1
2
3
bob = 1
bob ="bob"
bob ="An Ex-Parrot!"

名称可以绑定到任何内容:

1
2
3
4
5
6
>>> def spam():
...     print("Spam, spam, spam, spam")
...
>>> spam_on_eggs = spam
>>> spam_on_eggs()
Spam, spam, spam, spam

进一步阅读:

https://en.wikipedia.org/wiki/dynamic_调度

与此稍有关联但更为先进:

http://effbot.org/zone/call-by-object.htm(按对象调用)


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

因此,这个术语的使用取决于你和谁说话。

我不考虑任何一种语言,在这种语言中,变量的类型既没有显式声明,也没有静态类型化为强类型。

强类型不只是排除转换(例如,从整数到字符串的"自动"转换)。它排除了赋值(即改变变量的类型)。

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

FoO=1Fo="1"

在强类型语言中,程序员可以"依赖"一个类型。

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

uint64千南非兰特;

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


python变量存储对表示该值的目标对象的非类型化引用。

任何分配操作都意味着将非类型化的引用分配给分配的对象——也就是说,对象通过原始引用和新的(计数的)引用共享。

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

换句话说,变量(技术上)没有类型——如果你想精确的话,用变量类型来思考是没有意义的。但是引用是自动取消引用的,我们实际上是根据目标对象的类型来考虑的。


我发现了一种非常简洁的记忆方法:

Dynamic/static typed experssion; strongly/weakly typed value.


我认为,这个简单的例子应该解释强类型和动态类型之间的区别:

1
2
3
4
5
6
7
8
>>> tup = ('1', 1, .1)
>>> for item in tup:
...     type(item)
...
<type 'str'>
<type 'int'>
<type 'float'>
>>>

爪哇:

1
2
3
4
5
public static void main(String[] args) {
        int i = 1;
        i ="1"; //will be error
        i = '0.1'; // will be error
    }


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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....

上面的内容将在长时间内给大型系统带来无法维护的代码的噩梦。可以随意调用它,但是动态地更改变量类型的能力只是一个坏主意…