强弱类型不仅是关于有多少值被语言自动强制转换为另一种数据类型的连续统一体,还在于实际值的强弱程度 被输入。在 Python 和 Java 中,尤其是在 C# 中,值的类型是一成不变的。在 Perl 中,并没有那么多——实际上只有少数几种不同的值类型可以存储在一个变量中。
让我们一一打开案例。
Python
在 Python 示例 1 + "1" 中,+ 运算符为 int 类型调用 __add__,并为其提供字符串 "1" 作为参数 - 但是,这会导致 NotImplemented:
>>> (1).__add__('1')
NotImplemented
接下来,解释器尝试str的__radd__:
>>> '1'.__radd__(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute '__radd__'
当它失败时,+ 运算符失败,结果为TypeError: unsupported operand type(s) for +: 'int' and 'str'。因此,该异常并没有说明强类型,但运算符+ 不强制其参数自动为同一类型这一事实表明 Python 不是连续体中最弱类型的语言。
另一方面,在 Python 中 'a' * 5 是实现的:
>>> 'a' * 5
'aaaaa'
也就是说,
>>> 'a'.__mul__(5)
'aaaaa'
操作不同的事实需要一些强类型 - 但是与 * 在相乘之前将值强制为数字相反,仍然不一定会使值弱类型。
Java
Java 示例String result = "1" + 1; 之所以有效,是因为为了方便起见,运算符+ 对字符串进行了重载。 Java + 运算符将序列替换为创建StringBuilder(参见this):
String result = a + b;
// becomes something like
String result = new StringBuilder().append(a).append(b).toString()
这是一个非常静态类型的示例,没有实际的强制 - StringBuilder 有一个方法 append(Object) 在这里专门使用。文档说明如下:
附加Object 参数的字符串表示形式。
整体效果就像将参数转换为
通过String.valueOf(Object) 方法获得的字符串,以及
然后将该字符串附加到此字符序列中。
String.valueOf 然后在哪里
返回 Object 参数的字符串表示形式。
[返回] 如果参数是null,则等于"null"的字符串;否则返回obj.toString()的值。
因此,这是一个绝对没有语言强制的情况 - 将所有关注点委托给对象本身。
C#
根据Jon Skeet answer here,运算符+ 甚至没有为string 类重载——类似于Java,这只是编译器生成的便利,这要归功于静态和强类型。
Perl
正如perldata 解释的那样,
Perl 具有三种内置数据类型:标量、标量数组和标量关联数组,称为“哈希”。标量是单个字符串(任何大小,仅受可用内存限制)、数字或对某物的引用(将在 perlref 中讨论)。普通数组是按数字索引的标量的有序列表,从 0 开始。散列是按相关字符串键索引的标量值的无序集合。
然而,Perl 没有单独的数据类型用于数字、布尔值、字符串、空值、undefineds、对其他对象的引用等——它只有一种类型,即标量类型; 0 是与“0”一样多的标量值。设置为字符串的标量 变量 可以真正变为数字,并且从那里开始的行为不同于“只是一个字符串”if it is accessed in a numerical context。标量可以容纳 Perl 中的任何东西,它与系统中存在的对象一样多。而在 Python 中,名称只是指对象,而在 Perl 中,名称中的标量值是可变对象。此外,面向对象的类型系统粘在此之上:perl 中只有 3 种数据类型——标量、列表和散列。 Perl 中的用户定义对象是对包的引用(即指向前 3 个中的任何一个的指针)blessed - 您可以随时获取任何此类值并祝福它到任何类。
Perl 甚至允许您随心所欲地更改值的类 - 这在 Python 中是不可能的,在 Python 中创建某个类的值您需要使用 object.__new__ 或类似名称显式构造属于该类的值。在 Python 中你不能真正改变对象的本质,而在 Perl 中你可以做很多事情:
package Foo;
package Bar;
my $val = 42;
# $val is now a scalar value set from double
bless \$val, Foo;
# all references to $val now belong to class Foo
my $obj = \$val;
# now $obj refers to the SV stored in $val
# thus this prints: Foo=SCALAR(0x1c7d8c8)
print \$val, "\n";
# all references to $val now belong to class Bar
bless \$val, Bar;
# thus this prints Bar=SCALAR(0x1c7d8c8)
print \$val, "\n";
# we change the value stored in $val from number to a string
$val = 'abc';
# yet still the SV is blessed: Bar=SCALAR(0x1c7d8c8)
print \$val, "\n";
# and on the course, the $obj now refers to a "Bar" even though
# at the time of copying it did refer to a "Foo".
print $obj, "\n";
因此类型标识被弱绑定到变量,并且可以通过任何动态引用来改变它。事实上,如果你这样做了
my $another = $val;
\$another 没有类标识,尽管\$val 仍将提供祝福引用。
TL;DR
对于 Perl 来说,弱类型不仅仅是自动强制,更重要的是值的类型本身并不是一成不变的,这与 Python 不同,Python 是一种动态但非常强类型的语言。 python 在1 + "1" 上给出TypeError 表明该语言是强类型的,尽管相反的做一些有用的事情,如在Java 或C# 中并不排除它们是强类型语言。