web-dev-qa-db-ja.com

Pythonの関数パラメータ型

間違えない限り、Pythonで関数を作成するのはこのように動作します。

def my_func(param1, param2):
    # stuff

ただし、実際にはこれらのパラメータの種類を指定することはありません。また、私が覚えているとすれば、Pythonは強く型付けされた言語なので、Pythonは関数作成者が期待したものとは異なる型のパラメータを渡さないようにすべきではないようです。しかし、Pythonは関数のユーザーが適切な型を渡していることをどのように認識しますか?関数が実際にパラメータを使用していると仮定した場合、プログラムはそれが間違った型であればただ死ぬのでしょうか。タイプを指定する必要がありますか?

226
Leif Andersen

すべてのオブジェクトは型を持ち、すべてのオブジェクトはその型を知っているので、Pythonは強く型付けされています。偶然または故意にオブジェクトを使うことは不可能です。 「あたかも」型のオブジェクトは異なる型のオブジェクトであり、そのオブジェクトに対するすべての基本操作はその型に委任されます。

これはnamesとは関係ありません。 Pythonの名前は「型を持たない」:名前が定義されている場合、その名前はオブジェクトを参照するそして、オブジェクトは型を持っています(しかし実際にはnameに型を強制するわけではありません:名前は名前)。

Pythonの名前は(ほとんどのプログラミング言語のように)異なる時間に異なるオブジェクトを完全に参照することができます - そして名前がX型のオブジェクトを参照したことがある場合、名前に制約はありません。 名前に対する制約は、「強い型付け」の概念の一部ではありませんが、静的型付けの一部の愛好家には向いていません。名前doは制約を受けます。静的な場合は、AKAのコンパイル時にも、このように用語を誤用します。

139
Alex Martelli

他の答えは、アヒルのタイプと tzot による簡単な答え)を説明するのに良い仕事をしました。

Pythonには、変数には型と値がある他の言語のように、変数がありません。それはそれらのタイプを知っているオブジェクトを指す名前を持っています。

しかし、2010年以降、興味深いことが1つ変更されました(つまり、 PEP 3107 (Python 3で実装))の実装です。次のように、パラメータの型と関数の戻り型の型

def pick(l: list, index: int) -> int:
    return l[index]

pickname__は2つのパラメーター、リストlname__および整数indexname__を取ることがわかります。整数も返します。

そのため、ここではlname__があまり手間をかけずに表示できる整数のリストであることを暗示していますが、より複雑な関数の場合は、リストに何を含めるべきかについて少し混乱する可能性があります。また、indexname__のデフォルト値を0にします。これを解決するには、代わりにpickname__を書くことを選択します。

def pick(l: "list of ints", index: int = 0) -> int:
    return l[index]

lname__の型として文字列を入力するようになりました。これは構文的には許可されていますが、プログラム的な構文解析には適していません(後で説明します)。

TypeErrorname__にfloatを渡しても、Pythonはindexname__を発生させないことに注意することが重要です。これは、Pythonの設計思想における主要なポイントの1つです。ここで "、つまり、関数に渡すことができるものとできないものに注意する必要があります。 TypeErrorsをスローするコードを本当に書きたい場合は、isinstancename__関数を使用して、渡された引数が適切な型であること、または次のようなサブクラスであることを確認できます。

def pick(l: list, index: int = 0) -> int:
    if not isinstance(l, list):
        raise TypeError
    return l[index]

なぜあなたがこれをするのがめったにないのか、そしてあなたが代わりにすべきことについての詳細は次のセクションとコメントで述べられています。

PEP 3107 はコードの可読性を向上させるだけでなく、あなたが here について読むことができるいくつかの適切な使用例を持っています。


Python 3.5では、型注釈に PEP 484 が導入され、型ヒント用の標準モジュールが導入されました。)と、より注目を集めるようになりました。

これらの型ヒントは型チェッカー mypyGitHub )から来ました。現在は PEP 484 準拠)です。

Typingモジュールには、次のような、かなり包括的なタイプのヒント集が付属しています。

  • Listname__、Tuplename__、Setname__、Map - それぞれlistname__、Tuplename__、setname__およびmapname__の場合。
  • Iterable - ジェネレータに便利です。
  • Any - それが何かになり得るとき。
  • Union - Anyname__とは対照的に、指定されたタイプのセット内のいずれかになる可能性がある場合.
  • Optional - の場合なしの場合Union[T, None]の省略形。
  • TypeVar - 総称で使用されます。
  • Callable - 主に関数に使用されますが、他の呼び出し可能オブジェクトにも使用できます。

