Pythonは強く型付けされていますか?


234

Pythonは強く型付けされた言語であるというリンクを見つけました。

しかし、強く型付けされた言語ではこれはできないと思いました:

bob = 1
bob = "bob"

強く型付けされた言語は、実行時に型の変更を受け入れないと思いました。多分私は強い/弱いタイプの間違った(またはあまりに単純化した)定義を持っています。

では、Pythonは強く型付けされた言語か弱い型付けされた言語ですか?

回答:


359

Pythonは強く動的に型付けされています。

  • 強い型付けとは、値の型が予期しない方法で変化しないことを意味します。数字のみを含む文字列は、Perlで発生する可能性のあるように、魔法のように数値になることはありません。タイプを変更するたびに、明示的な変換が必要です。
  • 動的型付けとは、変数に型がある静的型付けとは対照的に、ランタイムオブジェクト(値)に型があることを意味します。

あなたの例は

bob = 1
bob = "bob"

これは、変数に型がないため機能します。任意のオブジェクトに名前を付けることができます。後bob=1、がtype(bob)返されることがわかりますintが、後bob="bob"はが返されますstr。(これtypeは通常の関数なので、引数を評価してから値の型を返します。)

これを、静的かつ静的に型付けされたCの古い方言とは対照的に、ポインターと整数はほとんど交換可能でした。(最新のISO Cは多くの場合変換を必要としますが、私のコンパイラーはデフォルトでこれについてまだ寛容です。)

強い型付けと弱い型付けは、ブール値の選択というよりも連続型のほうが多いことを付け加えておきます。C ++の型付けはCよりも強力です(より多くの変換が必要です)が、ポインターキャストを使用して型システムを覆すことができます。

Pythonなどの動的言語の型システムの強さは、プリミティブとライブラリ関数がさまざまな型にどのように応答するかによって実際に決まります。たとえば、+2つの数値または 2つの文字列では機能するが、文字列と数値では機能しないようにオーバーロードされます。これは、+が実装されたときに行われた設計上の選択ですが、実際には言語のセマンティクスに従う必要はありません。実際、+カスタムタイプをオーバーロードすると、暗黙的に何かを数値に変換できます。

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は厳密に型指定された言語になり、型は完全にばらばらになり、制御された形式のオーバーロードのみが型クラスを介して可能になります。


18
私は非常に頻繁に表示されていないが、私が考える一つの例は、Pythonのは、完全に強く型付けされていないことを示すことが重要であるブール値と評価さすべてのものです:docs.python.org/release/2.5.2/lib/truth.html
gsingh2011 2013

25
これが反例であるかどうかはわかりません。物事はブール値として評価できますが、突然ブール値になるわけではありません。まるで、誰かが暗黙的にas_​​boolean(<value>)のようなものを呼び出したようなものです。これは、変化するオブジェクト自体のタイプと同じではありませんよね?
jbrendel 2014年

15
何が実際に変換されていないので、真偽値コンテキストでtruthyであることは、反例ではありませんTrueFalse。しかし、番号の昇格はどうですか?1.0 + 2PythonでもPerlやCと同じように"1.0" + 2機能しますが、機能しません。@jbrendelに同意します。これは実際には暗黙的な変換ではなく、単なるオーバーロードですが、同じ意味で、Perlは暗黙的な変換も行っていません。関数にパラメーター型が宣言されていない場合、暗黙的な変換が行われる場所はありません。
abarnert 2014

13
強い型付けについて考えるより良い方法は、変数に対して演算を実行するときに型が重要であることです。型が期待どおりでない場合、不平を言う言語は強く型付けされています(python / java)とそうでないものは弱く型付けされています(javascript) 動的に型付けされた言語(python)は、変数の型を変更できる言語です一方、実行時に静的型付けの変数が宣言された後の言語(Javaは)これを許可しません。
kashif

2
@ gsingh2011真実性は有用であり、それ自体では弱いタイピングではありませんが、偶発的if isValid(value) - 1なリークが発生する可能性があります。ブール値は整数に強制変換され、それが真の値として評価されます。False - 1真実にTrue - 1なり、偽りになり、デバッグするのが恥ずかしいほど困難な2層オフバイワンエラーにつながります。この意味で、Pythonはほとんど強く型付けされています。型強制は通常、論理エラーを引き起こしません。
Aaron3468

