-> Python関数定義で何を意味しますか?


476

最近、Python 3.3の文法仕様を見て、興味深いことに気づきました。

funcdef: 'def' NAME parameters ['->' test] ':' suite

オプションの「矢印」ブロックはPython 2には存在せず、Python 3でのその意味に関する情報を見つけることができませんでした。これは正しいPythonであり、インタープリターによって受け入れられます。

def f(x) -> 123:
    return x

これはある種の前提条件構文かもしれないと思ったが、

  • xここではテストできません。まだ定義されていません。
  • 矢印の後に何を付けても(例:)2 < 1、それは関数の動作に影響を与えません。

この構文に慣れている人はそれを説明できますか?

回答:


375

これは関数アノテーションです。

より詳細には、Python 2.xにはdocstringがあり、メタデータ文字列をさまざまなタイプのオブジェクトにアタッチできます。これは驚くほど便利なので、Python 3は、パラメーターと戻り値を説明する関数にメタデータを添付できるようにすることで、機能を拡張します。

先入観のあるユースケースはありませんが、PEPはいくつか提案しています。非常に便利な方法の1つは、期待される型でパラメーターに注釈を付けることです。そうすれば、注釈を検証したり、引数を正しい型に強制したりするデコレータを簡単に作成できます。もう1つは、docstringにエンコードする代わりに、パラメーター固有のドキュメントを許可することです。


122
そして、情報は.__annotations__属性として利用可能です。
Martijn Pieters

8
うわー、私はかなり広い範囲の知識を逃しました-戻り値の注釈だけでなく、パラメーターの注釈も。どうもありがとうございました :)。
クロトン2013年

4
@Krottonそれを逃したことであなたを責めることはできません、それは実質的に未使用です。私はそれらを使用して1つのライブラリに会っただけで、それは非常にあいまいです。

5
そして__annotations__属性は辞書です。キーreturnは、矢印の後の値を取得するために使用されるキーです。
キース

9
@delnan-ほとんど使用されていない理由は、ほとんどのpythonライブラリが依然としてpython2.xとの互換性を目指しているためです。python3.xがより標準的になり始めているため、あちこちでこれらのものがさらに表示される可能性があります...
mgilson

252

これらはPEP 3107でカバーされている関数アノテーションです。具体的には、->はreturn関数アノテーションをマークします。

例:

>>> def kinetic_energy(m:'in KG', v:'in M/S')->'Joules': 
...    return 1/2*m*v**2
... 
>>> kinetic_energy.__annotations__
{'return': 'Joules', 'v': 'in M/S', 'm': 'in KG'}

注釈は辞書なので、これを行うことができます:

>>> '{:,} {}'.format(kinetic_energy(20,3000),
      kinetic_energy.__annotations__['return'])
'90,000,000.0 Joules'

文字列だけでなくpythonデータ構造を持つこともできます。

>>> rd={'type':float,'units':'Joules','docstring':'Given mass and velocity returns kinetic energy in Joules'}
>>> def f()->rd:
...    pass
>>> f.__annotations__['return']['type']
<class 'float'>
>>> f.__annotations__['return']['units']
'Joules'
>>> f.__annotations__['return']['docstring']
'Given mass and velocity returns kinetic energy in Joules'

または、関数属性を使用して呼び出された値を検証できます。

def validate(func, locals):
    for var, test in func.__annotations__.items():
        value = locals[var]
        try: 
            pr=test.__name__+': '+test.__docstring__
        except AttributeError:
            pr=test.__name__   
        msg = '{}=={}; Test: {}'.format(var, value, pr)
        assert test(value), msg

def between(lo, hi):
    def _between(x):
            return lo <= x <= hi
    _between.__docstring__='must be between {} and {}'.format(lo,hi)       
    return _between

def f(x: between(3,10), y:lambda _y: isinstance(_y,int)):
    validate(f, locals())
    print(x,y)

プリント

