【问题标题】:What does the "at" (@) symbol do in Python?\"at\" (@) 符号在 Python 中有什么作用?
【发布时间】:2022-12-02 23:25:09
【问题描述】:

@ 符号在 Python 中有什么作用?

【问题讨论】:

    标签: python syntax python-decorators


    【解决方案1】:

    @ 符号位于开始一行用于类和函数装饰者:

    最常见的 Python 装饰器是:

    一个@中间一行可能是矩阵乘法:

    【讨论】:

    【解决方案2】:

    例子

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

    这表明您在装潢师基本上只是在 @ 符号之后立即作为 argument 传递给 function/method

    初见

    微框架烧瓶介绍装饰者从一开始就采用以下格式:

    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 的参数中的 lambda,而不是(例如)在此处使用装饰器的句法或实际好处是什么? (后一个示例在 Node.js http.Server 和 Express 路由中很常见。)
    【解决方案3】:

    在 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/…下的所有内容
    【解决方案4】:

    此代码 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,对吗?
    • @Viragos 您定义的名称some_func 等于decorator(some_func) 给出的函数。所以两个some_func实例都是函数,第一个只是被保存为装饰版本。
    【解决方案5】:

    “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 表达式酒吧在另一种情况下。

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

    堆叠装饰器

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

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

    【讨论】:

      【解决方案6】:

      “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'"
      

      然而,如果不允许我操作原始函数,或者如果有数千个这样的函数要处理怎么办。

      通过不同的思考来解决问题并定义一个新的函数

      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) 的一种奇特而方便的表达方式,它是一种语法糖,没有比它更花哨的了。

      【讨论】:

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

      如果您指的是正在使用的 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 中添加了装饰器来制作函数和方法包装(接收函数并返回增强函数的函数)更易于阅读和理解。最初的用例是能够将方法定义为类方法或静态方法。如果没有装饰器语法,它将需要一个相当稀疏和重复的定义:

        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 表达式) 在调用时接受单个参数(它将是装饰函数)并返回另一个可调用对象。这里用“Callable”代替“function”是有预谋的。虽然装饰器通常在方法和函数的范围内进行讨论,但并不限于它们。事实上,任何可调用的东西(任何实现 _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
        

        【讨论】:

          【解决方案9】:

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

          【讨论】:

            【解决方案10】:

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

            这段代码:

            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/

            【讨论】:

              【解决方案11】:

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

              【讨论】:

                【解决方案12】:

                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是内部函数的名称,实际上只在这个装饰器定义中使用。功能是被装饰的函数。 在内部函数wrapped_func,我们可以在之前和之后做任何事情功能叫做。装饰器定义好后,我们简单的使用如下。

                @function_decorator
                def func():
                    pass
                

                然后,每当我们调用函数功能,我们在装饰器中定义的行为也将被执行。

                例子 :

                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
                

                【讨论】:

                  【解决方案13】:

                  换句话说别人有什么:对,就是装饰器。

                  在 Python 中,它就像:

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

                    这可以用于各种有用的东西,之所以成为可能,是因为函数是对象,只需要指令。

                  【讨论】:

                    【解决方案14】:

                    @ 符号还用于访问 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] 更美观,所以我更愿意尽可能使用它。
                    猜你喜欢
                    • 2011-09-17
                    • 2022-04-26
                    • 2015-10-27
                    相关资源
                    最近更新 更多