Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Python3 与 C# 扩展之~装饰器专栏

Python3 与 C# 扩展之~装饰器专栏

作者头像
逸鹏
发布于 2018-07-23 10:15:46
发布于 2018-07-23 10:15:46
1.1K00
代码可运行
举报
文章被收录于专栏:逸鹏说道逸鹏说道
运行总次数:0
代码可运行

上次知识回顾:Python3 与 C# 扩展之~基础衍生

终于期末考试结束了,聪明的小明同学现在当然是美滋滋的过暑假了,左手一只瓜,右手一本书~正在给老乡小张同学拓展他研究多日的知识点

1.NetCore装饰器模式

装饰器这次从 C#开始引入,上次刚讲 迭代器模式,这次把 装饰器模式也带一波(纯Python方向的可以选择性跳过,也可以当扩展)

其实通俗讲就是,给原有对象动态的添加一些额外的职责(毕竟动不动就改类你让其他调用的人咋办?也不符合开放封闭原则是吧~)

举个简单的例子:(https://github.com/lotapp/BaseCode/tree/master/netcore/3_Ext/Decorators)

BaseComponent.cs

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/// <summary>
/// 组件的抽象父类
/// </summary>
public abstract class BaseComponent
{
    /// <summary>
    /// 定义一个登录的抽象方法
    /// 其他方法,这边省略
    /// </summary>
    public abstract string Login();
}

LoginComponent.cs

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/// <summary>
/// 默认登录组件(账号+密码)
/// 其他方法省略
/// 友情提醒一下,抽象类里面可以定义非抽象方法
/// </summary>
public class LoginComponent : BaseComponent
{
    public override string Login()
    {
        return "默认账号密码登录";
    }
}

默认调用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static void Main(string[] args)
{
    var obj = new LoginComponent();
    var str = obj.Login();
    Console.WriteLine(str);
}

如果这时候平台需要添加微信第三方登录,怎么办?一般都是用继承来解决,其实还可以通过灵活的 装饰器来解决:(好处可以自己体会)

先定义一个通用装饰器(不一定针对登录,注册等等只要在BaseComponent中的都能用)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/// <summary>
/// 装饰器
/// </summary>
public class BaseDecorator : BaseComponent
{
    protected BaseComponent _component;
    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="obj">登录组件对象</param>
    protected BaseDecorator(BaseComponent obj)
    {
        this._component = obj;
    }
    public override string Login()
    {
        string str = string.Empty;
        if (_component != null) str = _component.Login();
        return str;
    }
}

现在根据需求添加微信登录:(符合开放封闭原则)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/// <summary>
/// 默认登录组件(账号+密码)
/// 其他方法省略
/// </summary>
public class WeChatLoginDecorator : BaseDecorator
{
    public WeChatLoginDecorator(BaseComponent obj) : base(obj)
    {
    }
    /// <summary>
    /// 添加微信第三方登录
    /// </summary>
    /// <returns></returns>
    public string WeChatLogin()
    {
        return "add WeChatLogin";
    }
}

调用:(原有系统该怎么用就怎么用,新系统可以使用装饰器来添加新功能)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static void Main(string[] args)
{
    #region 登录模块V2
    // 实例化登录装饰器
    var loginDecorator = new WeChatLoginDecorator(new LoginComponent());
    // 原有的登录方法
    var str1 = loginDecorator.Login();
    // 现在新增的登录方法
    var str2 = loginDecorator.WeChatLogin();
    Console.WriteLine($"{str1}\n{str2}");
    #endregion
}

结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
默认账号密码登录
add WeChatLogin

如果再加入QQ和新浪登录的功能就再添加一个V3版本的装饰器,继承当时V2版本的登录即可(版本迭代特别方便)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/// <summary>
/// 默认登录组件(账号+密码)
/// 其他方法省略
/// </summary>
public class LoginDecoratorV3 : WeChatLoginDecorator
{
    public LoginDecoratorV3(BaseComponent obj) : base(obj)
    {
    }

    /// <summary>
    /// 添加QQ登录
    /// </summary>
    /// <returns></returns>
    public string QQLogin()
    {
        return "add QQLogin";
    }

    /// <summary>
    /// 添加新浪登录
    /// </summary>
    /// <returns></returns>
    public string SinaLogin()
    {
        return "add SinaLogin";
    }
}

调用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static void Main(string[] args)
{
    #region 登录模块V3
    // 实例化登录装饰器
    var loginDecoratorV3 = new LoginDecoratorV3(new LoginComponent());
    // 原有的登录方法
    var v1 = loginDecoratorV3.Login();
    // 第二个版本迭代中的微信登录
    var v2 = loginDecoratorV3.WeChatLogin();
    // 新增的QQ和新浪登录
    var qqLogin = loginDecoratorV3.QQLogin();
    var sinaLogin = loginDecoratorV3.SinaLogin();
    Console.WriteLine($"{v1}\n{v2}\n{qqLogin}\n{sinaLogin}");
    #endregion
}

结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
默认账号密码登录
add WeChatLogin
add QQLogin
add SinaLogin

其实还有很多用处,比如原有系统缓存这块当时考虑不到,现在并发来了,已经上线了,原有代码又不太敢大幅度修改,这时候装饰器就很方便的给某些功能添加点缓存、测试、日记等等系列功能

实际场景说的已经很明白了,其他的自己摸索一下吧

2.Python装饰器

那Python怎么实现装饰器呢?小胖问道。

小明屁颠屁颠的跑过去说道,通过闭包咯~(闭包如果忘了,可以回顾一下)

2.1.装饰器引入

来看一个应用场景,以前老版本系统因为并发比较小,没考虑到缓存

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def get_data():
    print("直接数据库读取数据")

def main():
    get_data()

if __name__ == '__main__':
    main()

在不修改原有代码的前提下咋办?我们参照C#和Java写下如下代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 添加一个闭包
def cache(func):
    def decorator():
        print("给功能添加了缓存")
        if True:
            pass
        else:
            func()# 如果缓存失效则读取数据库获取新的数据
    return decorator

def get_data():
    print("直接数据库读取数据")

def main():
    f1 = cache(get_data)
    f1()
    print(type(f1))

if __name__ == '__main__':
    main()
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
给功能添加了缓存
<class 'function'>

小张问道:“怎么也这么麻烦啊,C#的那个我就有点晕了,怎么Python也这样啊?” f1=cache(get_data) f1()

小明哈哈一笑道:“人生苦短,我用Python~这句话可不是随便说着玩的,来来来,看看Python的语法糖”:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def cache(func):
    def wrapper():
        print("给功能添加了缓存")
        if True:
            pass
        else:
            func()  # 如果缓存失效则读取数据库获取新的数据
    return wrapper

@cache
def get_data():
    print("直接数据库读取数据")

def main():
    get_data()

if __name__ == '__main__':
    main()
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
给功能添加了缓存

其实

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@cache
def get_data()

等价于

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 把f1改成函数名字罢了。可以这么理解:get_data重写指向了一个新函数
get_data = cache(get_data)

小张同学瞪了瞪眼睛,努力回想着以前的知识点,然后脱口而出:“这不是我们之前讲的属性装饰器吗?而且好方便啊,这完全符合开放封闭原则啊!“

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Student(object):
    def __init__(self, name, age):
        # 一般需要用到的属性都直接放在__init__里面了
        self.name = name
        self.age = age

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self, name):
        self.__name = name

    @property
    def age(self):
        return self.__age

    @age.setter
    def age(self, age):
        if age > 0:
            self.__age = age
        else:
            print("age must > 0")

    def show(self):
        print("name:%s,age:%s" % (self.name, self.age))

