Python是强类型的吗?

问题:Python是强类型的吗?

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

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

bob = 1
bob = "bob"

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

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

I’ve come across links that say Python is a strongly typed language.

However, I thought in strongly typed languages you couldn’t do this:

bob = 1
bob = "bob"

I thought a strongly typed language didn’t accept type-changing at run-time. Maybe I’ve got a wrong (or too simplistic) definition of strong/weak types.

So, is Python a strongly or weakly typed language?


回答 0

Python是强大的动态类型。

  • 类型意味着值的类型不会以意外的方式改变。仅包含数字的字符串不会神奇地变成数字,这在Perl中可能会发生。类型的每次更改都需要显式转换。
  • 动态类型意味着运行时对象(值)具有类型,而静态类型则是变量具有类型。

至于你的例子

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是完全精细与添加类型的对象intfloat类型的对象,并返回float(例如,int(42) + float(1)返回43.0)。另一方面,由于类型之间的不匹配,如果尝试以下操作,Haskell将抱怨(42 :: Integer) + (1 :: Float)。这使Haskell成为严格类型化的语言,其中类型完全不相交,并且只能通过类型类控制重载形式。

Python is strongly, dynamically typed.

  • Strong typing means that the type of a value doesn’t change in unexpected ways. A string containing only digits doesn’t magically become a number, as may happen in Perl. Every change of type requires an explicit conversion.
  • Dynamic typing means that runtime objects (values) have a type, as opposed to static typing where variables have a type.

As for your example

bob = 1
bob = "bob"

This works because the variable does not have a type; it can name any object. After bob=1, you’ll find that type(bob) returns int, but after bob="bob", it returns str. (Note that type is a regular function, so it evaluates its argument, then returns the type of the value.)

Contrast this with older dialects of C, which were weakly, statically typed, so that pointers and integers were pretty much interchangeable. (Modern ISO C requires conversions in many cases, but my compiler is still lenient about this by default.)

I must add that the strong vs. weak typing is more of a continuum than a boolean choice. C++ has stronger typing than C (more conversions required), but the type system can be subverted by using pointer casts.

The strength of the type system in a dynamic language such as Python is really determined by how its primitives and library functions respond to different types. E.g., + is overloaded so that it works on two numbers or two strings, but not a string and an number. This is a design choice made when + was implemented, but not really a necessity following from the language’s semantics. In fact, when you overload + on a custom type, you can make it implicitly convert anything to a number:

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)

Instance of class Foo can be added to other objects:

>>> a = Foo(42)
>>> a + "1"
43.0
>>> a + Foo
42
>>> a + 1
43.0
>>> a + None
42

Observe that even though strongly typed Python is completely fine with adding objects of type int and float and returns an object of type float (e.g., int(42) + float(1) returns 43.0). On the other hand, due to the mismatch between types Haskell would complain if one tries the following (42 :: Integer) + (1 :: Float). This makes Haskell a strictly typed language, where types are entirely disjoint and only a controlled form of overloading is possible via type classes.


回答 1

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


