2022-10-27

<section id="nice" data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px; color: black; padding: 0 10px; line-height: 1.6; word-spacing: 0px; letter-spacing: 0px; word-break: break-word; word-wrap: break-word; text-align: left; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;"><p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">10个美妙的Python装饰器
对Python编程语言中我最喜欢的一些装饰器的概述。</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">简介</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">关于Python编程语言的伟大之处在于,它在一个小包里装了所有的功能,这些功能非常有用。很多特性可以完全改变Python代码的功能,这使得该语言更加通用。此外,如果使用得当,这些功能中的一些可以起到缩短编写有效软件的时间的作用。Python的装饰器是一个很好的例子,它很好地完成了这些目标。</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">装饰器是快速编程宏,可以用来改变 Python 对象的行为。它们可以应用于类和函数,并且实际上可以做很多非常有趣的事情。装饰器可以用来缩短代码,加快代码速度,并完全改变代码在Python中的行为方式。不用说,这肯定能派上用场! 今天我想展示一些我认为值得一看的装饰器。有很多装饰器,但我挑选了一些我认为功能最酷的。</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">№1:<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; color: #1e6bb8; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all;"> @lru_cache</code></p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">这个列表中的第一个装饰器来自<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; color: #1e6bb8; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all;">functools</code>模块。这个模块包含在标准库中,而且非常容易使用。除了这个装饰器之外,它还包含了很多很酷的功能,但这个装饰器无疑是我最喜欢的。</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; color: #1e6bb8; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all;">@lru_cache</code>装饰器可以用来加快函数和操作的连续运行速度,使用缓存。当然,在使用时要注意交换和缓存的一些注意事项,但在通用的使用情况下,大多数时候这个装饰器是值得使用的。如果你想了解更多关于Functools和我为什么喜欢它,实际上我写了一整篇文章,你可以在这里阅读。</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">FuncTools:一个被低估的Python包
用Functools把你的Python函数提升到一个新的水平!</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">能够用一个简单的装饰器来加快代码的速度是令人难以置信的。一个可以从这样的装饰器中受益的函数的很好的例子是递归函数,比如这个计算阶乘的函数。</p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #272822; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #ddd; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #272822; border-radius: 5px;">def factorial(n):
    <span class="hljs-built_in" style="color: #a6e22e; line-height: 26px;">return</span> n  factorial(n-1) <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">if</span> n <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">else</span> 1
</code></pre>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">递归对于计算时间来说是相当困难的,但是添加这个装饰器可以帮助大大加快这个函数的连续运行速度。</p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #272822; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #ddd; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #272822; border-radius: 5px;">@lru_cache
def factorial(n):
    <span class="hljs-built_in" style="color: #a6e22e; line-height: 26px;">return</span> n 
 factorial(n-1) <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">if</span> n <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">else</span> 1
</code></pre>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">现在,每当我们运行这个函数时,前几个阶乘的计算将被保存到缓存中。因此,下次我们再去调用这个函数时,只需要计算我们之前使用的阶乘之后的阶乘。当然,并不是所有的阶乘计算都会被保存,但是很容易看出为什么这是一个很好的应用,这个装饰器可以加速一些自然缓慢的代码。</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">№2:<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; color: #1e6bb8; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all;"> @jit</code></p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">JIT是Just In Time编译的简称。通常我们在Python中运行一些代码时,首先发生的是编译。这种编译产生了一些开销,因为类型被分配了内存,并以未分配但命名的别名的形式存储。有了及时编译,我们在执行时就完成了大部分的工作。在很多方面,我们可以认为这类似于并行计算,Python 解释器同时处理两件事情,以节省一些时间。</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">Numba JIT编译器就是因为在Python中提供了这个概念而闻名。与<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; color: #1e6bb8; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all;">@lru_cache</code>类似,这个装饰器可以很容易地被调用,在你的代码中可以立即提升性能。Numba包提供了jit装饰器,这使得运行更密集的软件变得更容易,而不需要落入C语言。</p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #272822; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #ddd; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #272822; border-radius: 5px;">from numba import jit
import random

@jit(nopython=True)
def monte_carlo_pi(nsamples):
    acc = 0
    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">for</span> i <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">in</span> range(nsamples):
        x = random.random()
        y = random.random()
        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">if</span> (x  2 + y  2) < 1.0:
            acc += 1
    <span class="hljs-built_in" style="color: #a6e22e; line-height: 26px;">return</span> 4.0  acc / nsamples

