Pythonが間違った数の引数で関数呼び出しを許可するのはなぜですか?


80

Pythonは私の最初の動的言語です。最近、間違った数の引数を誤って指定する関数呼び出しをコーディングしました。これは、関数が呼び出されたときの例外で失敗しました。動的言語でも、ソースファイルを解析するときにこの種のエラーを検出できると思っていました。

同じ変数に異なる時間に任意の型の値が含まれる可能性があるため、実際の引数のは関数が呼び出されるまでわからないことを理解しています。ただし、引数のは、ソースファイルが解析されるとすぐにわかります。プログラムの実行中は変更されません。

これが哲学的な質問ではないように

これをStackOverflowの範囲内に保つために、このような質問を言いましょう。Pythonが提供する、コードが実際に実行されるまで関数呼び出しの引数の数のチェックを遅らせる必要がある機能はありますか?


17
名前が実行時に完全に別の関数を参照している場合はどうなりますか?
jonrsharpe 2016年

まあ、反論として、呼び出すための引数の数は、f(*args)解析フェーズではわかりません。
ルカシュRogalski

3
型理論では、関数の場合、値の型には引数の数が含まれることに注意してください。
PyRulez 2016年

9
Pythonは、実際に呼び出そうとするまで、呼び出す関数をどのようにして知るのでしょうか。Pythonの関数に名前があるのとは異なります。(これは皮肉ではありません)
user253751 2016年

1
@immibis:ええ、違います。彼らには名前があります。彼らは特にしていない添付ものの、それらの名前に。def foo(): whateverhasとして定義されている関数ですがfoo.__name__ == 'foo'、それでもfoo = some_other_functionまたはを実行できbar = fooます。
user2357112は、Monica

回答:


146

Pythonは、動的であるため、関数オブジェクトを交換できるため、最終的にどのオブジェクトを呼び出すかを事前に知ることはできません。いつでも。そして、これらのオブジェクトのそれぞれは、異なる数の引数を持つことができます。

これが極端な例です:

import random

def foo(): pass
def bar(arg1): pass
def baz(arg1, arg2): pass

the_function = random.choice([foo, bar, baz])
print(the_function())

上記のコードでは、例外が発生する可能性が3分の2です。しかし、Pythonは、それが当てはまるかどうかを事前に知ることはできません。

そして、動的モジュールのインポート、動的関数の生成、その他の呼び出し可能なオブジェクト(__call__メソッドを持つ任意のオブジェクトを呼び出すことができます)、またはキャッチオール引数(*argsおよび**kwargs)から始めたことさえありません。

しかし、これをさらに明確にするために、あなたはあなたの質問で次のように述べています:

プログラムの実行中は変更されません。

これは当てはまりません。Pythonではそうではありません。モジュールが読み込まれると、関数オブジェクトを含む、モジュール名前空間内の任意のオブジェクトを削除、追加、または置換できます。


以前に関数テーブルを見たことがあるので、それほど悪いことではないかもしれません。ちょっと待ってください、テーブルの関数は*引数を使用していません....そしてこれはPythonなので....うん、かなり極端です。
レイトール

3
今はTシャツです。(帰属を追加する方法がわからなかった場合。必要に応じて教えてください。おそらく理解できます。)
PyRulez 2016年

@PyRulez:正規表現解析の投稿のユニコーン型のコピーが入った私のTシャツはURLを使用しています。追加https://stackoverflow.com/a/34567789して、それで済ませることができます。
MartijnPieters

4
@PyRulez:しかし、これは実際には単なるディスパッチリストです。ポイントは、あなたがオブジェクトとしての機能を使用できることを説明することであり、あなたは1つが呼ばれ、フロントまで知ることができません。それ以外の場合は、かなり一般的な手法です。
MartijnPieters

おそらく、ユーザーが元の問題を解決できるようにするためです。コードを実行する前に間違いを検出するために、静的検証ツールの使用について言及することができます。
Xavier Combelle 2016年

35

渡される引数の数はわかっていますが、実際に呼び出される関数はわかっていません。この例を参照してください。

def foo():
    print("I take no arguments.")

def bar():
    print("I call foo")
    foo()

これは当たり前のように思えるかもしれませんが、これらを「fubar.py」というファイルに入れましょう。さて、インタラクティブなPythonセッションで、これを行います。

>>> import fubar
>>> fubar.foo()
I take no arguments.
>>> fubar.bar()
I call foo
I take no arguments.

それは明らかでした。さて、楽しい部分です。ゼロ以外の量の引数を必要とする関数を定義します。

>>> def notfoo(a):
...    print("I take arguments!")
...

今、私たちはモンキーパッチと呼ばれる何かをします。実際、モジュール内の関数を置き換えることができます。foofubar

>>> fubar.foo = notfoo

さて、を呼び出すとbar、aTypeErrorが発生します。この名前fooは、以前は-として知られていた元の関数ではなく、上記で定義した関数を参照するようになりましたfoo

>>> fubar.bar()
I call foo
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/horazont/tmp/fubar.py", line 6, in bar
    foo()
TypeError: notfoo() missing 1 required positional argument: 'a'

したがって、呼び出された関数fooが引数をとらないことが非常に明白に見えるこのような状況でも、Pythonfooは、そのソース行を実行するときに呼び出されているのが実際に関数であることを知ることしかできません。

これはPythonのプロパティであり、強力ですが、速度が低下する原因にもなります。実際、パフォーマンスを向上させるためにモジュールを読み取り専用にすることは、しばらく前にpython-ideasメーリングリストで議論されましたが、実際のサポートは得られませんでした。

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