Pythonでアットマーク(@)は何をしますか?


580

@記号を使用したPythonコードをいくつか見ていますが、それが何をするのかわかりません。@シンボルが含まれていると、Pythonドキュメントを検索したり、Googleが関連する結果を返さなかったりするので、何を検索するかもわかりません。

回答:


305

@行頭のシンボルは、クラス、関数、メソッドのデコレータに使用されます。

詳細はこちら:

PEP 318:デコレーター

Pythonデコレータ

実行する最も一般的なPythonデコレータは次のとおりです。

@property

@classmethod

@staticmethod

@線の真ん中にが表示されている場合、それは別のこと、行列の乗算です。下にスクロールして、の使用に対処する他の回答を確認します@


31
それはまた、行列乗算演算子でもあるように見えます:stackoverflow.com/a/21563036/5049813
Pro Q

@デコレーターも追加できます
Vijay Panchal

349

class Pizza(object):
    def __init__(self):
        self.toppings = []

    def __call__(self, topping):
        # When using '@instance_of_pizza' before a function definition
        # the function gets passed onto 'topping'.
        self.toppings.append(topping())

    def __repr__(self):
        return str(self.toppings)

pizza = Pizza()

@pizza
def cheese():
    return 'cheese'
@pizza
def sauce():
    return 'sauce'

print pizza
# ['cheese', 'sauce']

これは、デコレータの後に定義しているfunction/ method/ classが、基本的には/の直後に/ として渡されることを示しています。argumentfunctionmethod@

最初の目撃

マイクロフレームワークFlaskは、最初から次の形式でデコレーターを紹介します。

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

これは次のように変換されます。

rule      = "/"
view_func = hello
# They go as arguments here in 'flask/app.py'
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
    pass

これを実現したことで、ようやく私はフラスコとの平和を感じることができました。


7
Flasksの場合app.route("/"):この関数は関数を返します。この関数はhello()、引数として呼び出します
Shaqed

3
ここでデコレータを持つの構文や実用的なメリットは、何の代わりに、ちょうどのようなものを呼び出す(例えば)app.route("/", hello)を定義した直後にhello、あるいは定義helloに引数にラムダとしてapp.route?(後者の例では、Node.jsのと共通であり、http.Serverそしてエクスプレスルート)
電離層

185

このコードスニペット:

def decorator(func):
   return func

@decorator
def some_func():
    pass

このコードと同等です:

def decorator(func):
    return func

def some_func():
    pass

some_func = decorator(some_func)

デコレータの定義では、通常は関数から返されない変更されたものを追加できます。


1
この行s "ome_func = decorator(some_func)"では、最初のsome_funcは変数です=関数some_funcへの、正しいですか?
Viragos

147

Python 3.5 @では、演算子としてオーバーロードできます。__matmul__行列の乗算を行うように設計されているため、と名付けられていますが、何でもかまいません。詳細については、PEP465を参照してください。

これは、行列乗算の単純な実装です。

class Mat(list):
    def __matmul__(self, B):
        A = self
        return Mat([[sum(A[i][k]*B[k][j] for k in range(len(B)))
                    for j in range(len(B[0])) ] for i in range(len(A))])

A = Mat([[1,3],[7,5]])
B = Mat([[6,8],[4,2]])

print(A @ B)

このコードは、

[[18, 14], [62, 66]]

14
あなたも持って@=いる(インプレース)演算子を__imatmul__
–PålGD 2018

このような他のオーバーライド可能な演算子はありますか?私はそれぞれ+と- を知って__add__おり、__sub__それぞれに関連付けられていますが、その@兆候を聞いたことはありません。他に潜んでいる人はいますか?
Thomas Kimber

103

Pythonでアットマーク(@)は何をしますか?

つまり、デコレータの構文や行列の乗算に使用されます。

デコレータのコンテキストでは、この構文は次のとおりです。

@decorator
def decorated_function():
    """this function is decorated"""