57

既存の答えのすべてが見逃していると私は思ういくつかの重要な問題があります。


弱い型付けとは、基になる表現へのアクセスを許可することを意味します。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;

*もちろん、最新のOSは仮想メモリとページ保護を使用しているため、自分のプロセスのメモリのみを上書きできますが、C自体については、そのような保護を提供するものはありません。

従来のLispは同様のハッカーを許可していました。一部のプラットフォームでは、ダブルワードフロートとコンスセルは同じ型であり、一方を他方を期待する関数に渡すだけで、「機能」します。

今日のほとんどの言語は、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をとるa があるべきではないのでしょうか。

*実際には、最新のPythonでは、これisinstance(2, numbers.Real)はtrue であるため、OOサブタイピングの観点から説明できます。2PerlやJavaScriptの文字列型のインスタンスがどれであるかはわかりません。Tclでは実際にはそうですが、すべてが文字列のインスタンスであるためです。


最後に、「強い」と「弱い」タイピングの完全に直交する別の定義があります。「強い」とは、強力/柔軟性/表現力を意味します。

たとえば、Haskellでは、数値、文字列、このタイプのリスト、または文字列からこのタイプへのマップであるタイプを定義できます。これは、JSONからデコードできるものを完全に表現する方法です。そのような型をJavaで定義する方法はありません。ただし、少なくともJavaにはパラメトリック(ジェネリック)型があるため、Tのリストを受け取り、要素の型がTであることを認識する関数を作成できます。初期のJavaなどの他の言語では、オブジェクトのリストとダウンキャストを使用する必要がありました。しかし、少なくともJavaでは、独自のメソッドで新しい型を作成できます。Cは構造体の作成のみを許可します。そしてBCPLにはそれさえありませんでした。そして、アセンブリまで続きます。ここでは、唯一のタイプは異なるビット長です。

したがって、その意味で、Haskellの型システムは、以前のJavaよりも強く、Cよりも強く、BCPLよりも強力な、現在のJavaよりも強力です。

では、Pythonはそのスペクトルにどこに適合するのでしょうか?それは少しトリッキーです。多くの場合、ダックタイピングを使用すると、Haskellで実行できるすべてのことをシミュレートできます。確かに、エラーはコンパイル時ではなく実行時にキャッチされますが、それでもキャッチされます。ただし、アヒルのタイピングでは不十分な場合もあります。たとえば、Haskellでは、intの空のリストがintのリストであること+を認識できるため、そのリストを削減すると0 *が返されるように決定できます。Pythonでは、空のリストは空のリストです。削減+が何をすべきかを決定するのに役立つタイプ情報はありません。

*実際、Haskellではこれを許可していません。空のリストで開始値をとらないreduce関数を呼び出すと、エラーが発生します。しかし、その型システムは強力であり、これを機能させることができます、Pythonの機能は機能しません。


3
この答えは素晴らしいです!リストの一番下でそれが長い間衰えているのは残念です。
LeoR

1
Cの例に対するほんの少しのコメント:char sz[]charへのポインタではなく、charの配列であり、割り当てではポインタに減衰します。
majkel.mk 2017

39

「強く型付けされた」「動的に型付けされた」を混同しています。

1文字列を追加してのタイプを変更することはできません'12'が、変数に格納するタイプを選択して、プログラムの実行中に変更することができます。

動的型付けの反対は静的型付けです。変数の型宣言は、プログラムの存続期間中に変更されません。強い型付けの反対は弱い型付けです。のタイプは、プログラムの存続期間中に変化する可能性があります


強く型付けされたリンクの説明:「一般に、強く型付けされた言語にはコンパイル時により厳密な型規則があります。これは、コンパイル中にエラーや例外が発生する可能性が高いことを意味します。」Pythonが弱く型付けされた言語であることを意味します...、wikiは間違っていますか?

1
@s̮̦̩e̝͓c̮͔̞ṛ̖̖e̬̣̦t̸͉̥̳̼:それはまったく意味されていません。Pythonにはコンパイル時の厳密な型指定ルールがあり、作成される各オブジェクトには1つの型しかありません。そして、「一般的に」は何も意味しません。それは、Pythonがその例外であることを意味します。
Martijn Pieters

24

