回答:
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)
リターンを43.0
)。一方、型の不一致により、Haskellは次のことを試みると文句を言うでしょう(42 :: Integer) + (1 :: Float)
。これにより、Haskellは厳密に型指定された言語になり、型は完全にばらばらになり、制御された形式のオーバーロードのみが型クラスを介して可能になります。
True
かFalse
。しかし、番号の昇格はどうですか?1.0 + 2
PythonでもPerlやCと同じように"1.0" + 2
機能しますが、機能しません。@jbrendelに同意します。これは実際には暗黙的な変換ではなく、単なるオーバーロードですが、同じ意味で、Perlは暗黙的な変換も行っていません。関数にパラメーター型が宣言されていない場合、暗黙的な変換が行われる場所はありません。
if isValid(value) - 1
なリークが発生する可能性があります。ブール値は整数に強制変換され、それが真の値として評価されます。False - 1
真実にTrue - 1
なり、偽りになり、デバッグするのが恥ずかしいほど困難な2層オフバイワンエラーにつながります。この意味で、Pythonはほとんど強く型付けされています。型強制は通常、論理エラーを引き起こしません。
既存の答えのすべてが見逃していると私は思ういくつかの重要な問題があります。
弱い型付けとは、基になる表現へのアクセスを許可することを意味します。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サブタイピングの観点から説明できます。2
Perlや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の機能は機能しません。
char sz[]
charへのポインタではなく、charの配列であり、割り当てではポインタに減衰します。
「強く型付けされた」と「動的に型付けされた」を混同しています。
1
文字列を追加してのタイプを変更することはできません'12'
が、変数に格納するタイプを選択して、プログラムの実行中に変更することができます。
動的型付けの反対は静的型付けです。変数の型の宣言は、プログラムの存続期間中に変更されません。強い型付けの反対は弱い型付けです。値のタイプは、プログラムの存続期間中に変化する可能性があります。
このwikiのPython記事によると、Pythonは動的かつ強く型付けされています(良い説明も提供されています)。
おそらく、プログラムの実行中に型を変更できない静的型付き言語について考えている可能性があり、コンパイル時に型チェックが起こり、エラーの可能性を検出します。
このSOの質問は興味深いかもしれません:動的型言語と静的型言語の比較および型システムに関するこのWikipediaの記事で詳細を説明しています
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'
'x' + 3
可能性がありますoperator+
か?
すでに何度か回答されていますが、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
少し関連していますが、より高度です:
"3"*4
あまりにも、pythonで。もちろん、結果は"3333"
です。あなたはそれがどちらかを変換しているとは言わないでしょう。もちろん、それはセマンティクスを論じているだけかもしれません。
float
組み合わせから生成されるため、暗黙的に型を変換しているfloat
とは限りませんint
。floatとintの間には自然な関係があり、実際、タイプ階層はそれを示しています。私はあなたがJavascriptが考えて主張することができると仮定'3'+4
し、'e'+4
Pythonは考えていることと同じように、明確に定義された操作のことの両方に3.0 + 4
定義され、明確に定義されたように、しかし、その時点で、その後、強いまたは弱いタイプのようなものが本当にありません、ただ(UN)操作。
Python変数は、値を表すターゲットオブジェクトへの型なし参照を格納します。
割り当て操作とは、型指定されていない参照を割り当てられたオブジェクトに割り当てることです。つまり、オブジェクトは、元の参照と新しい(カウントされた)参照を介して共有されます。
値タイプは、参照値ではなくターゲットオブジェクトにバインドされます。(強力な)型チェックは、値を持つ操作が実行されたときに実行されます(実行時)。
言い換えれば、(技術的に)変数には型がありません-正確にしたい場合、変数の型で考えることは意味がありません。しかし、参照は自動的に逆参照され、実際にはターゲットオブジェクトのタイプの観点から考えます。
「強い型付け」という用語には明確な定義はありません。
したがって、この用語の使用は、話している相手によって異なります。
変数の型が明示的に宣言されていない、または静的に型付けされていない言語は、強く型付けされているとは考えていません。
強い型付けは、変換を排除するだけではありません(たとえば、整数から文字列への「自動」変換)。代入(つまり、変数の型の変更)ができなくなります。
次のコードがコンパイル(解釈)する場合、言語は厳密に型指定されていません。
Foo = 1 Foo = "1"
強く型付けされた言語では、プログラマは型を「当てに」することができます。
たとえば、プログラマーが宣言を見た場合、
UINT64 kZarkCount;
そして彼または彼女は、20行後、kZarkCountが(同じブロック内にある限り)UINT64のままであることを知っています-介在するコードを調べる必要はありません。
この単純な例では、強い型付けと動的な型付けの違いについて説明します。
>>> 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
}
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....
上記は、長期間にわたって大規模なシステムで保守不可能なコードの悪夢を生み出します。それをあなたが望むものと呼びますが、変数の型を「動的に」変更する機能は単に悪い考えです...