web-dev-qa-db-fra.com

Que fait le symbole "at" (@) en Python?

Je regarde du code Python qui utilisait le symbole @, mais je n'ai aucune idée de ce qu'il fait. Je ne sais pas non plus quoi rechercher en tant que recherche Python docs ou Google ne renvoie pas de résultats pertinents lorsque le symbole @ est inclus.

461
AJ00200

Un symbole @ au début d'une ligne est utilisé pour les décorateurs de classe, fonction et méthode .

Lire la suite ici:

PEP 318: Décorateurs

Décorateurs Python

Les décorateurs les plus courants Python que vous rencontrerez sont:

@ propriété

@ classmethod

@ staticmethod

Si vous voyez un @ au milieu d'une ligne, c'est différent, la multiplication matricielle. Faites défiler la liste pour voir d'autres réponses qui traitent de l'utilisation de @.

241
FogleBird

Préambule

J'admets qu'il m'a fallu plus de quelques instants pour saisir pleinement ce concept, alors je vais partager ce que j'ai appris pour sauver les autres.

Le nom décorateur - la chose que nous définissons en utilisant la syntaxe @ avant une définition de fonction - était probablement le principal responsable ici.

Exemple

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']

Cela montre que la function/method/class que vous définissez après un décorateur est simplement passée en tant que argument à la function/method immédiatement après le signe @.

Première observation

Le microframework Flask introduit décorateurs depuis le début dans le format suivant:

from flask import Flask
app = Flask(__name__)

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

Cela se traduit par:

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

Réaliser cela m'a enfin permis de me sentir en paix avec Flask.

300
Morgan Wilde

Cet extrait de code:

def decorator(func):
   return func

@decorator
def some_func():
    pass

Est équivalent à ce code:

def decorator(func):
    return func

def some_func():
    pass

some_func = decorator(some_func)

Dans la définition d'un décorateur, vous pouvez ajouter des éléments modifiés qui ne seraient pas renvoyés normalement par une fonction.

164
Matheus Araujo

Dans Python 3.5, vous pouvez surcharger @ en tant qu'opérateur. Il est nommé __matmul__, car il est conçu pour effectuer la multiplication matricielle, mais peut être celui que vous souhaitiez. Voir PEP465 pour plus de détails.

Ceci est une simple implémentation de la multiplication matricielle.

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)

Ce code donne:

[[18, 14], [62, 66]]
108
jinhwanlazy

Que fait le symbole “at” (@) en Python?

En bref, il est utilisé dans la syntaxe de décorateur et pour la multiplication de matrices.

Dans le contexte des décorateurs, cette syntaxe:

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

est équivalent à ceci:

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

decorated_function = decorator(decorated_function)

Dans le contexte de la multiplication de matrice, a @ b appelle a.__matmul__(b) - en créant cette syntaxe:

a @ b

équivalent à

dot(a, b)

et

a @= b

équivalent à

a = dot(a, b)

dot est, par exemple, la fonction de multiplication de matrice numpy et a et b sont des matrices.

Comment pouvez-vous découvrir cela par vous-même?

Je ne sais pas non plus quoi rechercher en tant que recherche Python docs ou Google ne renvoie pas de résultats pertinents lorsque le symbole @ est inclus.

Si vous voulez avoir une vue assez complète de ce que fait un élément particulier de la syntaxe python, regardez directement le fichier de grammaire. Pour la branche 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

Nous pouvons voir ici que @ est utilisé dans trois contextes:

  • décorateurs
  • un opérateur entre facteurs
  • un opérateur d'affectation augmenté

Syntaxe du décorateur:

Une recherche google pour "decorator python docs" donne comme résultat principal la section "Instructions composées" de la "Référence du langage Python". En descendant vers le section sur les définitions de fonctions , que nous pouvons trouver en cherchant le mot "décorateur", nous voyons que ... il y a beaucoup à lire. Mais la Parole, "décorateur" est un lien vers le glossaire , qui nous dit:

décorateur

Une fonction renvoyant une autre fonction, généralement appliquée en tant que transformation de fonction à l'aide de la syntaxe @wrapper. Les exemples courants pour les décorateurs sont classmethod() et staticmethod().

La syntaxe du décorateur est simplement du sucre syntaxique, les deux définitions de fonction suivantes sont sémantiquement équivalentes:

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

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

Le même concept existe pour les classes, mais y est moins utilisé. Consultez la documentation pour les définitions de fonction et les définitions de classe pour en savoir plus sur les décorateurs.

Donc, on voit que

@foo
def bar():
    pass

est sémantiquement identique à:

def bar():
    pass

bar = foo(bar)