このwikiのPython記事によると、Pythonは動的かつ強く型付けされています(良い説明も提供されています)。

おそらく、プログラムの実行中に型を変更できない静的型付き言語について考えている可能性があり、コンパイル時に型チェックが起こり、エラーの可能性を検出します。

このSOの質問は興味深いかもしれません:動的型言語と静的型言語の比較および型システムに関するこのWikipediaの記事で詳細を説明しています


18

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'

JavaScriptの詳細はわかりませんが、オーバーロードして型変換をバックグラウンドで実行している'x' + 3可能性がありますoperator+か?

3
とにかく、あなたの答えは実際には上記のものよりも簡潔で理解しやすいです。

8

すでに何度か回答されていますが、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


1
数年後-別の有用で関連するリソース:youtu.be/_AEJHKGk9ns
Wayne Werner

強い型付けと弱い型付けは、3 + '4'のような式の結果の型とは関係ありません。この例では、JavaScriptはPythonと同じくらい強力です。
qznc 2015

@qznc Javasriptはどの程度強力ですか?結果の型と関係があるとは思わなかったと思います。確かに、弱い型がある型から別の型に自動的に変換しようとすることを明示的に述べています。
Wayne Werner、

2
@oneloopは必ずしも正しいとは限りません。floatとintを組み合わせるための動作が明確に定義されており、floatが生成されるだけです。あなたは行うことができます"3"*4あまりにも、pythonで。もちろん、結果は"3333"です。あなたはそれがどちらかを変換しているとは言わないでしょう。もちろん、それはセマンティクスを論じているだけかもしれません。
Wayne Werner

1
@oneloop Pythonはのfloat組み合わせから生成されるため、暗黙的に型を変換しているfloatとは限りませんint。floatとintの間には自然な関係があり、実際、タイプ階層はそれを示しています。私はあなたがJavascriptが考えて主張することができると仮定'3'+4し、'e'+4Pythonは考えていることと同じように、明確に定義された操作のことの両方に3.0 + 4定義され、明確に定義されたように、しかし、その時点で、その後、強いまたは弱いタイプのようなものが本当にありません、ただ(UN)操作。
Wayne Werner

6

Python変数は、値を表すターゲットオブジェクトへの型なし参照を格納します。

割り当て操作とは、型指定されていない参照を割り当てられたオブジェクトに割り当てることです。つまり、オブジェクトは、元の参照と新しい(カウントされた)参照を介して共有されます。

値タイプは、参照値ではなくターゲットオブジェクトにバインドされます。(強力な)型チェックは、値を持つ操作が実行されたときに実行されます(実行時)。

言い換えれば、(技術的に)変数には型がありません-正確にしたい場合、変数の型で考えることは意味がありません。しかし、参照は自動的に逆参照され、実際にはターゲットオブジェクトのタイプの観点から考えます。


6

「強い型付け」という用語には明確な定義はありません。

したがって、この用語の使用は、話している相手によって異なります。

変数の型が明示的に宣言されていない、または静的に型付けされていない言語は、強く型付けされているとは考えていません。

強い型付けは、変換を排除するだけではありません(たとえば、整数から文字列への「自動」変換)。代入(つまり、変数の型の変更)ができなくなります。

次のコードがコンパイル(解釈)する場合、言語は厳密に型指定されていません。

Foo = 1 Foo = "1"

強く型付けされた言語では、プログラマは型を「当てに」することができます。

たとえば、プログラマーが宣言を見た場合、

UINT64 kZarkCount;

そして彼または彼女は、20行後、kZarkCountが(同じブロック内にある限り)UINT64のままであることを知っています-介在するコードを調べる必要はありません。


1

私はそれを記憶するための素晴らしい簡潔な方法を発見しました:

動的/静的型付き式。強く/弱く型付けされた値。


0

この単純な例では、強い型付けと動的な型付けの違いについて説明します。

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

Pythonコードは動的型付けを示し、Javaは静的型付けを示します。より良い例は、$ var = '2' + 1です// //結果は3
erichlf、

@ivleph同意する。"a" * 3 == "aaa"
Dmitry Zagorulkin '26 / 02/26

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

上記は、長期間にわたって大規模なシステムで保守不可能なコードの悪夢を生み出します。それをあなたが望むものと呼びますが、変数の型を「動的に」変更する機能は単に悪い考えです...

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.