小明也愣了愣,说道:”也对哦,你不说我都忘了,我们学习面向对象三大特性的时候经常用呢,怪不得这么熟悉呢“

随后又嘀咕了一句:”我怎么不知道开放封闭原则...“

小张嘲笑道:”这你都不知道?对扩展开放,对已经实现的代码封闭嘛~“

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 需要注意一点
def cache(func):
    print("装饰器开始装饰")
    def wrapper():
            print("给功能添加了缓存")
            if True:
                pass
            else:
                func()  # 如果缓存失效则读取数据库获取新的数据
    return wrapper

@cache # 当你写这个的时候,装饰器就开始装饰了,闭包里面的功能是你调用的时候执行
def get_data():
    print("直接数据库读取数据")
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
装饰器开始装饰

2.2.多个装饰器

小明赶紧扯开话题,”咳咳,我们接下来我们接着讲装饰器"

小张问道,像上面那个第三方登录的案例,想加多少加多少,Python怎么办呢?

小明一笑而过~

现在项目又升级了,要求每次调用都要打印一下日记信息,方便以后纠错,小张先用自己的理解打下了这段代码,然后像小明请教:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def log(func):
    def wrapper():
        print("输出日记信息")
        cache(func)()
    return wrapper

def cache(func):
    def wrapper():
        print("给功能添加了缓存")
        if True:
            pass
        else:
            func()  # 如果缓存失效则读取数据库获取新的数据
    return wrapper