これらは最も一般的なタイプのヒントです。完全なリストは 型付けモジュールのドキュメント にあります。

以下はtypingモジュールで導入されたアノテーションメソッドを使った古い例です。

from typing import List

def pick(l: List[int], index: int) -> int:
    return l[index]

1つの強力な機能はCallablename__です。これは、関数を引数として取るアノテーションメソッドを入力することを可能にします。例えば:

from typing import Callable, Any, Iterable

def imap(f: Callable[[Any], Any], l: Iterable[Any]) -> List[Any]:
    """An immediate version of map, don't pass it any infinite iterables!"""
    return list(map(f, l))

上記の例はTypeVarname__の代わりにAnyname__を使用するとより正確になる可能性がありますが、型ヒントによって可能にされた素晴らしい新機能についての情報はすでにたくさんありますので、これは読者への課題として残しました。 。


以前は、たとえば Sphinx の一部のPythonコードをドキュメント化したときに、次のようにフォーマットされたdocstringを記述することで取得できました。

def pick(l, index):
    """
    :param l: list of integers
    :type l: list
    :param index: index at which to pick an integer from *l*
    :type index: int
    :returns: integer at *index* in *l*
    :rtype: int
    """
    return l[index]

ご覧のとおり、これには余分な行がいくつか必要になります(正確な数は、明示的に表現する方法とdocstringをフォーマットする方法によって異なります)。しかし、 PEP 3107 は多くの点で優れている代替手段を提供します。これは PEP 484 と組み合わせると特に当てはまります)明確で正確でありながら柔軟で強力な組み合わせを可能にするような方法で使用できる、これらの型ヒント/注釈の構文を定義する標準モジュール。

私の個人的な意見では、これはPythonのこれまでで最大の機能の1つです。私は人々がそれの力を利用し始めるのを待つことができません。長い答えで申し訳ありませんが、これは私が興奮したときに起こることです。


型ヒンティングを頻繁に使用するPythonコードの例は、 here )にあります。

550
erb

あなたはタイプを指定しません。このメソッドは、渡されたパラメーターで定義されていない属性にアクセスしようとした場合にのみ(実行時に)失敗します。

だから、この単純な機能:

def no_op(param1, param2):
    pass

2つの引数が渡されても失敗しません。

しかし、この機能:

def call_quack(param1, param2):
    param1.quack()
    param2.quack()

param1param2の両方にquackという名前の呼び出し可能な属性がない場合、...は実行時に失敗します。

13
TM.

多くの言語は変数を持っていて、それは特定の型でありそして値を持っています。 Pythonには変数がありません。それはオブジェクトを持っています、そしてあなたはこれらのオブジェクトを参照するために名前を使います。

他の言語では、あなたが言うとき:

a = 1

次に(通常は整数の)変数はその内容を値1に変更します。

Pythonでは、

a = 1

「オブジェクトを参照するために名前aを使用する1」を意味します。対話型のPythonセッションで次のことができます。

>>> type(1)
<type 'int'>

関数typeは、オブジェクト1とともに呼び出されます。すべてのオブジェクトはその型を知っているので、typeがその型を見つけてそれを返すのは簡単です。

同様に、関数を定義するときはいつでも

def funcname(param1, param2):

この関数は2つのオブジェクトを受け取り、それらの型に関係なく、それらをparam1およびparam2と名付けます。受け取ったオブジェクトが特定のタイプであることを確認したい場合は、必要なタイプであるかのように関数をコーディングし、そうでない場合はスローされる例外をキャッチします。スローされる例外は、通常TypeError(無効な操作を使用した場合)とAttributeError(存在しないメンバーにアクセスしようとした場合(メソッドもメンバーです))です。

8
tzot

Pythonは、静的またはコンパイル時の型チェックという意味で強く型付けされていません。

ほとんどのPythonコードはいわゆる "Duck Typing" に分類されます - 例えば、オブジェクト上でメソッドreadを探す - オブジェクトがディスク上のファイルであるかどうかは気にしません。ソケット、あなたはそれからNバイトを読みたいだけです。

