Pythonは強く型付けされた言語であるというリンクに出くわしました。
ただし、強く型付けされた言語では、これを行うことはできないと思いました。
bob = 1
bob = "bob"
強く型付けされた言語は、実行時の型変更を受け入れないと思いました。たぶん私は強い/弱いタイプの間違った(またはあまりにも単純な)定義を持っています。
では、Pythonは強い型の言語ですか、それとも弱い型の言語ですか?
Pythonは強く型付けされた言語であるというリンクに出くわしました。
ただし、強く型付けされた言語では、これを行うことはできないと思いました。
bob = 1
bob = "bob"
強く型付けされた言語は、実行時の型変更を受け入れないと思いました。たぶん私は強い/弱いタイプの間違った(またはあまりにも単純な)定義を持っています。
では、Pythonは強い型の言語ですか、それとも弱い型の言語ですか?
Pythonは強力に動的に型付けされます。
あなたの例として
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)
returns 43.0
)。一方、タイプ間の不一致のため、Haskellは次のことを試してみると文句を言うでしょう(42 :: Integer) + (1 :: Float)
。これにより、Haskellは厳密に型指定された言語になります。この言語では、型は完全に互いに素であり、型クラスを介してオーバーロードの制御された形式のみが可能です。
既存の答えのすべてが見逃していると私が思ういくつかの重要な問題があります。
弱い型付けとは、基底表現へのアクセスを許可することを意味します。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は仮想メモリとページ保護を使用しているため、自分のプロセスのメモリのみを上書きできますが、Classic Mac OSやWin16などでコーディングしたことがある人なら誰でもわかるように、C自体にはそのような保護を提供するものはありません。
従来のLispは同様の種類のハッカーを許可していました。一部のプラットフォームでは、ダブルワードのfloatセルとconsセルは同じタイプであり、一方を他方を期待する関数に渡すだけで、「機能」します。
今日のほとんどの言語はCやLispほど弱くはありませんが、それらの多くはまだやや漏れがあります。たとえば、「ダウンキャスト」がチェックされていないオブジェクト指向言語*は、型リークです。基本的に、コンパイラに「これが安全であることを知るのに十分な情報を提供しなかったのはわかっていますが、かなり確信しています。型システムの要点は、コンパイラが何が安全かを知るのに十分な情報を常に持っているということです。
*チェックされたダウンキャストは、チェックを実行時に移動するという理由だけで、言語の型システムを弱めることはありません。もしそうなら、サブタイプのポリモーフィズム(別名仮想または完全に動的な関数呼び出し)は型システムの同じ違反になるでしょう、そして私は誰もそれを言いたくないと思います。
この意味で弱い「スクリプト」言語はほとんどありません。PerlやTclでも、文字列を取得してそのバイトを整数として解釈することはできません。*しかし、CPython(および多くの言語の他の多くのインタープリターでも同様)では、本当に永続的であれば、を使用ctypes
して、をロードしlibpython
、オブジェクトid
をにキャストしPOINTER(Py_Object)
、型システムを強制的にリークさせることができます。これによって型システムが弱くなるかどうかは、ユースケースによって異なります。セキュリティを確保するために言語で制限された実行サンドボックスを実装しようとしている場合は、この種のエスケープに対処する必要があります…</ p>
struct.unpack
*バイトを読み取り、「Cがこれらのバイトをどのように表すか」から新しいintを作成するような関数を使用できますが、それは明らかにリークではありません。Haskellでさえそれを可能にします。
一方、暗黙の変換は、弱い型またはリーク型のシステムとは実際には異なります。
Haskellを含め、すべての言語には、整数を文字列または浮動小数点数に変換する関数があります。ただし、一部の言語では、これらの変換の一部が自動的に実行されます。たとえば、Cでは、を必要とする関数を呼び出し、float
それを渡すと、int
変換されます。これは間違いなく、たとえば予期しないオーバーフローを伴うバグにつながる可能性がありますが、弱い型システムから得られるのと同じ種類のバグではありません。そして、Cはここでは実際に弱くなっているわけではありません。Haskellでintとfloatを追加したり、floatを文字列に連結したりすることもできます。もっと明示的に行う必要があります。
そして動的言語では、これはかなり曖昧です。PythonやPerlには「floatが欲しい関数」のようなものはありません。しかし、さまざまなタイプでさまざまなことを行うオーバーロードされた関数があり、たとえば、文字列を他の何かに追加することは「文字列を必要とする関数」であるという強い直感的な感覚があります。その意味で、Perl、Tcl、およびJavaScriptは多くの暗黙の変換を実行するように見えます("a" + 1
を提供します"a1"
)が、Pythonははるかに少ない変換を実行します("a" + 1
例外を発生さ1.0 + 1
せますが、2.0
*を提供します)。その意味を正式な用語で表現するのは難しい+
です。インデックス付けなどの他の関数が明らかにあるのに、文字列と整数をとるaがないのはなぜですか?
*実際、最近のPythonでは、これはOOサブタイピングの観点から説明できisinstance(2, numbers.Real)
ます。PerlやJavaScriptでは文字列型のインスタンスであるという意味はないと思います…Tclでは、すべて2
が文字列のインスタンスであるため、実際にはそうです。
最後に、「強い」型と「弱い」型の別の完全に直交する定義があります。ここで、「強い」は強力/柔軟/表現力を意味します。
たとえば、Haskellでは、数値、文字列、このタイプのリスト、または文字列からこのタイプへのマップであるタイプを定義できます。これは、JSONからデコードできるものを完全に表す方法です。Javaでそのような型を定義する方法はありません。しかし、少なくともJavaにはパラメトリック(ジェネリック)型があるので、Tのリストを受け取り、要素がT型であることを知ることができる関数を書くことができます。初期のJavaのような他の言語では、オブジェクトのリストとダウンキャストを使用する必要がありました。ただし、少なくともJavaでは、独自のメソッドを使用して新しい型を作成できます。Cでは構造を作成することしかできません。そして、BCPLにはそれさえありませんでした。以下同様に、アセンブリまで続きます。ここで、タイプはビット長が異なります。
したがって、その意味で、Haskellの型システムは、BCPLよりも強力なCよりも強力な以前のJavaよりも強力な最新のJavaよりも強力です。
では、Pythonはそのスペクトルのどこに当てはまるのでしょうか?それは少しトリッキーです。多くの場合、ダックタイピングを使用すると、Haskellで実行できるすべてのことをシミュレートできます。確かに、エラーはコンパイル時ではなく実行時にキャッチされますが、それでもキャッチされます。ただし、ダックタイピングでは不十分な場合があります。たとえば、Haskellでは、intの空のリストがintのリストであることがわかります。そのため、+
そのリストを減らすと0*が返されると判断できます。Pythonでは、空のリストは空のリストです。削減が何を+
すべきかを決定するのに役立つタイプ情報はありません。
*実際、Haskellではこれを許可していません。空のリストで開始値をとらないreduce関数を呼び出すと、エラーが発生します。しかし、その型システムは、これを機能させることができるほど強力ですが、Pythonはそうではありません。
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'
「強く型付けされた」と「動的に型付けされた」を混同しています。
1
文字列を追加してタイプを変更することはできませんが'12'
、変数に格納するタイプを選択して、プログラムの実行時に変更することはできます。
動的型付けの反対は静的型付けです。変数型の宣言は、プログラムの存続期間中は変更されません。強い型付けの反対は弱い型付けです。値のタイプは、プログラムの存続期間中に変更される可能性があります。
このウィキのPythonの記事によると、Pythonは動的かつ強く型付けされています(良い説明も提供します)。
おそらく、静的に型付けされた言語について考えています。この言語では、プログラムの実行中に型を変更できず、コンパイル時に型チェックが行われて、発生する可能性のあるエラーが検出されます。
このSOの質問は興味深いかもしれません:動的型言語と静的型言語、そして型システムに関するこのウィキペディアの記事はより多くの情報を提供します
「強い型付け」という用語には明確な定義はありません。
したがって、この用語の使用は、誰と話しているかによって異なります。
変数の型が明示的に宣言されていない、または静的に型付けされていない言語は、強く型付けされているとは考えていません。
強い型付けは、変換を妨げるだけではありません(たとえば、整数から文字列への「自動的な」変換)。割り当て(つまり、変数のタイプの変更)ができなくなります。
次のコードがコンパイル(解釈)される場合、言語は厳密に型指定されていません。
Foo = 1 Foo = "1"
強く型付けされた言語では、プログラマーは型を「頼りにする」ことができます。
たとえば、プログラマーが宣言を見た場合、
UINT64 kZarkCount;
そして彼または彼女は、20行後、kZarkCountが(同じブロックで発生する限り)まだUINT64であることを知っています-介在するコードを調べる必要はありません。
すでに数回回答されていますが、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
少し関連していますが、より高度です:
Python変数は、値を表すターゲットオブジェクトへの型指定されていない参照を格納します。
割り当て操作とは、型指定されていない参照を割り当てられたオブジェクトに割り当てることを意味します。つまり、オブジェクトは元の参照と新しい(カウントされた)参照を介して共有されます。
値の型は、参照値ではなく、ターゲットオブジェクトにバインドされます。(強い)型チェックは、値を使用した操作が実行されたときに実行されます(実行時)。
言い換えれば、変数には(技術的には)型がありません。正確にしたい場合は、変数の型の観点から考えるのは意味がありません。ただし、参照は自動的に逆参照され、実際にはターゲットオブジェクトのタイプの観点から考えます。
私はそれを暗記するための素晴らしい簡潔な方法を発見しました:
動的/静的型付き式。強く/弱く型付けされた値。
この簡単な例では、ストロングタイピングとダイナミックタイピングの違いを説明する必要があると思います。
>>> 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が値をある型から別の型に暗黙的に変換しないため、強い型の言語であることにほとんど同意しています。これらの回答は、この主張をサポートするために整数に文字列を追加する場合について言及しています。Pythonでは"foo" + 3
aを生成しますが、 Javascript(一般に弱い型の言語と見なされます)では、数値は暗黙的に文字列に変換されてから連結されるため、結果は文字列になります。TypeError
3
"foo3"
しかし、Pythonが暗黙の型変換を行う他の状況がいくつかあります。
# implicit conversion from int to float
1 + 1.0
# implicit conversion from list to bool
if []: pass
比較すると、F#(一般に強い型の言語と見なされます)では、次の両方が許可されていません。
1 + 1.0;;
----^^^
error FS0001: The type 'float' does not match the type 'int'
if [] then 1 else 2;;
---^^
error FS0001: This expression was expected to have type bool but here has type 'a list
したがって、実際には「強く型付けされた」言語と「弱い型付けされた」言語の厳密な二分法はありません。むしろ、PythonはJavascriptよりも強く型付けされていますが、F#ほど強く型付けされていないと言えます。
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....
上記のことは、大規模なシステムで長期間にわたって保守不可能なコードの悪夢を生み出すことになります。それをあなたが望むものと呼んでください、しかし変数タイプを「動的に」変更する能力はただ悪い考えです...