@log
def get_data():
    print("直接数据库读取数据")

def main():
    get_data()

if __name__ == '__main__':
    main()
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
输出日记信息
给功能添加了缓存

小明刚美滋滋的喝着口口可乐呢,看到代码后一不小心喷了小张一脸,然后尴尬的说道:“Python又不是只能装饰一个装饰器,来看看我的代码”:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def log(func):
    print("开始装饰Log模块")
    def wrapper():
        print("输出日记信息")
        func()
    return wrapper

def cache(func):
    print("开始装饰Cache模块")
    def wrapper():
        print("给功能添加了缓存")
        if True:
            pass
        else:
            func()  # 如果缓存失效则读取数据库获取新的数据
    return wrapper

@log
@cache
def get_data():
    print("直接数据库读取数据")

def main():
    get_data()

if __name__ == '__main__':
    main()
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
开始装饰Cache模块
开始装饰Log模块
输出日记信息
给功能添加了缓存

小张耐心的看完了代码,然后说道:“咦,我发现它装饰的时候是从下往上装饰,执行的时候是从上往下啊?执行的时候程序本来就是从上往下,按照道理应该是从上往下装饰啊?”

小明神秘的说道:“你猜啊~你可以把它理解为寄快递和拆快递

小张兴奋的跳起来了:

装饰器:装快递,先包装里面的物品,然后再加个盒子。执行装饰器:拆快递,先拆外面的包装再拆里面的~简直妙不可言啊

2.3.带参装饰器

小明继续讲述他哥哥的血泪历史:

需求时刻在变,系统使用范围更广了,为了不砸场子,抠门的老板决定每年多花5W在技术研发的硬件支持上,这下子技术部老开心了,想想以前前端只能通过CDN和HTTP请求来缓存,后端只能依赖页面缓存和数据库缓存就心塞,于是赶紧新增加一台Redis云服务器。为了以后和现在缓存代码得变一变了,需要支持指定的缓存数据库:(如果不是维护别人搞的老项目,你这么玩保证被打死,开发的时候老老实实的工厂模式搞起)

带参数的装饰器一般都是用来记录logo日记比较多,自己开发知道debug模式,生产指定except模式等等

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 可以理解为,在原来的外面套了一层
def cache(cache_name):
    def decorator(func):
        def wrapper():
            if cache_name == "redis":
                print("给功能添加了Redis缓存")
            elif cache_name == "memcache":
                pass
            else:
                func()
        return wrapper
    return decorator

@cache("redis") # 相当于是:get_data = cache(”redis“)(get_data)
def get_data():
    print("直接数据库读取数据")

def main():
    get_data()

if __name__ == '__main__':
    main()
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
给功能添加了Redis缓存

小张很高兴,然后练了练手,然后质问小明道:”你是不是藏了一手!“