これと同等です:

def decorated_function():
    """this function is decorated"""

decorated_function = decorator(decorated_function)

行列乗算のコンテキストでは、次の構文を実行してa @ b呼び出しa.__matmul__(b)ます。

a @ b

に相当

dot(a, b)

そして

a @= b

に相当

a = dot(a, b)

ここdotで、は、たとえば、numpy行列の乗算関数で、abは行列です。

自分でこれをどのように発見できますか?

また、@記号が含まれていると、Pythonドキュメントを検索したり、Googleが関連する結果を返さなかったりするため、何を検索すべきかわかりません。

Python構文の特定の部分が何をするのかをかなり完全に知りたい場合は、文法ファイルを直接見てください。Python 3ブランチの場合:

~$ grep -C 1 "@" cpython/Grammar/Grammar 

decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
decorators: decorator+
--
testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [',']
augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' |
            '<<=' | '>>=' | '**=' | '//=')
--
arith_expr: term (('+'|'-') term)*
term: factor (('*'|'@'|'/'|'%'|'//') factor)*
factor: ('+'|'-'|'~') factor | power

ここで@は、3つのコンテキストで使用されていることがわかります。

  • デコレータ
  • 因子間の演算子
  • 拡張された代入演算子

デコレーター構文:

「decorator python docs」をGoogle検索すると、「Python言語リファレンス」の「複合ステートメント」セクションが上位の結果の1つとして表示されます。「デコレータ」という単語を検索して見つけることができる関数定義セクションまで下にスクロールすると、次のことがわかります...読むべきことがたくさんあります。ただし、「デコレータ」という用語は用語集へのリンクであり、次のように説明しています。

デコレータ

通常は@wrapper構文を使用して関数変換として適用される、別の関数を返す関数。デコレータのための一般的な例ですclassmethod()staticmethod()

デコレータの構文は単なる構文上の砂糖であり、次の2つの関数定義は意味的に同等です。

def f(...):
    ...
f = staticmethod(f)

@staticmethod
def f(...):
    ...

クラスにも同じ概念が存在しますが、そこではあまり使用されません。デコレータの詳細については、関数定義とクラス定義のドキュメントを参照してください。

だから、私たちはそれを見る

@foo
def bar():
    pass

意味的には次と同じです:

def bar():
    pass

bar = foo(bar)

Pythonは、decorator(@)構文を使用してbarの前にfoo式(ドットルックアップと関数呼び出しの可能性があります)を評価しますが、それ以外の場合、barの後に foo式を評価するため、まったく同じではありません。

(この違いがコードの意味に違いをもたらす場合、それは病理学的であるため、あなたは自分の人生で何をしているのかを再考する必要があります。)

スタックされたデコレータ

関数定義構文のドキュメントに戻ると、次のことがわかります。

@f1(arg)
@f2
def func(): pass

とほぼ同等

def func(): pass
func = f1(arg)(f2(func))

これは、デコレータである関数とスタックデコレータを最初に呼び出すことができるデモです。Pythonでは、関数はファーストクラスオブジェクトです。つまり、関数を引数として別の関数に渡し、関数を返すことができます。デコレータはこれらの両方を行います。

デコレータをスタックする場合、定義されているとおり、関数は最初にそのすぐ上のデコレータに渡され、次に次のように渡されます。

これ@は、デコレータのコンテキストでの使用法をまとめたものです。

オペレーター、 @

言語リファレンスの字句解析セクションには、を含む演算子に関するセクションがあります@

次のトークンは演算子です。

+       -       *       **      /       //      %      @
<<      >>      &       |       ^       ~
<       >       <=      >=      ==      !=

次のページのデータモデルには、「数値型のエミュレート」セクションがあります。

object.__add__(self, other)
object.__sub__(self, other) 
object.__mul__(self, other) 
object.__matmul__(self, other) 
object.__truediv__(self, other) 
object.__floordiv__(self, other)

