<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>kurass.eth</title>
        <link>https://paragraph.com/@kurass</link>
        <description>undefined</description>
        <lastBuildDate>Wed, 27 May 2026 18:08:21 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[Python装饰器]]></title>
            <link>https://paragraph.com/@kurass/python</link>
            <guid>BU8vmNV8bAEGHVUHe0ra</guid>
            <pubDate>Thu, 02 Feb 2023 16:53:12 GMT</pubDate>
            <description><![CDATA[如果我们一些函数内部有大量的与业务无关且重复的代码，那么就可以将这些代码抽出形成一个装饰器来使用。装饰器本质上是一个Python函数，它可以让其他函数在不需要做任何代码变动的前提下增加额外功能，装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景，比如：插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计，有了装饰器，我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。装饰器如何写普通装饰器使用@语法糖def debug(func): def wrapper(*args, **kwargs): print "[DEBUG]: enter {}()".format(func.__name__) print 'Prepare and say...', return func(*args, **kwargs) return wrapper # 返回 @debug def say(something): print "hello {}!".format(something) 装饰器中wrapper指定* args, **kwargs参数，就可以...]]></description>
            <content:encoded><![CDATA[<p>如果我们一些函数内部有大量的与业务无关且重复的代码，那么就可以将这些代码抽出形成一个装饰器来使用。</p><blockquote><p>装饰器本质上是一个Python函数，它可以让其他函数在不需要做任何代码变动的前提下增加额外功能，装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景，比如：插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计，有了装饰器，我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。</p></blockquote><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">装饰器如何写</h2><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">普通装饰器</h3><p>使用@语法糖</p><pre data-type="codeBlock" text="def debug(func):
    def wrapper(*args, **kwargs):
        print &quot;[DEBUG]: enter {}()&quot;.format(func.__name__)
        print &apos;Prepare and say...&apos;,
        return func(*args, **kwargs)
    return wrapper  # 返回

@debug
def say(something):
    print &quot;hello {}!&quot;.format(something)
"><code><span class="hljs-keyword">def</span> <span class="hljs-title function_">debug</span>(<span class="hljs-params">func</span>):
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">wrapper</span>(<span class="hljs-params">*args, **kwargs</span>):
        <span class="hljs-built_in">print</span> <span class="hljs-string">"[DEBUG]: enter {}()"</span>.<span class="hljs-built_in">format</span>(func.__name__)
        <span class="hljs-built_in">print</span> <span class="hljs-string">'Prepare and say...'</span>,
        <span class="hljs-keyword">return</span> func(*args, **kwargs)
    <span class="hljs-keyword">return</span> wrapper  <span class="hljs-comment"># 返回</span>

<span class="hljs-meta">@debug</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">say</span>(<span class="hljs-params">something</span>):
    <span class="hljs-built_in">print</span> <span class="hljs-string">"hello {}!"</span>.<span class="hljs-built_in">format</span>(something)
</code></pre><p>装饰器中wrapper指定* args, **kwargs参数，就可以接受任意被装饰函数的参数了。</p><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">高级点的装饰器</h3><h4 id="h-" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">带参数的装饰器</h4><p>编写带参数的装饰器，需要在普通装饰器上再封装一层。</p><pre data-type="codeBlock" text="def logging(level):
    def wrapper(func):
        def inner_wrapper(*args, **kwargs):
            print &quot;[{level}]: enter function {func}()&quot;.format(
                level=level,
                func=func.__name__)
            return func(*args, **kwargs)
        return inner_wrapper
    return wrapper

@logging(level=&apos;INFO&apos;)
def say(something):
    print &quot;say {}!&quot;.format(something)
"><code>def logging(level):
    def wrapper(func):
        def inner_wrapper(<span class="hljs-operator">*</span>args, <span class="hljs-operator">*</span><span class="hljs-operator">*</span>kwargs):
            print <span class="hljs-string">"[{level}]: enter function {func}()"</span>.format(
                level<span class="hljs-operator">=</span>level,
                func<span class="hljs-operator">=</span>func.__name__)
            <span class="hljs-keyword">return</span> func(<span class="hljs-operator">*</span>args, <span class="hljs-operator">*</span><span class="hljs-operator">*</span>kwargs)
        <span class="hljs-keyword">return</span> inner_wrapper
    <span class="hljs-keyword">return</span> wrapper

@logging(level<span class="hljs-operator">=</span><span class="hljs-string">'INFO'</span>)
def say(something):
    print <span class="hljs-string">"say {}!"</span>.format(something)
</code></pre><p>logging(level=‘INFO’)在被打在某个函数上时会立即执行，返回一个装饰器。这其实就是闭包，返回的装饰器里已经通过参数指定了level了。</p><h4 id="h-" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">类装饰器</h4><p>装饰器的接口约束：必须接受一个callable对象（一般callable对象都是函数），然后返回一个callable对象。除函数外，若某个对象重载了_<em>call</em>_()方法，那么这个对象就是callable的。</p><pre data-type="codeBlock" text="class Test():
    def __call__(self):
        print &apos;call me!&apos;
t = Test()
t()  # call me
"><code><span class="hljs-keyword">class</span> <span class="hljs-title class_">Test</span>():
    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__call__</span>(<span class="hljs-params"><span class="hljs-variable language_">self</span></span>):
        print <span class="hljs-string">'call me!'</span>
t = <span class="hljs-title class_">Test</span>()
t()  <span class="hljs-comment"># call me</span>
</code></pre><p><strong>call__这种前后带下划线的方法叫内置方法。重载这些方法会改变对象一些内部行为。像上面的例子中就让一个类对象能够被调用。 这样我们就可以实现类的装饰器了。我们让类的构造函数__init</strong>()j接受一个函数，然后重载_<em>call</em>_()并返回一个函数，就可以达到装饰函数的效果。</p><pre data-type="codeBlock" text="class logging(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print &quot;[DEBUG]: enter function {func}()&quot;.format(
            func=self.func.__name__)
        return self.func(*args, **kwargs)
@logging
def say(something):
    print &quot;say {}!&quot;.format(something)
"><code>class logging(object):
    def __init__(<span class="hljs-built_in">self</span>, func):
        <span class="hljs-built_in">self</span>.func <span class="hljs-operator">=</span> func

    def __call__(<span class="hljs-built_in">self</span>, <span class="hljs-operator">*</span>args, <span class="hljs-operator">*</span><span class="hljs-operator">*</span>kwargs):
        print <span class="hljs-string">"[DEBUG]: enter function {func}()"</span>.format(
            func<span class="hljs-operator">=</span><span class="hljs-built_in">self</span>.func.__name__)
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">self</span>.func(<span class="hljs-operator">*</span>args, <span class="hljs-operator">*</span><span class="hljs-operator">*</span>kwargs)
@logging
def say(something):
    print <span class="hljs-string">"say {}!"</span>.format(something)
</code></pre><h4 id="h-" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">带参数的类装饰器</h4><p>带参数的类装饰器就要复杂一些，但我们只需要遵守装饰器的接口约束就能很容易自己写出带参数的类装饰器。 类装饰器的构造函数的参数要改为装饰器的参数，然后_<em>call</em>_的参数改为修饰的函数对象，并返回一个函数对象用于接受被修饰的函数的参数。</p><pre data-type="codeBlock" text="class logging(object):
    def __init__(self, level=&apos;INFO&apos;):
        self.level = level
        
    def __call__(self, func): # 接受函数
        def wrapper(*args, **kwargs):
            print &quot;[{level}]: enter function {func}()&quot;.format(
                level=self.level,
                func=func.__name__)
            func(*args, **kwargs)
        return wrapper  #返回函数

@logging(level=&apos;INFO&apos;)
def say(something):
    print &quot;say {}!&quot;.format(something)
"><code>class logging(object):
    def __init__(<span class="hljs-built_in">self</span>, level<span class="hljs-operator">=</span><span class="hljs-string">'INFO'</span>):
        <span class="hljs-built_in">self</span>.level <span class="hljs-operator">=</span> level
        
    def __call__(<span class="hljs-built_in">self</span>, func): # 接受函数
        def wrapper(<span class="hljs-operator">*</span>args, <span class="hljs-operator">*</span><span class="hljs-operator">*</span>kwargs):
            print <span class="hljs-string">"[{level}]: enter function {func}()"</span>.format(
                level<span class="hljs-operator">=</span><span class="hljs-built_in">self</span>.level,
                func<span class="hljs-operator">=</span>func.__name__)
            func(<span class="hljs-operator">*</span>args, <span class="hljs-operator">*</span><span class="hljs-operator">*</span>kwargs)
        <span class="hljs-keyword">return</span> wrapper  #返回函数

@logging(level<span class="hljs-operator">=</span><span class="hljs-string">'INFO'</span>)
def say(something):
    print <span class="hljs-string">"say {}!"</span>.format(something)
</code></pre><h4 id="h-" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">内置装饰器</h4><p>@property @staticmethod @classmethod</p><h4 id="h-" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">一些坑</h4><ul><li><p>装饰过的函数函数签名_<em>name</em>_和文档_<em>doc</em>_已经改变。</p></li><li><p>不能装饰静态方法@staticmethod和类方法@classmethod，因为@staticmethod和@classmethod返回的补上callable对象，而是staticmethod和classmethod对象</p></li></ul><h4 id="h-" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">优化装饰器</h4><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://decorator.py">decorator.py</a></p></li><li><p>wrapt</p></li></ul>]]></content:encoded>
            <author>kurass@newsletter.paragraph.com (kurass.eth)</author>
        </item>
    </channel>
</rss>