代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def log(func):
    def inner():
        print("%s log_info..." % func.__name__)
        func()
    return inner

@log
def login_in(name_str, pass_str):
    return "欢迎登录:%s" % (name_str)

@log
def login_out():
    print("已经退出登录")

@log
def get_data(id):
    print("%s:data xxx" % id)

def main():
    login_out()
    get_data(1)
    print(login_in("小明", "xxx"))

if __name__ == '__main__':
    main()
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
login_out log_info...
已经退出登录



---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-7-dcb695819107> in <module>()
     23 
     24 if __name__ == '__main__':
---> 25     main()


<ipython-input-7-dcb695819107> in main()
     19 def main():
     20     login_out()
---> 21     get_data(1)
     22     print(login_in("小明", "xxx"))
     23 


TypeError: inner() takes 0 positional arguments but 1 was given

2.4.通用装饰器

小明尴尬的笑了下,然后赶紧倾囊相授,定义一个通用的装饰器:(传参数就在外面套一层)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def log(func):
    @functools.wraps(func) # 签名下面一个案例就会讲
    def wrapper(*args,**kv):
        """可变参 + 关键字参数"""
        print("%s log_info..." % func.__name__)
        return func(*args,**kv)
    return wrapper

这部分知识如果忘记了可以回顾一下,我们之前讲的函数系列:https://www.cnblogs.com/dotnetcrazy/p/9175950.html

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def log(func):
    # 可变参 + 关键字参数
    def wrapper(*args,**kv):
        print("%s log_info..." % func.__name__)
        return func(*args,**kv)
    return wrapper

@log
def login_in(name_str, pass_str):
    return "欢迎登录:%s" % (name_str)

@log
def login_out():
    print("已经退出登录")

@log
def get_data(id):
    print("%s:data xxx" % id)

def main():
    login_out()
    get_data(1)
    print(login_in("小明", "xxx"))

if __name__ == '__main__':
    main()
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
login_out log_info...
已经退出登录
get_data log_info...
1:data xxx
login_in log_info...
欢迎登录:小明

2.5.扩展补充

其实装饰器可以做很多事情,比如强制类型检测等,先看几个扩展:

1.装饰器方法签名的问题

成也装饰器,败也装饰器,来个案例看看,装饰器装饰的函数真的就对原函数没点影响?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 添加一个闭包
def cache(func):
    def wrapper(*args,**kv):
        if True:
            print("缓存尚未失效:直接返回缓存数据")
        else:
            func(*args,**kv)
    return wrapper

def get_data(id):
    """获取数据"""
    print("通过%d直接数据库读取数据"%id)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 进行装饰
get_data = cache(get_data)
# 调用原有名称的函数
get_data(110)
# 发现虽然函数调用时候的名字没有变
# 但是内部签名却变成了闭包里面的函数名了
print(get_data.__name__)
print(get_data.__doc__)
# print(get_data.__annotations__)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
缓存尚未失效:直接返回缓存数据
wrapper
None

发现虽然函数调用时候的名字没有变,但是内部签名却变成了闭包里面的函数名了!

玩过逆向的人都知道,像你修改了apk文件,它看似一样,但签名就变了,得再处理才可能绕过原来的一些自效验的验证措施

这边一样的道理,你写了一个装饰器作用在某个函数上,但是这个函数的重要的元信息比如名字、文档字符串、注解和参数签名都丢失了。

