【Python】基本语法:装饰器语法糖@

1、什么是装饰器?

装饰器就是:动态地给一个现有对象或函数附加额外功能,同时又不修改原有类或函数的源代码、不通过继承扩展子类。

和继承的区别:

  • 继承是静态绑定:编译期就确定好了功能,运行时无法随时增删功能;
  • 装饰器模式:运行时动态给对象叠加 / 移除功能;
  • 如果依靠类继承扩展功能:每增加一种功能就要新建一个子类,功能组合一多,子类会爆炸式增多

2、Python装饰器

Python中的装饰器:一般是用于包装函数,函数式编程思想;

Python使用@符号,标记装饰器,是 Python 语法糖,在不修改原函数代码的前提下,给函数增加额外功能,比如:异常捕获、计时、日志、权限校验等。

2.1 异常捕获+日志

异常捕获装饰器:给被修饰的函数,添加捕获异常功能,并将异常记录到日志中;

防止因为没有处理被装饰的函数的异常,而导致崩溃;

python 复制代码
def safe_wrapper(logger=None , default = None):
    def outer(func):
        def inner(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except Exception as e:
                if logger : logger.exception(f"{func.__doc__}失败 : {e}")
                return default
        return inner
    return outer

1)第一层safe_wrapper:接收两个参数:日志对象、出错要返回的默认值;

2)第二层outer:标准装饰器入口,接收要被包装的函数 func;

3)第三层inner:真正替换原函数的内层函数,原函数所有参数传递给 *args **kwargs;

4)第四层 try-except:将原函数包装到异常处理中;

5)第五层func:执行原函数,返回执行结果;

用法:

python 复制代码
import logging
logging.basicConfig(level=logging.INFO)
log = logging.getLogger()

@safe_wrapper(logger=log, default=None)
def div(a, b):
    return a / b

print(div(10, 0))

捕获除零异常,不会崩溃,返回None,并打印异常日志

2.2 不使用语法糖

如果不使用Python的语法糖@,等同于使用下面的方式执行:div(10, 0)

python 复制代码
def div(a, b):
    return a / b
    
outer = safe_wrapper(logger=log, default=None)
inner = outer(div)
inner(10, 0)

2.3 统计函数运行时间

python 复制代码
import time

def timer(func):
    def wrap(*args, **kwargs):
        start = time.time()
        res = func(*args, **kwargs)
        print(f"耗时:{time.time()-start:.2f}s")
        return res
    return wrap

@timer
def calc():
    sum(range(1000000))

calc()

3、内置装饰器

3.1 @property 函数当变量

把类的函数伪装成实例变量(属性):

  • 调用函数使用:class.fun();
  • 伪装成变量后:class.fun;
  • 默认不能赋值,添加setter修饰后,可以赋值;
python 复制代码
class Person:
    def __init__(self, birth_year):
        self.birth_year = birth_year
        self._age=0
        self._set=False
    @property
    def age(self):
        if(self._set):
            return self._age
        else:
            return 2026 - self.birth_year
    @age.setter
    def age(self, val):
          self._age = val
          self._set = True

3.2 @staticmethod 静态函数

和C++一样:静态函数属于类,不用实例化类也可以直接调用的函数;

注意:静态函数中,不能调用非静态的函数和变量(实例属性);但是可以访问类属性

python 复制代码
class Tools:
    @staticmethod
    def add(a, b):
        return a + b

调用时,直接用类调用,不用创建对象也能调用

复制代码
print(Tools.add(1, 2))

3.3 @classmethod 类函数

类函数 和 静态函数类似;区别如下:

  • 类函数支持继承重写,静态函数不能;
  • 类函数的第一个参数表示类本身,子类调用时,该参数自动变成子类;
python 复制代码
class Tool:
    PI = 3.14159
    @staticmethod
    def static_pi():
        return Tool.PI
    @classmethod
    def class_pi(cls):
        return cls.PI

class NewTool(Tool):
    PI = 3.14

print(NewTool.static_pi())

输出,依然是父类的Tool值,static_pi中写死了 Tool.PI:

复制代码
3.14159  

print(NewTool.class_pi())

输出,是子类NewTool自己的PI:

复制代码
3.14     
  • 静态函数:函数逻辑完全独立,既不调用类变量,也不调用实例变量,仅仅是把函数归类放到类中管理
  • 类函数:需要调用类属性,例如:常用于工厂方法模式中,根据不同参数,创建类的实例

4、类属性和实例属性

C++中的成员变量,在Python中称为属性

类属性(类似C++中,静态成员变量)

  • 写在 class 内部、__init__ 外面,属于整个类本;
  • 所有实例共享同一份类属性;
  • 可以通过类直接访问,也可以通过实例对象来访问;

实例属性(类似C++中,非静态成员变量)

  • 在构造方法 __init__(self) 里通过 self.xxx 定义,只属于每一个单独的对象实例;
  • 每个对象互相独立,修改互不影响;
  • 只能通过实例对象访问,类无法直接访问;

可以通过 __dict__,来打印所有的属性字典

python 复制代码
class Student:
    school = "河北石油大学"
    def __init__(self, name, age):
        self.name = name
        self.age = age 
        
laoer = Student("laoer", 28)
print(laoer.__dict__)
print(Student.__dict__)
相关推荐
_Jimmy_2 小时前
Agent常用检索器的详细介绍
python·langchain
小柯南敲键盘3 小时前
图片翻译API接入与自动化实现指南
运维·python·自动化
旅僧3 小时前
Q-learning(自用)
python·机器学习
Python大数据分析@3 小时前
曝梁文锋称「一度不想维护C端用户,奈何赶不走」,DeepSeek真不需要C端用户吗?可能会有啥影响吗?
python
艾斯特_4 小时前
工作流与多Agent协作:LangGraph、MCP和A2A的应用分层
人工智能·python·ai
IT小盘5 小时前
04-大模型流式输出原理-SSE与Python实现
开发语言·网络·人工智能·python
我叫黑大帅5 小时前
add()和 __add__() 写法哪个更好呢?
后端·python·面试
不如语冰5 小时前
AI大模型入门-Python进阶-上下文管理与with语句
开发语言·数据结构·数据库·人工智能·pytorch·redis·python
柠檬味的Cat5 小时前
GEO优化系统哪个渠道商好
大数据·人工智能·python