Ils ne sont pas exactement les mêmes car Python évalue l'expression foo (qui peut être une recherche en pointillé et un appel de fonction) avant la barre avec le décorateur (syntaxe @), mais évalue l'expression foo après dans l’autre cas.

(Si cette différence fait une différence dans la signification de votre code, vous devriez reconsidérer ce que vous faites dans votre vie, car ce serait pathologique.)

Décorateurs empilés

Si nous revenons à la documentation sur la syntaxe de définition de fonction, nous voyons:

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

est à peu près équivalent à

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

Ceci est une démonstration que nous pouvons appeler une fonction qui est d'abord un décorateur, ainsi que des décorateurs empilés. Les fonctions, en Python, sont des objets de première classe, ce qui signifie que vous pouvez transmettre une fonction sous forme d'argument à une autre et renvoyer des fonctions. Les décorateurs font ces deux choses.

Si nous empilons des décorateurs, la fonction, telle que définie, est d'abord transmise au décorateur immédiatement supérieur, puis à la suivante, etc.

Cela résume l’utilisation de @ dans le contexte des décorateurs.

L'opérateur, @

Dans la section d'analyse lexicale de la référence de langue, nous avons une section sur les opérateurs , qui inclut @, ce qui en fait également un opérateur:

Les jetons suivants sont des opérateurs:

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

et dans la page suivante, le modèle de données, nous avons la section Emulation des types numériques ,

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

[...] Ces méthodes sont appelées pour implémenter les opérations arithmétiques binaires (+, -, *, @, /, //, [...]

Et on voit que __matmul__ correspond à @. Si nous recherchons dans la documentation "matmul", nous obtenons un lien vers Nouveautés de Python 3.5 avec "matmul" sous un en-tête "PEP 465 - Opérateur d’infix dédié pour la multiplication de matrices ".

il peut être implémenté en définissant __matmul__(), __rmatmul__() et __imatmul__() pour une multiplication de matrice régulière, réfléchie et sur place.

(Nous apprenons donc maintenant que @= est la version sur place). Il explique plus loin:

La multiplication matricielle est une opération particulièrement courante dans de nombreux domaines des mathématiques, des sciences, de l'ingénierie, et l'ajout de @ permet d'écrire du code plus propre:

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

au lieu de:

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

Bien que cet opérateur puisse être surchargé pour faire presque n'importe quoi, dans numpy, par exemple, nous utiliserions cette syntaxe pour calculer le produit intérieur et extérieur des tableaux et des matrices:

>>> 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]])

Multiplication matricielle en place: @=

En recherchant l'utilisation antérieure, nous apprenons qu'il existe également la multiplication de matrice en place. Si nous essayons de l'utiliser, nous découvrirons peut-être qu'il n'est pas encore implémenté pour 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'.

Quand il sera mis en œuvre, je pense que le résultat devrait ressembler à ceci:

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

Que fait le symbole “at” (@) en Python?

@ symbol est un sucre syntaxique python permet d'utiliser decorator,
Pour paraphraser la question, c’est exactement ce que fait le décorateur en Python?

Dit simplement decorator vous permet de modifier la définition d'une fonction donnée sans toucher à son plus profond (c'est la fermeture).
C’est le cas le plus fréquent lorsque vous importez un merveilleux paquet depuis un tiers. Vous pouvez le visualiser, vous pouvez l'utiliser, mais vous ne pouvez toucher ni à son cœur ni à son cœur.

Voici un exemple rapide,
supposons que je définisse une fonction read_a_book sur 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: '

Vous voyez, j'ai oublié d'y ajouter un nom.
Comment résoudre un tel problème? Bien sûr, je pourrais redéfinir la fonction comme:

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

Néanmoins, que se passe-t-il si je ne suis pas autorisé à manipuler la fonction d'origine ou s'il en existe des milliers à gérer?.

Résoudre le problème en pensant différemment et définir une nouvelle fonction

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

Alors employez-le.

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'

Tada, tu vois, j'ai amendé read_a_book sans toucher à la fermeture intérieure. Rien ne m'arrête équipé de decorator.

Qu'en est-il de @

@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 est une manière élégante et pratique de dire read_a_book = add_a_book(read_a_book), c'est un sucre syntaxique, rien de plus sophistiqué à ce sujet.

29
Algebra

Si vous faites référence à du code dans un cahier python qui utilise la bibliothèque Numpy, alors @ operator signifie Multiplication de matrice. Par exemple:

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
14
f__society