functools里面的 wraps就帮我们干了这个事情(之前讲模块的时候引入了functools,随后讲衍生的时候用了里面的偏函数,这边讲讲 wraps

上面代码改改:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
from functools import wraps

# 添加一个闭包
def cache(func):
    @wraps(func)
    def wrapper(*args,**kv):
        if True:
            print("缓存尚未失效:直接返回缓存数据")
        else:
            func(*args,**kv)
    return wrapper

def get_data(id):
    """获取数据"""
    print("通过%d直接数据库读取数据"%id)

# 进行装饰
get_data = cache(get_data)
# 调用原有名称的函数
get_data(110)
# 签名已然一致
print(get_data.__name__)
print(get_data.__doc__)
# print(get_data.__annotations__)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
缓存尚未失效:直接返回缓存数据
get_data
获取数据

另外: @wraps有一个重要特征是它能让你通过属性 __wrapped__ 直接访问被包装函数,eg:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
get_data.__wrapped__(100)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
通过100直接数据库读取数据
2.装饰器传参的扩展(可传可不传)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import logging
from functools import wraps, partial

def logged(func=None, *, level=logging.DEBUG, name=None, message=None):
    if func is None:
        return partial(logged, level=level, name=name, message=message)

    logname = name if name else func.__module__
    log = logging.getLogger(logname)
    logmsg = message if message else func.__name__

    @wraps(func)
    def wrapper(*args, **kwargs):
        log.log(level, logmsg)
        return func(*args, **kwargs)
    return wrapper

@logged
def add(x, y):
    return x + y

@logged(level=logging.CRITICAL, name='测试')
def get_data():
    print("读数据ing")

def main():
    add(1,2)
    get_data()

if __name__ == '__main__':
    main()
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
get_data


读数据ing
3.类中定义装饰器

在类里面定义装饰器很简单,但是你首先要确认它的使用方式。比如到底是作为一个实例方法还是类方法:(别忘记写 selfcls

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
from functools import wraps

class A(object):
    # 实例方法
    def decorator1(self, func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            print("实例方法装饰器")
            return func(*args, **kwargs)
        return wrapper

    # 类方法
    @classmethod
    def decorator2(cls, func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            print("类方法装饰器")
            return func(*args, **kwargs)
        return wrapper
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 装饰方式不一样
a = A()
@a.decorator1 # 实例方法调用
def test1():
    pass

@A.decorator2 # 类方法调用
def test2():
    pass
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 调用一下
test1()
test2()
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
实例方法装饰器
类方法装饰器

在涉及到继承的时候。 例如,假设你想让在A中定义的装饰器作用在子类B中。你需要像下面这样写:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class B(A):
    @A.decorator2
    def test(self):
        pass

也就是说,装饰器要被定义成类方法并且你必须显式的使用父类名去调用它。

你不能使用 @B.decorator2 ,因为在方法定义时,这个类B还没有被创建。

4.类装饰器

看这个之前,我们先来看看怎么把类当函数一样使用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class A(object):
    def __call__(self):
        print("让类对象能像函数一样调用的~魔法方法")

def main():
    a = A()
    a()

if __name__ == '__main__':
    main()
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
让类对象能像函数一样调用的~魔法方法

重载这些魔法方法一般会改变对象的内部行为。上面这个例子就让一个类对象拥有了被调用的行为。

装饰器函数其实是这样一个接口约束,它必须接受一个 callable对象作为参数,然后返回一个 callable对象。

在Python中一般 callable对象都是函数,但也有例外。只要某个对象重写了 __call__() 方法,那么这个对象就是callable的

用类来实现呢?我们可以让类的构造函数 __init__()接受一个函数,然后重载 __call__()并返回一个函数,也可以达到装饰器函数的效果

我们拿之前说的通用装饰器的例子继续说:(一般来说装饰器就定义成方法,然后给需要添加的函数或者类方法添加就基本够用了)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
from functools import wraps

class Log(object):
    def __init__(self, func):
        wraps(func)(self)  # @wraps(func) 访问不到,所以用这种方式
        self.__func = func

    def __call__(self, *args, **kvs):
        print("%s log_info..." % self.__func.__name__)
        return self.__func(*args, **kvs)
@Log
def login_in(name_str, pass_str):
    return "欢迎登录:%s" % (name_str)

@Log
def login_out():
    print("已经退出登录")

@Log
def get_data(id):
    print("%s:data xxx" % id)

def main():
    login_out()
    get_data(1)
    print(login_in("小明", "xxx"))

if __name__ == '__main__':
    main()
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
login_out log_info...
已经退出登录
get_data log_info...
1:data xxx
login_in log_info...
欢迎登录:小明

对类进行装饰的测试:(以上一个案例为例)

装饰实例方法的时候容易出现莫名其妙的错误,所以一般加上get方法(反射系列的稍后会讲)

eg:show()missing1required positional argument:'self'

完整写法:(你可以去除 __get__试试)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import types
from functools import wraps

class Log(object):
    def __init__(self, func):
        wraps(func)(self)  # @wraps(func) 访问不到,所以用这种方式
        self.__func = func

    def __call__(self, *args, **kvs):
        print("%s log_info..." % self.__func.__name__)
        return self.__func(*args, **kvs)

    # 装饰实例方法的时候容易出现莫名其妙的错误,所以一般加上get方法
    # eg:show() missing 1 required positional argument: 'self'
    def __get__(self, instance, cls):
        if instance is None:
            return self
        else:
            return types.MethodType(self, instance)

class LoginComponent(object):
    def __init__(self, name):
        self.__name = name

    @Log
    def show(self):
        """实例方法"""
        print("欢迎你:%s" % self.__name)

    @classmethod
    @Log  # 写在下面("从下往上装,从上往下拆")
    def login_in(cls):
        """类方法"""
        print("登录ing")

    @staticmethod
    @Log
    def show_news():
        """静态方法"""
        print("今天的新闻是...")

def main():
    LoginComponent.login_in()
    LoginComponent.show_news()
    login = LoginComponent("小明")
    login.show()

if __name__ == '__main__':
    main()
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
login_in log_info...
登录ing
show_news log_info...
今天的新闻是...
show log_info...
欢迎你:小明

更多的可以参考如下链接:

详解Python装饰器

将装饰器定义为类

Python中的init()和call()函数

python中装饰器的使用和类装饰器在类中方法的使用

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-07-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 我为Net狂 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Python装饰器是个什么鬼?
所谓的装饰器,其实就是通过装饰器函数,来修改原函数的一些功能,使得原函数不需要修改。
MeteoAI
2019/07/30
9290
Python中的装饰器详解及实际应用
在Python编程中,装饰器(Decorator)是一种强大而灵活的工具,用于修改函数或方法的行为。它们广泛应用于许多Python框架和库,如Flask、Django等。本文将深入探讨装饰器的概念、使用方法,并提供实际应用的代码示例和详细解析。
一键难忘
2024/04/16
7040
Python函数装饰器高级用法
函数装饰器和闭包紧密结合,入参func代表被装饰函数,通过自由变量绑定后,调用函数并返回结果。
dongfanger
2021/06/10
9310
Python函数装饰器高级用法
一文搞定 Python 装饰器
最近趁着有时间,搞了一下 MCP,MCP server 中定义的 Function ,想要被 Client 使用,必须添加 @mcp.tool()。这其实是 python 的装饰器
shengjk1
2025/05/16
2800
Python装饰器实战场景解析:从原理到应用的10个经典案例
装饰器是Python中最具魅力的特性之一,它用简洁的语法实现了代码的横向扩展。本文通过10个真实开发场景,带你从入门到精通这个"魔法工具"。每个案例都包含问题背景、解决方案和源码解析,让你轻松掌握装饰器的核心思想。
富贵软件
2025/09/02
1180
Python 装饰器怎么用?
Python 的装饰器是一种非常强大的工具,允许程序员在不修改原有函数定义的情况下,增加额外的功能。装饰器的应用场景非常广泛,从日志记录、性能测试、事务处理到缓存、权限校验等都有涉及。本文将通过几个实例详细介绍如何巧妙地使用 Python 中的装饰器来解决实际问题。
程序猿川子
2024/07/23
2070
Python 装饰器怎么用?
Python装饰器学习笔记
装饰器是 Python 的一个重要部分。它是修改其他函数的功能的函数,有助于让我们的代码更简短
周希
2019/10/15
7050
一文搞懂Python装饰器
装饰器是一种非常有用和强大的python特性,它可以让你在不修改原函数的情况下,给函数添加一些额外的功能。在这篇文章中,我将介绍装饰器的概念、语法、作用和实例。
Echo_Wish
2023/11/30
3010
Python技巧 | 一个任务超时退出的装饰器,用起来真方便~
我们日常在使用的各种网络请求库时都带有timeout参数,例如request库。这个参数可以使请求超时就不再继续了,直接抛出超时错误,避免等太久。
快学Python
2021/08/09
1.2K0
Python装饰器的实用技巧与原理剖析
装饰器是Python中一项强大而优雅的功能,能够在不修改原函数代码的情况下,为函数添加额外的逻辑。无论是记录日志、验证权限还是优化性能,装饰器都能让代码更简洁、更可复用。然而,装饰器的底层原理和最佳实践往往让初学者望而却步。本文将从原理入手,结合实用技巧,帮助开发者快速上手装饰器并在实际项目中灵活运用。
是山河呀
2025/07/16
670
python3 装饰器
1111 test now1() 1505878800.4148097 222 now2() 1505878800.4148097
py3study
2020/01/03
3930
我是装饰器
其实,我并不难理解,而且学会使用我之后,可以让你写代码时偷点懒,少点重复性工作,代码也更优雅,更具有 Pythonic。
somenzz
2020/11/25
3850
(长文收藏) 如何理解 Python 装饰器?
https://www.biaodianfu.com/python-decorator.html 大家好,欢迎来到 Crossin的编程教室 !
Crossin先生
2021/12/01
6850
(长文收藏) 如何理解 Python 装饰器?
Python懒人必备:推荐7个高效实用的装饰器!
对于编程新手来说,Python装饰器可能是一个稍显复杂的概念。简单来说,装饰器是一个函数,它可以接受另一个函数作为参数,并返回一个新的函数(通常是修改后的原始函数的版本)。这个特性使得装饰器在Python中成为一种非常强大且灵活的工具,可以用于在不修改原始函数代码的情况下,为其添加新的功能或修改其行为。常用于统计时间、插入日志、性能度量、权限校验、缓存、事务处理等场景。
测试开发技术
2024/09/10
4280
Python懒人必备:推荐7个高效实用的装饰器!
Python装饰器:优雅增强函数行为的艺术
在Python的世界中,装饰器(Decorator)被誉为"语法糖之王",它以一种优雅而强大的方式改变了我们编写和思考代码的方式。装饰器不仅仅是Python的一个功能特性,更是一种编程哲学的体现——它完美诠释了"开放封闭原则"(对扩展开放,对修改封闭),允许我们在不改变函数原始代码的情况下,增强其功能。
熊猫钓鱼
2025/08/01
1210
python装饰器1:函数装饰器详解
假如你已经定义了一个函数funcA(),在准备定义函数funcB()的时候,如果写成下面的格式:
py3study
2020/01/19
8080
Python进阶——如何实现一个装饰器?
在 Python 开发中,我们经常会看到使用装饰器的场景,例如日志记录、权限校验、本地缓存等等。
_Kaito
2021/03/23
3710
python装饰器学习笔记
装饰器用于修改或扩展函数或方法的行为。装饰器本质上是一个高阶函数,它可以接受一个或者多个函数作为参数,并返回一个新的函数。
薛定谔的馋猫
2024/11/03
1480
Python 装饰器使用指南
装饰器的一大特性是,能把被装饰的函数替换成其他函数。第二大特性是,装饰器在加载模块时立即执行。
goodspeed
2020/12/22
5530
Python 装饰器使用指南
python 3层装饰器及应用场景
    1)把一个函数名当做实参传给另一个函数(在不修改被装饰函数的源代码情况下为其添加功能);
py3study
2020/01/03
5280
相关推荐
Python装饰器是个什么鬼?
更多 >
领券
一站式MCP教程库,解锁AI应用新玩法
涵盖代码开发、场景应用、自动测试全流程,助你从零构建专属AI助手
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验