弱类型意味着允许访问基础表示。在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,将对象投射idPOINTER(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则不能。

There are some important issues that I think all of the existing answers have missed.


Weak typing means allowing access to the underlying representation. In C, I can create a pointer to characters, then tell the compiler I want to use it as a pointer to integers:

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

On a little-endian platform with 32-bit integers, this makes i into an array of the numbers 0x64636261 and 0x00676665. In fact, you can even cast pointers themselves to integers (of the appropriate size):

intptr_t i = (intptr_t)&sz;

And of course this means I can overwrite memory anywhere in the system.*

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

* Of course modern OS’s use virtual memory and page protection so I can only overwrite my own process’s memory, but there’s nothing about C itself that offers such protection, as anyone who ever coded on, say, Classic Mac OS or Win16 can tell you.

Traditional Lisp allowed similar kinds of hackery; on some platforms, double-word floats and cons cells were the same type, and you could just pass one to a function expecting the other and it would “work”.

Most languages today aren’t quite as weak as C and Lisp were, but many of them are still somewhat leaky. For example, any OO language that has an unchecked “downcast”,* that’s a type leak: you’re essentially telling the compiler “I know I didn’t give you enough information to know this is safe, but I’m pretty sure it is,” when the whole point of a type system is that the compiler always has enough information to know what’s safe.

* A checked downcast doesn’t make the language’s type system any weaker just because it moves the check to runtime. If it did, then subtype polymorphism (aka virtual or fully-dynamic function calls) would be the same violation of the type system, and I don’t think anyone wants to say that.

Very few “scripting” languages are weak in this sense. Even in Perl or Tcl, you can’t take a string and just interpret its bytes as an integer.* But it’s worth noting that in CPython (and similarly for many other interpreters for many languages), if you’re really persistent, you can use ctypes to load up libpython, cast an object’s id to a POINTER(Py_Object), and force the type system to leak. Whether this makes the type system weak or not depends on your use cases—if you’re trying to implement an in-language restricted execution sandbox to ensure security, you do have to deal with these kinds of escapes…

* You can use a function like struct.unpack to read the bytes and build a new int out of “how C would represent these bytes”, but that’s obviously not leaky; even Haskell allows that.


Meanwhile, implicit conversion is really a different thing from a weak or leaky type system.

Every language, even Haskell, has functions to, say, convert an integer to a string or a float. But some languages will do some of those conversions for you automatically—e.g., in C, if you call a function that wants a float, and you pass it in int, it gets converted for you. This can definitely lead to bugs with, e.g., unexpected overflows, but they’re not the same kinds of bugs you get from a weak type system. And C isn’t really being any weaker here; you can add an int and a float in Haskell, or even concatenate a float to a string, you just have to do it more explicitly.

And with dynamic languages, this is pretty murky. There’s no such thing as “a function that wants a float” in Python or Perl. But there are overloaded functions that do different things with different types, and there’s a strong intuitive sense that, e.g., adding a string to something else is “a function that wants a string”. In that sense, Perl, Tcl, and JavaScript appear to do a lot of implicit conversions ("a" + 1 gives you "a1"), while Python does a lot fewer ("a" + 1 raises an exception, but 1.0 + 1 does give you 2.0*). It’s just hard to put that sense into formal terms—why shouldn’t there be a + that takes a string and an int, when there are obviously other functions, like indexing, that do?

* Actually, in modern Python, that can be explained in terms of OO subtyping, since isinstance(2, numbers.Real) is true. I don’t think there’s any sense in which 2 is an instance of the string type in Perl or JavaScript… although in Tcl, it actually is, since everything is an instance of string.


Finally, there’s another, completely orthogonal, definition of “strong” vs. “weak” typing, where “strong” means powerful/flexible/expressive.

For example, Haskell lets you define a type that’s a number, a string, a list of this type, or a map from strings to this type, which is a perfectly way to represent anything that can be decoded from JSON. There’s no way to define such a type in Java. But at least Java has parametric (generic) types, so you can write a function that takes a List of T and know that the elements are of type T; other languages, like early Java, forced you to use a List of Object and downcast. But at least Java lets you create new types with their own methods; C only lets you create structures. And BCPL didn’t even have that. And so on down to assembly, where the only types are different bit lengths.

So, in that sense, Haskell’s type system is stronger than modern Java’s, which is stronger than earlier Java’s, which is stronger than C’s, which is stronger than BCPL’s.

So, where does Python fit into that spectrum? That’s a bit tricky. In many cases, duck typing allows you to simulate everything you can do in Haskell, and even some things you can’t; sure, errors are caught at runtime instead of compile time, but they’re still caught. However, there are cases where duck typing isn’t sufficient. For example, in Haskell, you can tell that an empty list of ints is a list of ints, so you can decide that reducing + over that list should return 0*; in Python, an empty list is an empty list; there’s no type information to help you decide what reducing + over it should do.

* In fact, Haskell doesn’t let you do this; if you call the reduce function that doesn’t take a start value on an empty list, you get an error. But its type system is powerful enough that you could make this work, and Python’s isn’t.


回答 2

您将“强类型”“动态类型”混淆了。

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

动态类型的反义词是静态类型。变量类型声明在程序生存期内不会更改。强类型的反义词是弱类型。的类型可以在程序的生存期内更改。

You are confusing ‘strongly typed’ with ‘dynamically typed’.

I cannot change the type of 1 by adding the string '12', but I can choose what types I store in a variable and change that during the program’s run time.

The opposite of dynamic typing is static typing; the declaration of variable types doesn’t change during the lifetime of a program. The opposite of strong typing is weak typing; the type of values can change during the lifetime of a program.


回答 3

根据该Wiki Python文章,Python是动态且强类型的(也提供了很好的解释)。

也许您正在考虑使用静态类型的语言,这些类型的语言在程序执行期间不能更改,并且在编译期间进行类型检查以检测可能的错误。

这样的SO问题可能很有趣:动态类型语言与静态类型语言,此有关Type Systems的 Wikipedia文章提供了更多信息

According to this wiki Python article Python is both dynamically and strongly typed (provides a good explanation too).

Perhaps you are thinking about statically typed languages where types can not change during program execution and type checking occurs during compile time to detect possible errors.

This SO question might be of interest: Dynamic type languages versus static type languages and this Wikipedia article on Type Systems provides more information


回答 4

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'

TLDR;

Python typing is Dynamic so you can change a string variable to an int

x = 'somestring'
x = 50

Python typing is Strong so you can’t merge types:

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

In weakly-typed Javascript this happens…

 'foo'+3 = 'foo3'

Regarding Type Inference

Java forces you to explicitly declare your object types

int x = 50;

Kotlin uses inference to realize it’s an int

x = 50

But because both languages use static types, x can’t be changed from an int. Neither language would allow a dynamic change like

x = 50
x = 'now a string'

回答 5

它已经被回答过几次了,但是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

It’s already been answered a few times, but Python is a strongly typed language:

>>> 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'

The following in JavaScript:

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

That’s the difference between weak typing and strong typing. Weak types automatically try to convert from one type to another, depending on context (e.g. Perl). Strong types never convert implicitly.

Your confusion lies in a misunderstanding of how Python binds values to names (commonly referred to as variables).

In Python, names have no types, so you can do things like:

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

And names can be bound to anything:

>>> def spam():
...     print("Spam, spam, spam, spam")
...
>>> spam_on_eggs = spam
>>> spam_on_eggs()
Spam, spam, spam, spam

For further reading:

https://en.wikipedia.org/wiki/Dynamic_dispatch

and the slightly related but more advanced:

http://effbot.org/zone/call-by-object.htm


回答 6

Python变量存储对表示值的目标对象的无类型引用。

任何分配操作都意味着将无类型的引用分配给所分配的对象-即,该对象是通过原始引用和新的(计数的)引用共享的。

值类型绑定到目标对象,而不是参考值。当执行带有值的操作(运行时)时,将进行(强)类型检查。

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

A Python variable stores an untyped reference to the target object that represent the value.

Any assignment operation means assigning the untyped reference to the assigned object — i.e. the object is shared via the original and the new (counted) references.

The value type is bound to the target object, not to the reference value. The (strong) type checking is done when an operation with the value is performed (run time).

In other words, variables (technically) have no type — it does not make sense to think in terms of a variable type if one wants to be exact. But references are automatically dereferenced and we actually think in terms of the type of the target object.


回答 7

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

因此,该术语的使用取决于您与谁交谈。

我不考虑任何没有明确声明变量类型或静态类型都不是强类型的语言。

强类型化不仅会阻止转换(例如,“自动”从整数转换为字符串)。它排除了赋值(即更改变量的类型)的可能性。

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

Foo = 1 Foo =“ 1”

在强类型语言中,程序员可以“依靠”类型。

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

UINT64 kZarkCount;

并且他或她知道20行之后,kZarkCount仍然是UINT64(只要它出现在同一块中),而无需检查中间代码。

The term “strong typing” does not have a definite definition.

Therefore, the use of the term depends on with whom you’re speaking.

I do not consider any language, in which the type of a variable is not either explicitly declared, or statically typed to be strongly typed.

Strong typing doesn’t just preclude conversion (for example, “automatically” converting from an integer to a string). It precludes assignment (i.e., changing the type of a variable).

If the following code compiles (interprets), the language is not strong-typed:

Foo = 1 Foo = “1”

In a strongly typed language, a programmer can “count on” a type.

For example, if a programmer sees the declaration,

UINT64 kZarkCount;

and he or she knows that 20 lines later, kZarkCount is still a UINT64 (as long as it occurs in the same block) – without having to examine intervening code.


回答 8

我刚刚发现了一种精妙的简洁方式来记忆它:

动态/静态类型的表达式;强/弱类型值。

I just discovered a superb concise way to memorize it:

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


回答 9

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

>>> 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
    }

i think, this simple example should you explain the diffs between strong and dynamic typing:

>>> 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
    }

回答 10

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....

在长时间内,以上内容将在大型系统中造成无法维护的代码的噩梦。称它为您想要的东西,但是“动态”更改变量类型的能力只是一个坏主意…

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....

The above would create a nightmare of unmaintainable code in a large system over a long period time. Call it what you want, but the ability to “dynamically” change a variables type is just a bad idea…