</code></pre>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">№3:<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; color: #1e6bb8; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all;"> @do_twice</code></p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">do_twice装饰器的作用与它的名字差不多。这个装饰器可以被用来在一次调用中运行一个函数两次。这当然有一些用途,我发现它对调试特别有帮助。它的另一个用途是测量两个不同迭代的性能。以Functools为例,我们可以让一个函数运行两次,以便检查出现的改进。这个函数是由Python中的装饰器模块提供的,它在标准库中。</p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #272822; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #ddd; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #272822; border-radius: 5px;">from decorators import do_twice
@do_twice
def timerfunc():
    %timeit factorial(15)
</code></pre>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">№4: @count_calls</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">与do_twice装饰器的简单性相一致的是<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; color: #1e6bb8; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all;">count_calls</code>装饰器。这个装饰器可以用来提供一个函数在软件中被使用多少次的信息。</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">和do_twice一样,这在调试时肯定会派上用场。当添加到一个给定的函数时,我们将收到一个输出,告诉我们该函数在每次运行时被运行了多少次。这个装饰器也在标准库的装饰器模块中。</p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #272822; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #ddd; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #272822; border-radius: 5px;">from decorators import count_calls
@count_calls
def function_example():
    <span class="hljs-built_in" style="color: #a6e22e; line-height: 26px;">print</span>(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">"Hello World!"</span>
function_example()
function_example()
function_example()
</code></pre>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">№5:<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; color: #1e6bb8; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all;"> @dataclass</code></p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">为了在编写类时节省时间,我一直利用的最好的装饰器之一是数据类装饰器。这个装饰器可以用来快速编写常见的标准方法,这些方法通常在我们编写的类中出现。如果你想进一步了解这个装饰器,我也有一篇关于它的文章,你可以在这里阅读。</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">这个装饰器来自于dataclass模块。这个模块也在标准库中,所以没有必要用PIP来尝试这个例子</p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #272822; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #ddd; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #272822; border-radius: 5px;">from dataclasses import dataclass@dataclass
class Food:
    name: str
    unit_price: <span class="hljs-built_in" style="color: #a6e22e; line-height: 26px;">float</span>
    stock: int = 0
        
    def stock_value(self) -> <span class="hljs-built_in" style="color: #a6e22e; line-height: 26px;">float</span>:
        <span class="hljs-built_in" style="color: #a6e22e; line-height: 26px;">return</span>(self.stock 
 self.unit_price)
</code></pre>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">这段代码将自动创建一个初始化函数,<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; color: #1e6bb8; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all;">init()</code>,其中有必要的位置参数来填充我们类中的数据。它们也将自动提供给self,所以没有必要为了在类中获得一些数据参数而写一个很长的函数。</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">№6:<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; color: #1e6bb8; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all;"> @singleton</code></p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">为了理解<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; color: #1e6bb8; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all;"> @singleton</code>装饰器的目的,我们首先需要理解什么是<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; color: #1e6bb8; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all;">singleton</code>。singleton在某种意义上是全局类型的一个版本。这意味着,这些类型被定义为只存在一次。</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">尽管这些类型在像 C++ 这样的语言中很常见,但在 Python 中却很少见到。对于singleton,我们创建一个只使用一次的类,并对该类进行突变,而不是初始化构造类型。在这种情况下,类型的作用不像是模板,而更像是一个单一的受控对象。</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">通常情况下,singleton装饰器是由用户制作的,事实上并没有导入。这是因为单子仍然是对我们的单子装饰器中提供的模板的引用。我们可以命名一个单子函数并编写一个包装器,以便在我们的类上使用这个装饰器。</p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #272822; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #ddd; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #272822; border-radius: 5px;">def singleton(cls):
    instances = {}
    def wrapper(args, kwargs):
        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">if</span> cls not <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">in</span> instances:
          instances[cls] = cls(
args, kwargs)
        <span class="hljs-built_in" style="color: #a6e22e; line-height: 26px;">return</span> instances[cls]
    <span class="hljs-built_in" style="color: #a6e22e; line-height: 26px;">return</span> wrapper
@singleton
class cls:
    def func(self):
</code></pre>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">另一种方法是通过使用元类来解决这个问题。如果你想了解更多关于元类的信息,我在去年写了一篇文章,详细介绍了这些元类和它们的用途,你可以在这里查看。</p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #272822; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #ddd; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #272822; border-radius: 5px;">class Singleton(<span class="hljs-built_in" style="color: #a6e22e; line-height: 26px;">type</span>):
    _instances = {}
    def call(cls, 
args, 
kwargs):
        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">if</span> cls not <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">in</span> cls._instances:
            cls._instances[cls] = super(Singleton, cls).call(
args, kwargs)
        <span class="hljs-built_in" style="color: #a6e22e; line-height: 26px;">return</span> cls._instances[cls]
        
class Logger(object):
    metaclass = Singleton
</code></pre>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">№7: <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; color: #1e6bb8; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all;">@use_unit</code></p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">对于科学计算来说,有一个装饰器可能经常派上用场,那就是use_unit装饰器。这个装饰器可以用来改变返回方法的输出。</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">这对于那些不想给他们的数据添加测量单位,但仍想让人们知道这些单位是什么的人来说是很有用的。这个装饰器在任何模块中都不是真正可用的,但它是非常常用的,对科学应用来说非常有用。</p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #272822; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #ddd; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #272822; border-radius: 5px;">def use_unit(unit):
    <span class="hljs-string" style="color: #a6e22e; line-height: 26px;">"让一个函数返回一个给定单位的数量"</span>
    use_unit.ureg = pint.UnitRegistry()
    def decorator_use_unit(func):
        @functools.wraps(func)
        def wrapper_use_unit(
args, 
kwargs):
            value = func(
args, kwargs)
            <span class="hljs-built_in" style="color: #a6e22e; line-height: 26px;">return</span> value 
 use_unit.ureg(unit)
        <span class="hljs-built_in" style="color: #a6e22e; line-height: 26px;">return</span> wrapper_use_unit
    <span class="hljs-built_in" style="color: #a6e22e; line-height: 26px;">return</span> decorator_use_unit

@use_unit(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">"meters per second"</span>)
def average_speed(distance, duration):
    <span class="hljs-built_in" style="color: #a6e22e; line-height: 26px;">return</span> distance / duration
</code></pre>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">№7:<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; color: #1e6bb8; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all;"> @wraps</code></p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">函数包装器<code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; color: #1e6bb8; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all;"> @wraps</code>是一种设计模式,用于处理Python中相对复杂的函数。封装函数通常用来做一些可能被认为是更低级的、迭代的任务。封装函数的好处是,它可以用来极大地提高性能。和lru_cache装饰器一样,这个装饰器是由FuncTools包提供的。</p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #272822; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #ddd; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #272822; border-radius: 5px;">import functools as ft
</code></pre>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">wraps装饰器本身只是一个方便的装饰器,用于更新一个给定函数的包装器。当这个装饰器被调用时,该函数将在每次使用时更新其包装器。这对提高性能有很大的帮助,FuncTools中的许多工具都是如此。</p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #272822; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #ddd; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #272822; border-radius: 5px;">def my_decorator(f):
    @wraps(f)
    def wrapper(
args, kwds):
        <span class="hljs-built_in" style="color: #a6e22e; line-height: 26px;">print</span>(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">'Calling decorated function'</span>)
        <span class="hljs-built_in" style="color: #a6e22e; line-height: 26px;">return</span> f(
args, 
*kwds)
    <span class="hljs-built_in" style="color: #a6e22e; line-height: 26px;">return</span> wrapper
</code></pre>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;"><code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; color: #1e6bb8; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all;"> @wraps</code>本身就是典型的装饰器,wrapper装饰器的调用通常用于该装饰器调用中的一个包装器函数。写完这段代码后,我们就可以用那个包装器来装饰我们的新函数了。</p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #272822; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #ddd; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #272822; border-radius: 5px;">@my_decorator
def func(x):
    <span class="hljs-built_in" style="color: #a6e22e; line-height: 26px;">print</span>(x)   
  
</code></pre>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">№8: <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; color: #1e6bb8; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all;">@staticmethod</code></p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">在某些情况下,人们可能希望能够访问那些被私下定义的东西,就像在一个更全局的意义上。有时我们有一些包含在类中的函数,我们希望将其方法化,而这正是staticmethod装饰器的用途。</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">使用这个装饰器,我们可以使C++静态方法与我们的类一起工作。通常情况下,写在类的范围内的方法对该类来说是私有的,除非作为子类调用,否则无法访问。然而,在某些情况下,你可能希望采取一种更实用的方式来处理你的方法与数据的交互。使用这个装饰器,我们可以创建这两个选项,而不需要创建一个以上的函数。</p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #272822; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #ddd; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #272822; border-radius: 5px;">class Example:
    @staticmethod
    def our_func(stuff):
        <span class="hljs-built_in" style="color: #a6e22e; line-height: 26px;">print</span>(stuff)     
</code></pre>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">我们也不需要明确地提供我们的类作为一个参数。staticmethod装饰器为我们处理了这个问题。</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">№9: <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; color: #1e6bb8; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all;">@singledispatch</code></p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">FuncTools在这个列表中再次出击,推出了非常有用的singledispatch装饰器。单一调度是一种编程技术,在许多编程语言中都很常见,因为它是一种很好的编程方式。虽然我更倾向于多路调度,但我认为单路调度在很多方面都可以发挥同样的作用。</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">这个装饰器使得在 Python 中处理类型要容易得多。当我们在处理要通过同一个方法的多个类型时,情况更是如此。我在我的FuncTools文章中写了更多关于这个问题的内容,所以如果你对使用单一派发方法感兴趣,我确实推荐你使用(链接在#1。)</p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #272822; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #ddd; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #272822; border-radius: 5px;">@singledispatch
def fun(arg, verbose=False):
        <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">if</span> verbose:
            <span class="hljs-built_in" style="color: #a6e22e; line-height: 26px;">print</span>(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">"Let me just say,"</span>, end=<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">" "</span>)
        <span class="hljs-built_in" style="color: #a6e22e; line-height: 26px;">print</span>(arg)
@fun.register
def (arg: int, verbose=False):
    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">if</span> verbose:
        <span class="hljs-built_in" style="color: #a6e22e; line-height: 26px;">print</span>(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">"Strength in numbers, eh?"</span>, end=<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">" "</span>)
    <span class="hljs-built_in" style="color: #a6e22e; line-height: 26px;">print</span>(arg)
@fun.register
def 
(arg: list, verbose=False):
    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">if</span> verbose:
        <span class="hljs-built_in" style="color: #a6e22e; line-height: 26px;">print</span>(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">"Enumerate this:"</span>)
    <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">for</span> i, elem <span class="hljs-keyword" style="color: #f92672; font-weight: bold; line-height: 26px;">in</span> enumerate(arg):
        <span class="hljs-built_in" style="color: #a6e22e; line-height: 26px;">print</span>(i, elem)
</code></pre>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">№10: <code style="font-size: 14px; word-wrap: break-word; padding: 2px 4px; border-radius: 4px; margin: 0 2px; color: #1e6bb8; background-color: rgba(27,31,35,.05); font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; word-break: break-all;">@register</code></p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">注册函数来自于模块atexit。考虑到该模块的名称和工作原理,你可能会想到这个装饰器可能与终止时执行某些动作有关。</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">register装饰器命名了一个终止时要运行的函数。例如,这将与一些需要在你退出时进行保存的软件一起工作。</p>
<pre class="custom" data-tool="mdnice编辑器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block; background: url(https://files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #272822; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #ddd; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #272822; border-radius: 5px;">from atexit import register
@register
def termin():
    <span class="hljs-built_in" style="color: #a6e22e; line-height: 26px;">print</span>(<span class="hljs-string" style="color: #a6e22e; line-height: 26px;">" Goodbye!"</span> )
</code></pre>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">总结</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">不用说,Python 的装饰器是非常有用的。它们不仅可以用来减慢编写一些代码的时间,而且对加快代码的速度也有很大的帮助。当你发现装饰器的时候,不仅是令人难以置信的有用,而且编写你自己的装饰器也是一个好主意。</p>
<p data-tool="mdnice编辑器" style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">这些装饰器之所以强大,是因为装饰器作为一种特性在 Python 中非常强大。感谢你阅读我的文章,我希望它能让你注意到Python的一些很酷的能力!</p>
</section>

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • CD-Python-JY-1809班项目阶段教学内容 开篇 - 就业形势分析 就业方向Python后端开发工程师(...
    ychaochaochao阅读 980评论 0 1
  • Python语言进阶 数据结构和算法 算法:解决问题的方法和步骤 评价算法的好坏:渐近时间复杂度和渐近空间复杂度。...
    you的日常阅读 585评论 2 8
  • Python语言进阶 数据结构和算法算法:解决问题的方法和步骤评价算法的好坏:渐近时间复杂度和渐近空间复杂度。渐近...
    赤剑吟龙阅读 552评论 0 0
  • 多有参考全栈修炼手册Python常见的170道面试题关于python的面试题 python语言特性 python2...
    luoluoluol阅读 599评论 2 1
  • 显示有限的接口到外部 当发布python第三方package时, 并不希望代码中所有的函数或者class可以被外部...
    种花家LY阅读 385评论 0 0