【问题标题】:What does the "at" (@) symbol do in Python?Python 中的“at”(@) 符号有什么作用?
【发布时间】:2011-09-17 13:49:37
【问题描述】:

我正在查看一些使用 @ 符号的 Python 代码,但我不知道它的作用。我也不知道要搜索什么,因为搜索 Python 文档或包含 @ 符号时 Google 不会返回相关结果。

【问题讨论】:

    标签: python syntax python-decorators


    【解决方案1】:

    这表明您正在使用装饰器。这是 2008 年的 Bruce Eckel's example

    【讨论】:

      【解决方案2】:

      行首的@ 符号用于类、函数和方法装饰器

      在这里阅读更多:

      PEP 318: Decorators

      Python Decorators

      您会遇到的最常见的 Python 装饰器是:

      @property

      @classmethod

      @staticmethod

      如果你在一行的中间看到一个@,那就是另一回事了,矩阵乘法。 See this answer showing the use of @ as a binary operator.

      【讨论】:

      【解决方案3】:

      这段代码sn-p:

      def decorator(func):
         return func
      
      @decorator
      def some_func():
          pass
      

      相当于这段代码:

      def decorator(func):
          return func
      
      def some_func():
          pass
      
      some_func = decorator(some_func)
      

      在装饰器的定义中,您可以添加一些函数通常不会返回的修改内容。

      【讨论】:

      • 在这行s"ome_func = decorator(some_func)"中,第一个some_func是函数some_func的变量=,对吗?
      【解决方案4】:

      示例

      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基本上只是作为argument传递给function/@987654327 @ 紧跟在 @ 符号之后。

      第一眼

      微框架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
      

      意识到这一点终于让我对 Flask 感到安心。

      【讨论】:

      • 在 Flasks 的 app.route("/") 的情况下:此函数返回一个函数,您可以使用 hello() 作为参数调用该函数
      • 这里有装饰器的语法或实际好处是什么,而不是(例如)在定义 hello 之后立即调用类似 app.route("/", hello) 的东西,或者甚至将 hello 定义为app.route 的参数? (后一个示例在 Node.js http.Server 和 Express 路由中很常见。)
      【解决方案5】:

      用不同的方式说别人有什么:是的,它是一个装饰器。

      在 Python 中是这样的:

      1. 创建函数(在@调用下)
      2. 调用另一个函数来操作您创建的函数。这将返回一个新函数。您调用的函数是 @ 的参数。
      3. 用返回的新函数替换定义的函数。

      这可以用于各种有用的事情,因为函数是对象并且只是必要的指令。

      【讨论】:

        【解决方案6】:

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

        【讨论】:

        • 您还有@=(就地)运算符,即__imatmul__
        • 还有像这样的其他可覆盖操作符吗?我知道__add____sub__ 分别与+ 和- 相关联,但以前从未听说过@ 符号。还有其他人潜伏在那里吗?
        • @ThomasKimber 当然。查看docs.python.org/3/reference/… 下的所有内容
        【解决方案7】:

        从 Python 3.5 开始,“@”用作 MATRIX MULTIPLICATION 的专用中缀符号(PEP 0465 -- 参见 https://www.python.org/dev/peps/pep-0465/

        【讨论】:

          【解决方案8】:

          “at”(@) 符号在 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
          

          我们可以在这里看到@在三种情况下使用:

          • 装饰器
          • 因子之间的运算符
          • 增强的赋值运算符

          装饰器语法:

          Google 搜索“decorator python docs”给出了最重要的结果之一,即“Python 语言参考”的“复合语句”部分。向下滚动到section on function definitions,我们可以通过搜索单词“decorator”找到它,我们看到……有很多东西要读。但是"decorator" is a link to the glossary这个词告诉我们:

          装饰器

          返回另一个函数的函数,通常使用@wrapper 语法作为函数转换应用。常见的 装饰器的示例是 classmethod()staticmethod()

          装饰器语法只是语法糖,以下两个 函数定义在语义上是等价的:

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

          同样的概念也存在于类中,但不太常用。 有关函数定义和类定义,请参阅文档 有关装饰器的更多信息。

          所以,我们看到了

          @foo
          def bar():
              pass
          

          在语义上与:

          def bar():
              pass
          
          bar = foo(bar)
          

          它们并不完全相同,因为 Python 使用装饰器 (@) 语法在 bar 之前评估 foo 表达式(可能是点查找和函数调用),但在 之后评估 foo 表达式 em> 在其他情况下为 bar。

          (如果这种差异对代码的含义产生了影响,那么您应该重新考虑您的生活正在做什么,因为那将是病态的。)

          堆叠的装饰器

          如果我们回到函数定义语法文档,我们会看到:

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

          大致相当于

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

          这是一个演示,我们可以先调用作为装饰器的函数,也可以调用堆栈装饰器。在 Python 中,函数是第一类对象——这意味着您可以将函数作为参数传递给另一个函数,并返回函数。装饰器做这两件事。

          如果我们堆叠装饰器,定义的函数首先被传递给它上面的装饰器,然后是下一个,依此类推。

          这大概总结了 @ 在装饰器上下文中的用法。

          接线员,@

          在语言参考的词法分析部分,我们有一个section on operators,其中包含@,这使它也是一个运算符:

          以下标记是运算符:

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

          在下一页,数据模型中,我们有 Emulating Numeric Types 部分,

          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”的What's new in 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]])
          

          【讨论】:

            【解决方案9】:

            @ 符号还用于访问 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
            

            【讨论】:

            • 虽然如此,但至少在 pandas 中,有更简单(或至少更优雅)的方法可以做到这一点。按照你的例子,你可以做df[df.foo &gt; y](或者,更一般地说,df[df['foo'] &gt; y])。不同之处在于 df.foo 仅在列名仅包含没有空格的字母数字字符时才会按预期运行。 df[df['foo'] &gt; y] 更健壮,无论列标题是什么都可以使用,尽管我个人觉得df[df.foo &gt; y] 更美观,所以我更喜欢尽可能使用它。
            【解决方案10】:

            “at”(@) 符号在 Python 中的作用是什么?

            @符号是python提供的一种语法糖,用于利用decorator
            套用这个问题,这正是装饰器在 Python 中的作用?

            简单地说decorator 允许您修改给定函数的定义而无需触及其最里面(它是闭包)。
            当您从第三方导入精美的包时,这是最常见的情况。你可以想象它,你可以使用它,但你无法触及它的最深处和它的心脏。

            这是一个简单的例子,
            假设我在 Ipython 上定义了一个 read_a_book 函数

            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'
            

            Tada,你看,我修改了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_bookread_a_book = add_a_book(read_a_book) 的一种花哨且方便的说法,它是一种语法糖,没有什么比它更花哨的了。

            【讨论】:

            • 这是整个页面中绝对最好的页面,你解释得很好,只有在阅读你的答案后我才能理解它!太棒了!
            【解决方案11】:

            如果您指的是使用 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
            

            【讨论】:

              【解决方案12】:

              在 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")
              

              一般语法和可能的实现

              装饰器通常是一个命名对象(不允许使用 lambda 表达式),它在调用时接受单个参数(它将是装饰函数)并返回另一个可调用对象目的。这里使用“可调用”而不是带有预谋的“函数”。虽然装饰器经常在方法和函数的范围内讨论,但它们并不限于它们。事实上,任何可调用的东西(任何实现 _call__ 方法的对象都被认为是可调用的),都可以用作装饰器,并且它们返回的对象通常不是简单的函数,而是实现自己的 __call_ 方法的更复杂类的更多实例。

              装饰器语法只是语法糖。考虑以下装饰器用法:

              @some_decorator
              def decorated_function():
                  pass
              

              这总是可以用显式的装饰器调用和函数重新分配来代替:

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

              但是,如果在单个函数上使用多个装饰器,后者的可读性较差,也很难理解。 装饰器可以有多种不同的使用方式,如下所示:

              作为一个函数

              有很多方法可以编写自定义装饰器,但最简单的方法是编写一个函数,该函数返回一个包装原始函数调用的子函数。

              通用模式如下:

              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
              

              参数化装饰器

              在实际代码中,经常需要使用可以参数化的装饰器。当函数用作装饰器时,解决方案很简单——必须使用第二层包装。下面是一个装饰器的简单示例,它在每次调用装饰函数时重复执行指定次数:

              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
              

              最后让我们看看带有属性的装饰器。

              属性

              属性提供了一个内置的descriptor 类型,它知道如何将属性链接到一组方法。一个属性有四个可选参数: fget 、 fset 、 fdel 和 doc 。可以提供最后一个来定义链接到属性的文档字符串,就好像它是一个方法一样。下面是一个 Rectangle 类的示例,可以通过直接访问存储两个角点的属性或使用 width 和 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
                  )
              

              创建属性的最佳语法是使用属性作为装饰器。这将减少类中方法签名的数量 并使代码更可读性和可维护性。有了装饰器,上面的类就变成了:

              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
              

              【讨论】:

                【解决方案13】:

                Python 装饰器就像一个函数或类的包装器。还是太概念化了。

                def function_decorator(func):
                    def wrapped_func():
                        # Do something before the function is executed
                        func()
                        # Do something after the function has been executed
                    return wrapped_func
                

                上面的代码是一个装饰器的定义,用来装饰一个函数。 function_decorator 是装饰器的名称。

                wrapped_func 是内部函数的名称,实际上只用在这个装饰器定义中。 func 是被修饰的函数。 在内部函数 wrapped_func 中,我们可以在调用 func 之前和之后做任何事情。装饰器定义好后,我们简单的使用如下。

                @function_decorator
                def func():
                    pass
                

                那么,每当我们调用函数func时,我们在装饰器中定义的行为也会被执行。

                示例:

                from functools import wraps
                
                def mydecorator(f):
                    @wraps(f)
                    def wrapped(*args, **kwargs):
                        print "Before decorated function"
                        r = f(*args, **kwargs)
                        print "After decorated function"
                        return r
                    return wrapped
                
                @mydecorator
                def myfunc(myarg):
                    print "my function", myarg
                    return "return value"
                
                r = myfunc('asdf')
                print r
                

                输出:

                    Before decorated function
                    my function asdf
                    After decorated function
                    return value
                

                【讨论】:

                  【解决方案14】:

                  @ 可以是数学运算符或装饰器,但您的意思是装饰器。

                  这段代码:

                  def func(f):
                      return f
                  
                  func(lambda :"HelloWorld")()
                  

                  使用装饰器可以这样写:

                  def func(f):
                      return f
                  @func
                  def name():
                      return "Hello World"
                  
                  name()
                  

                  装饰器可以有参数。

                  你可以看到这个 GeeksforGeeks 帖子:https://www.geeksforgeeks.org/decorators-in-python/

                  【讨论】:

                    猜你喜欢
                    • 2022-04-26
                    • 2022-12-02
                    • 2015-10-27
                    相关资源
                    最近更新 更多