À partir de Python 3.5, le symbole '@' est utilisé comme symbole infixe dédié à MATRIX MULTIPLICATION (PEP 0465 - voir https://www.python.org/dev/peps/pep) -0465 / )

8
dpodbori

@ symbol est également utilisé pour accéder aux variables dans une requête plydata/pandas dataframe, pandas.DataFrame.query. Exemple:

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

Les décorateurs ont été ajoutés dans Python pour faciliter la lecture et la compréhension de la fonction et de la méthode (une fonction qui reçoit une fonction et renvoie une fonction améliorée). Le cas d'utilisation initial était de pouvoir définir les méthodes en tant que méthodes de classe ou méthodes statiques en tête de leur définition. Sans la syntaxe de décorateur, cela nécessiterait une définition plutôt rare et répétitive:

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)

Si la syntaxe de décorateur est utilisée dans le même but, le code est plus court et plus facile à comprendre:

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

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

Syntaxe générale et implémentations possibles

Le décorateur est généralement un objet nommé ( les expressions lambda ne sont pas autorisées ) qui accepte un seul argument lorsque appelé (ce sera la fonction décorée) et retourne un autre objet appelable. "Appelable" est utilisé ici au lieu de "fonction" avec préméditation. Bien que les décorateurs soient souvent abordés dans le cadre de méthodes et de fonctions, ils ne se limitent pas à eux. En fait, tout ce qui est appelable (tout objet implémentant la méthode _call__ est considéré comme appelable) peut être utilisé comme décorateur et les objets renvoyés par celles-ci ne sont souvent pas de simples fonctions, mais davantage d'instances de classes plus complexes implémentant leur propre méthode __call_.

La syntaxe du décorateur est simplement un sucre syntaxique . Considérez l'utilisation suivante du décorateur:

@some_decorator
def decorated_function():
    pass

Cela peut toujours être remplacé par un appel explicite de décorateur et une réaffectation de fonction:

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

Cependant, ce dernier est moins lisible et très difficile à comprendre si plusieurs décorateurs sont utilisés pour une même fonction. Les décorateurs peuvent être utilisés de différentes manières, comme indiqué ci-dessous:

En tant que fonction

Il existe de nombreuses façons d'écrire des décorateurs personnalisés, mais le moyen le plus simple consiste à écrire une fonction qui renvoie une sous-fonction qui encapsule l'appel de fonction d'origine.

Les modèles génériques sont les suivants:

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

En tant que classe

Bien que les décorateurs puissent presque toujours être implémentés à l'aide de fonctions, il est parfois préférable d'utiliser des classes définies par l'utilisateur. Cela est souvent vrai lorsque le décorateur a besoin d'une paramétrisation complexe ou dépend d'un état spécifique.

Le modèle générique pour un décorateur non paramétré en tant que classe est le suivant:

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

Paramétrage des décorateurs

Dans le vrai code, il est souvent nécessaire d'utiliser des décorateurs paramétrables. Lorsque la fonction est utilisée en tant que décoratrice, la solution est simple: un second niveau d’emballage doit être utilisé. Voici un exemple simple de décorateur qui répète l'exécution d'une fonction décorée le nombre de fois spécifié à chaque appel:

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

Le décorateur défini de cette manière peut accepter les paramètres suivants:

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

Notez que même si les arguments par défaut du décorateur sont paramétrés, les parenthèses après son nom sont obligatoires. La manière correcte d'utiliser le décorateur précédent avec les arguments par défaut est la suivante:

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

Enfin, permet de voir les décorateurs avec Propriétés.

Propriétés

Les propriétés fournissent un type descripteur intégré qui sait comment lier un attribut à un ensemble de méthodes. Une propriété prend quatre arguments facultatifs: fget, fset, fdel et doc. Le dernier peut être fourni pour définir une chaîne de documentation liée à l'attribut comme s'il s'agissait d'une méthode. Voici un exemple de classe Rectangle pouvant être contrôlée soit par accès direct à des attributs stockant deux points d'angle, soit en utilisant les propriétés width et height:

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
    )

La meilleure syntaxe pour la création de propriétés consiste à utiliser property en tant que décorateur. Cela réduira le nombre de signatures de méthode à l’intérieur de la classe et rendra le code plus lisible et maintenable . Avec les décorateurs, la classe ci-dessus devient:

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
iun1x

Dire ce que les autres ont d'une manière différente: oui, c'est un décorateur.

En Python, c'est comme:

  1. Création d'une fonction (suit sous l'appel @)
  2. Appeler une autre fonction pour utiliser votre fonction créée. Ceci retourne une nouvelle fonction. La fonction que vous appelez est l'argument de la @.
  3. Remplacer la fonction définie par la nouvelle fonction renvoyée.

Cela peut être utilisé pour toutes sortes de choses utiles, rendues possibles parce que les fonctions sont des objets et juste des instructions nécessaires.

2
Mayur Patel

Cela indique que vous utilisez un décorateur. Voici exemple de Bruce Eckel à partir de 2008.

2
Peter Rowell