[...]これらの方法は、(バイナリ算術演算を実現するために呼び出されます+-*@///、[...]

そして、これはに__matmul__対応して@いることがわかります。「matmul」のドキュメンテーションを検索する、見出し「PEP 465-行列の乗算のための専用のインフィックス演算子」の下に「matmul」を含むPython 3.5の新機能へのリンクが表示されます。

それは定義することによって実現することができ__matmul__()__rmatmul__()および __imatmul__()正規のために、反射され、インプレース行列乗算。

(つまり@=、これがインプレースバージョンであることがわかります)。それはさらに説明します:

行列の乗算は、数学、科学、工学の多くの分野で特に一般的な演算であり、@を追加すると、よりクリーンなコードを記述できます。

S = (H @ beta - r).T @ inv(H @ V @ H.T) @ (H @ beta - r)

の代わりに:

S = dot((dot(H, beta) - r).T,
        dot(inv(dot(dot(H, V), H.T)), dot(H, beta) - r))

この演算子はオーバーロードしてほとんど何でも実行できますがnumpy、たとえばでは、次の構文を使用して配列と行列の内積と外積を計算します。

>>> from numpy import array, matrix
>>> array([[1,2,3]]).T @ array([[1,2,3]])
array([[1, 2, 3],
       [2, 4, 6],
       [3, 6, 9]])
>>> array([[1,2,3]]) @ array([[1,2,3]]).T
array([[14]])
>>> matrix([1,2,3]).T @ matrix([1,2,3])
matrix([[1, 2, 3],
        [2, 4, 6],
        [3, 6, 9]])
>>> matrix([1,2,3]) @ matrix([1,2,3]).T
matrix([[14]])

インプレース行列乗算: @=

以前の使用法を調査しているときに、インプレース行列乗算もあることがわかります。使用しようとすると、numpyにはまだ実装されていない場合があります。

>>> m = matrix([1,2,3])
>>> m @= m.T
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: In-place matrix multiplication is not (yet) supported. Use 'a = a @ b' instead of 'a @= b'.

実装すると、結果は次のようになります。

>>> m = matrix([1,2,3])
>>> m @= m.T
>>> m
matrix([[14]])

36

Pythonでアットマーク(@)は何をしますか?

@記号は、シンタックスシュガーパイソンを利用する提供しているdecorator
それはまさに、Pythonでデコレータん何をするかについて、質問を言い換えするのか?

簡単decoratorに言うと、最も内側(クロージャ)に触れることなく、特定の関数の定義を変更できます。
サードパーティから素晴らしいパッケージをインポートする場合がこれに該当します。あなたはそれを視覚化し、それを使用することができますが、その最も内側とその心に触れることはできません。

ここに簡単な例があり
ますread_a_book。Ipythonで関数を定義するとします。

In [9]: def read_a_book():
   ...:     return "I am reading the book: "
   ...: 
In [10]: read_a_book()
Out[10]: 'I am reading the book: '

名前を追加するのを忘れました。
このような問題を解決するにはどうすればよいですか?もちろん、関数を次のように再定義できます。

def read_a_book():
    return "I am reading the book: 'Python Cookbook'"

それでも、元の関数を操作することが許可されていない場合、またはそのような関数が何千も処理されている場合はどうでしょうか。

別の考え方で問題を解決し、new_functionを定義する

def add_a_book(func):
    def wrapper():
        return func() + "Python Cookbook"
    return wrapper

次に、それを採用します。

In [14]: read_a_book = add_a_book(read_a_book)
In [15]: read_a_book()
Out[15]: 'I am reading the book: Python Cookbook'

多田さん、ほら、read_a_book内蓋は触れずに修正しました。私が装備しているのを止めるものは何もないdecorator

何について @

@add_a_book
def read_a_book():
    return "I am reading the book: "
In [17]: read_a_book()
Out[17]: 'I am reading the book: Python Cookbook'

@add_a_bookは空想的で便利な言い方ですread_a_book = add_a_book(read_a_book)、それは構文上の砂糖であり、それ以上に凝ったものはありません。


16

Numpyライブラリを使用しているpythonノートブックのコードを参照している場合は、行列乗算を@ operator意味します。例えば:

import numpy as np
def forward(xi, W1, b1, W2, b2):
    z1 = W1 @ xi + b1
    a1 = sigma(z1)
    z2 = W2 @ a1 + b2
    return z2, a1

8

Python 3.5以降、 '@'はMATRIX MULTIPLICATIONの専用のインフィックス記号として使用されます(PEP 0465- https: //www.python.org/dev/peps/pep-0465/を参照)


6

関数とメソッドのラッピング(関数を受け取って拡張された関数を返す関数)を読みやすく、理解しやすくするために、デコレーターがPythonに追加されました。元の使用例は、メソッドをクラスメソッドまたは静的メソッドとして定義できるようにすることでした。デコレータ構文がないと、かなりまばらで反復的な定義が必要になります。

class WithoutDecorators:
def some_static_method():
    print("this is static method")
some_static_method = staticmethod(some_static_method)

def some_class_method(cls):
    print("this is class method")
some_class_method = classmethod(some_class_method)

デコレーター構文が同じ目的で使用されている場合、コードは短く、理解しやすくなります。

class WithDecorators:
    @staticmethod
    def some_static_method():
        print("this is static method")

    @classmethod
    def some_class_method(cls):
        print("this is class method")

一般的な構文と可能な実装

デコレータは通常、名前付きオブジェクトであり(ラムダ式は許可されていません)、呼び出されたときに1つの引数を受け入れ(デコレートされた関数になります)、別の呼び出し可能なオブジェクトを返します。ここでは、「関数」の代わりに「呼び出し可能」を使用します。デコレータはメソッドと関数の範囲で議論されることが多いですが、それらに限定されません。実際、呼び出し可能なものはすべて(_call__メソッドを実装するオブジェクトは呼び出し可能と見なされます)、デコレータとして使用でき、それらによって返されるオブジェクトは単純な関数ではなく、独自の__call_メソッドを実装するより複雑なクラスのインスタンスが多くなります。

デコレータの構文は、単なる構文上の糖衣です。次のデコレータの使用法を検討してください。

@some_decorator
def decorated_function():
    pass

これは常に明示的なデコレータ呼び出しと関数の再割り当てで置き換えることができます:

def decorated_function():
    pass
decorated_function = some_decorator(decorated_function)

ただし、後者は可読性が低く、1つの関数で複数のデコレータが使用されているかどうかを理解することも非常に困難です。以下に示すように、デコレータは複数の異なる方法で使用できます。

機能として

カスタムデコレータを記述する方法はたくさんありますが、最も簡単な方法は、元の関数呼び出しをラップするサブ関数を返す関数を記述することです。

一般的なパターンは次のとおりです。

def mydecorator(function):
    def wrapped(*args, **kwargs):
        # do some stuff before the original
        # function gets called
        result = function(*args, **kwargs)
        # do some stuff after function call and
        # return the result
        return result
    # return wrapper as a decorated function
    return wrapped

クラスとして

ほとんどの場合、デコレータは関数を使用して実装できますが、ユーザー定義クラスを使用する方が適切な場合もあります。これは、デコレーターが複雑なパラメーター化を必要とする場合、または特定の状態に依存する場合に当てはまります。

クラスとしての非パラメータ化デコレータの一般的なパターンは次のとおりです。

class DecoratorAsClass:
    def __init__(self, function):
        self.function = function

    def __call__(self, *args, **kwargs):
        # do some stuff before the original
        # function gets called
        result = self.function(*args, **kwargs)
        # do some stuff after function call and
        # return the result
        return result

パラメータ化デコレータ

実際のコードでは、パラメーター化できるデコレーターを使用する必要があることがよくあります。関数がデコレーターとして使用される場合、解決策は単純です。2番目のレベルのラッピングを使用する必要があります。以下は、装飾された関数が呼び出されるたびに指定された回数だけ実行を繰り返すデコレータの簡単な例です。

def repeat(number=3):
"""Cause decorated function to be repeated a number of times.

Last value of original function call is returned as a result
:param number: number of repetitions, 3 if not specified
"""
def actual_decorator(function):
    def wrapper(*args, **kwargs):
        result = None
        for _ in range(number):
            result = function(*args, **kwargs)
        return result
    return wrapper
return actual_decorator

この方法で定義されたデコレーターはパラメーターを受け入れることができます。

>>> @repeat(2)
... def foo():
...     print("foo")
...
>>> foo()
foo
foo

パラメータ化されたデコレータの引数にデフォルト値がある場合でも、その名前の後の括弧は必須です。上記のデコレータをデフォルトの引数で使用する正しい方法は次のとおりです。

>>> @repeat()
... def bar():
...     print("bar")
...
>>> bar()
bar
bar
bar

最後に、Propertiesでデコレータを確認します。

プロパティ

プロパティは、属性をメソッドのセットにリンクする方法を知っている組み込み記述子タイプを提供します。プロパティは4つのオプションの引数を取ります:fget、fset、fdel、およびdoc。最後のものは、あたかもそれがメソッドであるかのように、属性にリンクされているdocstringを定義するために提供できます。次に、2つのコーナーポイントを格納する属性に直接アクセスするか、widthとheightプロパティを使用して制御できるRectangleクラスの例を示します。

class Rectangle:
    def __init__(self, x1, y1, x2, y2):
        self.x1, self.y1 = x1, y1
        self.x2, self.y2 = x2, y2

    def _width_get(self):
        return self.x2 - self.x1

    def _width_set(self, value):
        self.x2 = self.x1 + value

    def _height_get(self):
        return self.y2 - self.y1

    def _height_set(self, value):
        self.y2 = self.y1 + value

    width = property(
        _width_get, _width_set,
        doc="rectangle width measured from left"
    )
    height = property(
        _height_get, _height_set,
        doc="rectangle height measured from top"
    )

    def __repr__(self):
        return "{}({}, {}, {}, {})".format(
            self.__class__.__name__,
            self.x1, self.y1, self.x2, self.y2
    )

プロパティを作成するための最良の構文は、プロパティをデコレータとして使用することです。これにより、クラス内のメソッドシグネチャの数減り、コードが読みやすくなり、保守しやすくなります。デコレータを使用すると、上記のクラスは次のようになります。

class Rectangle:
    def __init__(self, x1, y1, x2, y2):
        self.x1, self.y1 = x1, y1
        self.x2, self.y2 = x2, y2

    @property
    def width(self):
        """rectangle height measured from top"""
        return self.x2 - self.x1

    @width.setter
    def width(self, value):
        self.x2 = self.x1 + value

    @property
    def height(self):
        """rectangle height measured from top"""
        return self.y2 - self.y1

    @height.setter
    def height(self, value):
        self.y2 = self.y1 + value

2

他の人が別の方法で持っているものを言うために:はい、それはデコレータです。

Pythonでは、次のようになります。

  1. 関数の作成(@呼び出しの下に続く)
  2. 作成した関数を操作するために別の関数を呼び出す。これは新しい関数を返します。呼び出す関数は@の引数です。
  3. 定義された関数を、返された新しい関数で置き換える。

関数はオブジェクトであり、必要なのは命令だけなので、これはあらゆる種類の便利なものに使用できます。


2

@記号は、plydata / pandasデータフレームクエリ内の変数にアクセスするためにも使用されますpandas.DataFrame.query。例:

df = pandas.DataFrame({'foo': [1,2,15,17]})
y = 10
df >> query('foo > @y') # plydata
df.query('foo > @y') # pandas

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