>>> f(2,2) 
AssertionError: x==2; Test: _between: must be between 3 and 10
>>> f(3,2.1)
AssertionError: y==2.1; Test: <lambda>

86

他の答えが述べたように、->記号は関数注釈の一部として使用されます。しかし、より最近のバージョンのPython >= 3.5では、定義された意味があります。

PEP 3107-関数の注釈は、仕様を記述し、文法の変更、func.__annotations__それらが格納される存在、およびそのユースケースがまだオープンであるという事実を定義しています。

3.5ただし、Pythonでは、PEP 484-Type Hints->はこれに単一の意味を付加します。関数が返すタイプを示すために使用されます。また、アノテーションの既存の使用についてはどうですか?で説明されているように、これは将来のバージョンで適用されるようです。

考えられる最速のスキームは、3.6では非タイプヒントアノテーションのサイレント非推奨を導入し、3.7では完全に非推奨にし、タイプヒントをPython 3.8で使用できる唯一のアノテーションとして宣言します。

(エンファシス鉱山)

これは3.6私が知る限り実際には実装されていないため、将来のバージョンにぶつかる可能性があります。

これによると、あなたが提供した例:

def f(x) -> 123:
    return x

将来的には禁止されます(現在のバージョンでは混乱を招きます)。次のように変更する必要があります。

def f(x) -> int:
    return x

その関数fがタイプのオブジェクトを返すことを効果的に説明するためintです。

アノテーションはPython自体では決して使用されません。アノテーションはほとんど入力され、無視されます。それらと連携するのはサードパーティのライブラリまでです。


64

次のコードでは:

def f(x) -> int:
    return int(x)

-> intちょうどそれが伝えf()整数を返します(が、それは整数を返す関数を強制するものではありません)。これはreturn注釈と呼ばれ、としてアクセスできますf.__annotations__['return']

Pythonはパラメータアノテーションもサポートしています。

def f(x: float) -> int:
    return int(x)

: floatプログラムを読ん人(および一部のサードパーティのライブラリ/プログラム、例えばpylint)伝えxなければなりませんがfloat。これはとしてアクセスされf.__annotations__['x']、それ自体では意味がありません。詳細については、ドキュメントを参照してください。

https://docs.python.org/3/reference/compound_stmts.html#function-definitions https://www.python.org/dev/peps/pep-3107/


4

これは、関数が返す結果のタイプを意味しますが、でもかまいませんNone

これは、Python 3.xを指向した最新のライブラリで広く使用されています。

たとえば、ライブラリpandas-profilingのコードには多くの場所にあります。次に例を示します。

def get_description(self) -> dict:

def get_rejected_variables(self, threshold: float = 0.9) -> list:

def to_file(self, output_file: Path or str, silent: bool = True) -> None:
"""Write the report to a file.

「これは、関数が返す結果のタイプを意味しますが、なしの場合もあります。」Noneまたはその他のタイプを使用できます。
Ebram Shehata

2

def function(arg)->123:

これは単に戻り値の型であり、この場合の整数は、どの数値を記述してもかまいません。

Javaのように:

public int function(int args){...}

しかし、Python(Jim Fasarakis Hilliardの発言)では、戻り値の型は単なるヒントにすぎないため、戻り値が推奨され ますが、とにかく文字列のような他の型を返すことができます。


1
def f(x) -> 123:
    return x

私の要約:

  1. ->開発者がオプションで関数の戻り値の型を指定できるようにするためだけに導入されています。Python Enhancement Proposal 3107を参照してください

  2. これは、Pythonが広く採用されているため、将来どのように発展するかを示すものであり、強力なタイピングへの示唆です-これは私の個人的な観察です。

  3. 引数の型も指定できます。関数と引数の戻り値の型を指定すると、論理エラーを減らし、コードの機能強化を改善できます。

  4. 式を戻り値の型として(関数レベルとパラメーターレベルの両方で)持つことができ、式の結果には注釈オブジェクトの 'return'属性を介してアクセスできます。ラムダインライン関数の式/戻り値の注釈は空になります。


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