6
Mark Rushakoff

として Alex Martelli氏が説明

通常のPythonicの推奨される解決策は、ほとんど常に「ダックタイピング」です。引数を特定の望ましい型であるかのように使用してみてください。そして、except節の中で、何か他のものを試してみてください(引数 "まるで"他のタイプのものであるかのように)。

役立つ情報については彼の投稿の残りを読んでください。

5
Nick Presta

Pythonは関数に何を渡しても構いません。 my_func(a,b)を呼び出すと、param1とparam2変数はaとbの値を保持します。 Pythonは、あなたが関数を適切な型で呼び出していることを知りませんし、プログラマがそれを引き受けることを期待します。関数が異なる種類のパラメータで呼び出される場合は、try/exceptブロックを使用してそれらにアクセスするコードをラップし、必要な方法でパラメータを評価できます。

3
Kyle

このページで言及する価値があるアヒルのタイプからの1つの悪名高い例外があります。

str関数が__str__クラスメソッドを呼び出すと、その型が微妙にチェックされます。

>>> class A(object):
...     def __str__(self):
...         return 'a','b'
...
>>> a = A()
>>> print a.__str__()
('a', 'b')
>>> print str(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __str__ returned non-string (type Tuple)

プログラムが予期しない型に遭遇した場合にGuidoがどの例外を発生させるべきかをGuidoが示唆しているかのようです。

2

型を指定することはありません。 Pythonには、 アヒルの型付け という概念があります。基本的に、パラメータを処理するコードは、それらについて特定の仮定をします - おそらく、パラメータが実装すると予想される特定のメソッドを呼び出すことによって。パラメータの型が間違っていると、例外がスローされます。

一般に、適切な型のオブジェクトを確実に渡すようにするのはコード次第です。これを前もって強制するコンパイラはありません。

2
Justin Ethier

Pythonでは、すべてに型があります。 Python関数は、引数の型がそれをサポートしていれば、要求されたことをすべて行います。

例:fooは、その型を気にすることなく、__add__ed;)になる可能性があるものすべてを追加します。つまり、失敗を避けるためには、追加をサポートするものだけを提供する必要があります。

def foo(a,b):
    return a + b

class Bar(object):
    pass

class Zoo(object):
    def __add__(self, other):
        return 'zoom'

if __name__=='__main__':
    print foo(1, 2)
    print foo('james', 'bond')
    print foo(Zoo(), Zoo())
    print foo(Bar(), Bar()) # Should fail
1
Pratik Deoghare

私はこれが他の答えで言及されているのを見なかったので、私はこれをポットに追加します。

他の人が言っているように、Pythonは関数やメソッドのパラメータに型を強制しません。自分がしていることを知っていること、そして渡された何かの種類を本当に知る必要がある場合は、それを確認して自分で何をするかを決めることになります。

これを行うための主なツールの1つがisinstance()関数です。

たとえば、通常のUTF-8でエンコードされた文字列ではなく、生のバイナリテキストデータを取得するメソッドを作成する場合は、途中でパラメータの型を確認し、見つかったものに適応するか、拒否する例外.

def process(data):
    if not isinstance(data, bytes) and not isinstance(data, bytearray):
        raise TypeError('Invalid type: data must be a byte string or bytearray, not %r' % type(data))
    # Do more stuff

Pythonはオブジェクトを掘り下げるためのあらゆる種類のツールも提供します。勇気があれば、importlibを使って任意のクラスのオブジェクトをその場で作成することもできます。 JSONデータからオブジェクトを再作成するためにこれを行いました。そのようなことは、C++のような静的言語における悪夢になるでしょう。

1
Dread Quixadhal

タイピングモジュール(Python 3.5の新機能)を効果的に使うためにall(*)を含めてください。

from typing import *

そして、あなたは使用する準備が整います:

List, Tuple, Set, Map - for list, Tuple, set and map respectively.
Iterable - useful for generators.
Any - when it could be anything.
Union - when it could be anything within a specified set of types, as opposed to Any.
Optional - when it might be None. Shorthand for Union[T, None].
TypeVar - used with generics.
Callable - used primarily for functions, but could be used for other callables.

ただし、intlistdict、...のような型名を使用することはできます。

0
prosti