当前位置: 首页 > news >正文

Python装饰器

什么是python装饰器?

顾名思义,从字面意思就可以理解,它是用来"装饰"Python的工具,使得代码更具有Python简洁的风格。换句话说,它是一种函数的函数,因为装饰器传入的参数就是一个函数,然后通过实现各种功能来对这个函数的功能进行增强。

为什么要使用装饰器?

装饰器是通过某种方式来增强函数的功能。当然,我们可以通过很多方式来增强函数的功能,只是装饰器有一个无法替代的优势--简洁。只需要在每个函数上方加一个@就可以对这个函数进行增强。

装饰器(Decorator)是Python中一个重要部分,它本质上是一个函数,不同于普通函数,装饰器的返回值是一个函数对象。通过利用装饰器,我们可以让其他函数在不做任何代码改动的情况下增加额外的功能,同时也能够让代码更加简洁。

装饰器

装饰器是Python的一种高级函数,它可以接受一个函数作为参数,并返回一个新的函数。通过使用装饰器,我们可以在不修改原函数代码的情况下,为函数添加额外的功能或行为。

基本定义

装饰器的定义格式如下:

def decorator_function(original_function):def wrapper_function(*args, **kwargs):# 在调用原函数之前的额外操作result = original_function(*args, **kwargs)# 在调用原函数之后的额外操作return resultreturn wrapper_function

在上述示例中,decorator_function是装饰器函数,它接受一个原函数original_function作为参数,并返回一个新的函数wrapper_function。wrapper_function内部可以执行一些在调用原函数之前或之后的额外操作。‍

def a_new_decorator(a_func):def wrapTheFunction():print("I am doing some boring work before executing a_func()")a_func()print("I am doing some boring work after executing a_func()")return wrapTheFunctiondef a_function_requiring_decoration():print("I am the function which needs some decoration to remove my foul smell")a_function_requiring_decoration()
#outputs: "I am the function which needs some decoration to remove my foul smell"a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
#now a_function_requiring_decoration is wrapped by wrapTheFunction()a_function_requiring_decoration()
#outputs:I am doing some boring work before executing a_func()
#        I am the function which needs some decoration to remove my foul smell
#        I am doing some boring work after executing a_func()

上面的例子演示了装饰器的工作过程,Python提供了语法糖来简化装饰器,在函数定义时加上@装饰器,在函数被调用时,自动会调用装饰器返回的函数

@a_new_decorator
def a_function_requiring_decoration():"""Hey you! Decorate me!"""print("I am the function which needs some decoration to ""remove my foul smell")a_function_requiring_decoration()
#outputs: I am doing some boring work before executing a_func()
#         I am the function which needs some decoration to remove my foul smell
#         I am doing some boring work after executing a_func()#the @a_new_decorator is just a short way of saying:
a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)

原函数参数

如果被装饰器包装的函数带有参数,需要通过*args, **kwargs来传递参数,*args, **kwargs可以传递任意数量的位置参数和关键字参数。

def do_twice(func):def wrapper_do_twice(*args, **kwargs):func(*args, **kwargs)func(*args, **kwargs)return wrapper_do_twice@do_twice
def say_hello(name):print(f'Hello {name}!')say_hello('Jack’)‘’’
Hello Jack!
Hello Jack!
’‘’

装饰器返回值

装饰器如果要返回原函数的返回值,需要用一个返回变量接收原函数的返回值

def log_foo(func):def wrapper(*args, **kwargs):print(f'func() start...')result = func(*args, **kwargs)print(f'func() end.')return resultreturn wrapper@log_foo
def say_hello(name):print(f'Hello {name}!')say_hello('Jack’)‘’'
func() start...
Hello Jack!
func() end.
‘''

保留原函数信息

函数携带的一些基本信息,例如函数名、函数文档等,我们可以通过func.__name__获取函数名、可以通过func.__doc__获取函数的文档信息,用户也可以通过注解等方式为函数添加元信息。

由于包装后的函数是wrapper,函数名的等信息都修改为wrapper的信息。

def log_foo(func):def wrapper(*args, **kwargs):'''自定义日志装饰器'''print(f'func() start...')result = func(*args, **kwargs)print(f'func() end.')return resultreturn wrapper@log_foo
def say_hello(name):'''say_hello函数'''print(f'Hello {name}!')print(say_hello.__name__) 
print(say_hello.__doc__) 
print(help(say_hello)) ‘’’
wrapper自定义日志装饰器Help on function wrapper in module __main__:wrapper(*args, **kwargs)自定义日志装饰器None
’‘’

为了解决这个问题,使用functools.wraps修饰修饰器,这将保留有关原始功能的信息。

import functools
def log_foo(func):@functools.wraps(func)def wrapper(*args, **kwargs):'''自定义日志装饰器'''print(f'func() start...')result = func(*args, **kwargs)print(f'func() end.')return resultreturn wrapper@log_foo
def say_hello(name):'''say_hello函数'''print(f'Hello {name}!')print(say_hello.__name__)
print(say_hello.__doc__)
print(help(say_hello))‘’’
say_hellosay_hello函数Help on function say_hello in module __main__:say_hello(name)say_hello函数None
’‘’

多个装饰器装饰

可以多个装饰器同时装饰一个函数

def dec_foo1(func):@functools.wraps(func)def wrapper(*args, **kwargs):print(f'--dec_foo1() start--')result = func(*args, **kwargs)print(f'--dec_foo1() end--')return resultreturn wrapperdef dec_foo2(func):@functools.wraps(func)def wrapper(*args, **kwargs):print(f'--dec_foo2() start--')result = func(*args, **kwargs)print(f'--dec_foo2() end--')return resultreturn wrapper@dec_foo1
@dec_foo2
def say_hello(name):'''say_hello函数'''print(f'Hello {name}!')say_hello('Rose’)‘’'
--dec_foo1() start--
--dec_foo2() start--
Hello Rose!
--dec_foo2() end--
--dec_foo1() end--
‘''

装饰器函数包装的顺序是从上往下。

 

带参数的装饰器

能将参数传递给装饰器是很有用的,比如我们可以给@dec_foo1()扩展为@dec_foo1(num_times)

注意代码多内置了一层函数传参(需要嵌套3层函数,前面的都是2层),通过闭包的形式将参数num_times传入内层函数,可以理解为dec_foo1(num_times)返回的函数再来装饰func

def dec_foo1(num_times):def decorator_repeat(func):@functools.wraps(func)def wrapper(*args, **kwargs):print(f'--dec_foo1() start--')for _ in range(num_times):result = func(*args, **kwargs)print(f'--dec_foo1() end--')return resultreturn wrapperreturn decorator_repeat@dec_foo1(4)
def say_hello(name):print(f'Hello {name}!')say_hello('Rose’)‘’'
--dec_foo1() start--
Hello Rose!
Hello Rose!
Hello Rose!
Hello Rose!
--dec_foo1() end--
‘''

装饰器类

前面介绍类对象只要实现了__call__也可以作为函数对象被调用,函数对象也可以用来作为装饰器。

类的装饰器在@声明的时候需要使用cls()来声明,因为这样才能正确的生成一个函数对象,不能简单的使用类名来声明装饰器,那样会出错TypeError

from functools import wrapsclass logit(object):def __init__(self, logfile='out.log'):self.logfile = logfiledef __call__(self, func):@wraps(func)def wrapped_function(*args, **kwargs):log_string = func.__name__ + " was called"print(log_string)# 打开logfile并写入with open(self.logfile, 'a') as opened_file:# 现在将日志打到指定的文件opened_file.write(log_string + '\n')# 现在,发送一个通知self.notify()return func(*args, **kwargs)return wrapped_functiondef notify(self):# logit只打日志,不做别的pass@logit()   #将类实例化
def myfunc1():passmyfunc1()

并且通过函数对象实现装饰器,有一个优势,便是可以提供更多的预置的变量。

给 logit 创建子类,来添加 email 的功能。

class email_logit(logit):'''一个logit的实现版本,可以在函数调用时发送email给管理员'''def __init__(self, email='admin@myproject.com', *args, **kwargs):self.email = emailsuper(email_logit, self).__init__(*args, **kwargs)def notify(self):# 发送一封email到self.email# 这里就不做实现了pass

@email_logit() 将会和 @logit() 产生同样的效果,但是在打日志的基础上,还会多发送一封邮件给管理员。

常见用法

类的装饰器

from dataclasses import dataclass@dataclass
class PlayingCard:rank: strsuit: str

类方法装饰器

类中修饰器的一种用法是装饰类中的函数,有一些常用的python内置的修饰器。

@property

修饰函数可以作为属性使用,与所定义的属性配合使用,这样可以防止属性被修改。

class House:def __init__(self, price):self._price = price@propertydef price(self):return self._price@price.setterdef price(self, new_price):if new_price > 0 and isinstance(new_price, float):self._price = new_priceelse:print("Please enter a valid price")@price.deleterdef price(self):del self._price

按照惯例,在python中当在变量名前加一个下划线时,意味着告诉其他开发人员不应直接在类外访问或者修改改变量。

  • @property 获取属性。
  • @price.setter 属性设定,可以用来限制属性的范围等等。
  • @price.deleter 定义属性的删除,在del house.price时被执行。

@abstractmethod

抽象方法表示基类的一个方法,没有实现,所以基类不能实例化,子类实现了该抽象方法才能被实例化

@classmethod

classmethod声明方法为类方法,直接通过 类或者实例.类方法()调用。经过@classmethod修饰的方法,不需要self参数,但是需要一个标识类本身的cls参数。

class T:@classmethoddef class_test(cls):#必须有cls参数print "i am a class method"
if __name__ == "__main__":T.class_test()T().class_test()

@staticmethoed

声明方法为静态方法,直接通过 类或者实例.静态方法()调用。经过@staticmethod修饰的方法,不需要self参数,其使用方法和直接调用函数一样。

记录状态的装饰器

装饰器可以跟踪函数的状态,如下是一个记录函数调用次数的装饰器,通过函数属性来计数。

import functoolsdef count_calls(func):@functools.wraps(func)def wrapper_count_calls(*args, **kwargs):wrapper_count_calls.num_calls += 1print(f"Call {wrapper_count_calls.num_calls} of {func.__name__!r}")return func(*args, **kwargs)wrapper_count_calls.num_calls = 0return wrapper_count_calls@count_calls
def say_whee():print("Whee!")

记录状态的最好的方式是使用类作为装饰器,只需要实现__init__()__call__()。使用functools.update_wrapper(self, func)保留信息。

import functoolsclass CountCalls:def __init__(self, func):functools.update_wrapper(self, func)self.func = funcself.num_calls = 0def __call__(self, *args, **kwargs):self.num_calls += 1print(f"Call {self.num_calls} of {self.func.__name__!r}")return self.func(*args, **kwargs)@CountCalls
def say_whee():print("Whee!")

@timer

@timer装饰器,记录函数执行的时间。

import functools
import timedef timer(func):"""Print the runtime of the decorated function"""@functools.wraps(func)def wrapper_timer(*args, **kwargs):start_time = time.perf_counter()    # 1value = func(*args, **kwargs)end_time = time.perf_counter()      # 2run_time = end_time - start_time    # 3print(f"Finished {func.__name__!r} in {run_time:.4f} secs")return valuereturn wrapper_timer@timer
def waste_some_time(num_times):for _ in range(num_times):sum([i**2 for i in range(10000)])waste_some_time(999)
#Finished 'waste_some_time' in 1.6487 secs

@debug

@debug装饰器,输出调试信息,帮助程序员分析问题。

import functoolsdef debug(func):"""Print the function signature and return value"""@functools.wraps(func)def wrapper_debug(*args, **kwargs):args_repr = [repr(a) for a in args]                      # 1kwargs_repr = [f"{k}={v!r}" for k, v in kwargs.items()]  # 2signature = ", ".join(args_repr + kwargs_repr)           # 3print(f"Calling {func.__name__}({signature})")value = func(*args, **kwargs)print(f"{func.__name__!r} returned {value!r}")           # 4return valuereturn wrapper_debug@debug
def make_greeting(name, age=None):if age is None:return f"Howdy {name}!"else:return f"Whoa {name}! {age} already, you are growing up!">>> make_greeting("Benjamin")
Calling make_greeting('Benjamin')
'make_greeting' returned 'Howdy Benjamin!'
'Howdy Benjamin!'

授权

装饰器能有助于检查某个人是否被授权去使用一个web应用的端点(endpoint)。它们被大量使用于Flask和Django web框架中。这里是一个例子来使用基于装饰器的授权:

from functools import wrapsdef requires_auth(f):@wraps(f)def decorated(*args, **kwargs):auth = request.authorizationif not auth or not check_auth(auth.username, auth.password):authenticate()return f(*args, **kwargs)return decorated

日志

日志是装饰器运用的另一个亮点

rom functools import wrapsdef logit(func):@wraps(func)def with_logging(*args, **kwargs):print(func.__name__ + " was called")return func(*args, **kwargs)return with_logging@logit
def addition_func(x):"""Do some math."""return x + xresult = addition_func(4)
# Output: addition_func was called

另外一个例子:

import logging
from functools import partial
​
def wrapper_property(obj, func=None):if func is None:return partial(wrapper_property, obj)setattr(obj, func.__name__, func)return func
​
def logger_info(level, name=None, message=None):def decorate(func):logmsg = message if message else func.__name__def wrapper(*args, **kwargs):log.log(level, logmsg)return func(*args, **kwargs)
​@wrapper_property(wrapper)def set_level(newlevel):nonlocal levellevel = newlevel
​@wrapper_property(wrapper)def set_message(newmsg):nonlocal logmsglogmsg = newmsg
​return wrapper
​return decorate
​
​
@logger_info(logging.WARNING)
def main(x, y):return x + ymain(3, 3)
​
# 输出
# WARNING:Test:main
# 6main.set_level(logging.ERROR)
main(5, 5)# 输出
# ERROR:Test:main
# 10

这里面最重要的是wrapper_property这个函数,它的功能是把一个函数func变成一个对象obj的属性,然后通过调用wrapper_property,给装饰器添加了两个属性set_messageset_level,分别用于改变输出日志的内容和改变输出日志的等级。

 

Registering Plugins

装饰器不必包装他们正在装饰的功能,它们还可以简单地注册一个函数的存在,并将其解封返回。如下是创建轻量级插件架构的代码。

import random
PLUGINS = dict()def register(func):"""Register a function as a plug-in"""PLUGINS[func.__name__] = funcreturn func@register
def say_hello(name):return f"Hello {name}"@register
def be_awesome(name):return f"Yo {name}, together we are the awesomest!"def randomly_greet(name):greeter, greeter_func = random.choice(list(PLUGINS.items()))print(f"Using {greeter!r}")return greeter_func(name)

创建单例(Singletons)

单例是一种设计模式,单例模式保证了在程序的不同位置都可以且仅可以取到同一个对象实例。通过保存实例使得每次返回的是同一个对象实例。

import functoolsdef singleton(cls):"""Make a class a Singleton class (only one instance)"""@functools.wraps(cls)def wrapper_singleton(*args, **kwargs):if not wrapper_singleton.instance:wrapper_singleton.instance = cls(*args, **kwargs)return wrapper_singleton.instancewrapper_singleton.instance = Nonereturn wrapper_singleton@singleton
class TheOne:pass

实现缓存和记忆机制

通过记录状态来保存之前的计算结果。

import functools
from decorators import count_callsdef cache(func):"""Keep a cache of previous function calls"""@functools.wraps(func)def wrapper_cache(*args, **kwargs):cache_key = args + tuple(kwargs.items())if cache_key not in wrapper_cache.cache:wrapper_cache.cache[cache_key] = func(*args, **kwargs)return wrapper_cache.cache[cache_key]wrapper_cache.cache = dict()return wrapper_cache@cache
@count_calls
def fibonacci(num):if num < 2:return numreturn fibonacci(num - 1) + fibonacci(num - 2)

添加额外的信息

通过在装饰函数时给函数附加属性来添加信息。

def set_unit(unit):"""Register a unit on a function"""def decorator_set_unit(func):func.unit = unitreturn funcreturn decorator_set_unitimport math@set_unit("cm^3")
def volume(radius, height):return math.pi * radius**2 * height>>> volume(3, 5)
141.3716694115407>>> volume.unit
'cm^3'

相关文章:

Python装饰器

什么是python装饰器&#xff1f; 顾名思义&#xff0c;从字面意思就可以理解&#xff0c;它是用来"装饰"Python的工具&#xff0c;使得代码更具有Python简洁的风格。换句话说&#xff0c;它是一种函数的函数&#xff0c;因为装饰器传入的参数就是一个函数&#xff0…...

【Spring】使用自定义注解方式实现AOP鉴权

AOP&#xff0c;是一种面向切面编程&#xff0c;可以通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。 在软件开发中&#xff0c;鉴权&#xff08;Authentication&#xff09;是一项非常重要的安全措施&#xff0c;用于验证用户身份和权限。在应用程序中&…...

Go几种读取配置文件的方式

比较有名的方案有 使用viper管理配置[1] 支持多种配置文件格式&#xff0c;包括 JSON,TOML,YAML,HECL,envfile&#xff0c;甚至还包括Java properties 支持为配置项设置默认值 可以通过命令行参数覆盖指定的配置项 支持参数别名 viper[2]按照这个优先级&#xff08;从高到低&am…...

每日一题(反转链表)

每日一题&#xff08;反转链表&#xff09; 206. 反转链表 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 可以定义一个新的newhead结构体指针。再定义cur指针和next指针互相配合&#xff0c;将原链表中的节点从头到尾依次头插到newhead链表中&#xff0c;同时更…...

某人事系统架构搭建设计记录

首发博客地址 https://blog.zysicyj.top/ 先大致列一下基础情况 架构必须是微服务 场景上涉及大量查询操作&#xff0c;分析操作 存在临时大量写入的场景 并发并不高 对高可用要求较高&#xff0c;不能挂掉 对安全要求高 要能过等保测试等三方测试 使用人数并不多&#xff0c;十…...

uniapp 实现切换tab锚点定位到指定位置

1.主要使用uniapp scroll-view 组件的scroll-into-view属性实现功能 2.代码如下 <scroll-view:scroll-into-view"intoView"><u-tabsclass"tabs-list"change"tabChange":list"tabList"></u-tabs><view id"1&…...

华纳云:ssh登录22号端口拒绝连接Ubuntu?

如果您在尝试使用SSH登录Ubuntu服务器的时候遇到了22号端口拒绝连接的问题&#xff0c;您可以尝试以下几个步骤来解决问题&#xff1a; 确认SSH服务已启动&#xff1a; 确保Ubuntu服务器上的SSH服务已经正确启动。您可以在服务器上运行以下命令来检查SSH服务的状态&#xff1a…...

python conda实践 sanic框架gitee webhook实践

import subprocess import hmac import hashlib import base64 from sanic.response import text from sanic import Blueprint from git import Repo# 路由蓝图 hook_blue Blueprint(hook_blue)hook_blue.route(/hook/kaifa, methods["POST"]) async def kaifa(req…...

LeetCode——无重复的最长子串(中等)

题目 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度 示例 1: 输入: s "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc"&#xff0c;所以其长度为 3。示例 2: 输入: s "bbbbb" 输出: 1 解释: 因为…...

【SQL】关系模型与查询和更新数据

一、关系模型 1.1 主键 主键是关系表中记录的唯一标识。主键的选取非常重要&#xff1a;主键不要带有业务含义&#xff0c;而应该使用BIGINT自增或者GUID类型。主键也不应该允许NULL。 可以使用多个列作为联合主键&#xff0c;但联合主键并不常用。 1.2 外键 FOREIGN KEY …...

【Centos8_配置单节点伪分布式Spark环境】

安装centos8 jdk部署伪分布式spark环境 安装Centos8 环境下的JDK 下载jdk linux版本 下载链接&#xff1a; jdk-8u381-linux-x64.tar.gz 将该文件上传到Centos8 主机 部署配置jdk&#xff08;java8&#xff09; # 解压到指定路径 [lhangtigerkeen Downloads]$ sudo tar …...

【软考】系统集成项目管理工程师(三)信息系统集成专业技术知识①【16分】

一、系统集成的特点 官方解释&#xff1a; 1、信息系统建设的内容主要包括设备采购、系统集成、软件开发和运维服务等&#xff1b; 2、信息系统集成是指将计算机软件、硬件、网络通信、信息安全等技术和产品集成为能够满足用户特定需求的信息系统&#xff1b;显著特点如下&am…...

揭秘特权账号潜在风险,你中招了吗?

什么是特权账号&#xff1f; 特权账号指在企业运营过程中&#xff0c;为相关业务运营、系统管理、系统运维等人员赋予的系统维护、权限增加、数据修改删除、导出等高级权限的系统账号。这些账号多数连接企业核心资源&#xff0c;保障企业内部各项业务正常运作。然而&#xff0…...

线性代数的学习和整理13: 定义域,值域,到达域 和单射,满射,双射,反函数,逆矩阵

目录 1 函数与 向量/矩阵 2 初等数学的函数 2.1 函数 2.2 函数的定义&#xff1a;定义域 →映射→ 值域 3 高等数学里的函数&#xff1a;定义域和陪域/到达域&#xff08;非值域&#xff09;的映射关系 3.1 函数 3.2 单射&#xff0c;满射&#xff0c;双射等都是针对…...

深入MaxCompute -第十一弹 -QUALIFY

简介&#xff1a; MaxCompute支持QUALIFY语法过滤Window函数的结果&#xff0c;使得查询语句更简洁易理解。Window函数和QUALIFY语法之间的关系可以类比聚合函数GROUP BY语法和HAVING语法。 MaxCompute&#xff08;原ODPS&#xff09;是阿里云自主研发的具有业界领先水平的分…...

Mysql定时备份事件

创建了一个名为backup_database的定时任务&#xff0c;每天自动在当前时间的后一天开始执行。备份数据库的代码使用mysqldump命令将数据库导出为sql文件保存在指定的备份目录中。 需要注意的是&#xff0c;上述代码中的用户名 (username)、密码 (password)、主机名 (hostname) …...

探索ClickHouse——安装和测试

我们在Ubuntu 20 Server版虚拟机上对ClickHouse进行探索。 安装 检测环境 grep -q sse4_2 /proc/cpuinfo && echo "SSE 4.2 supported" || echo "SSE 4.2 not supported"SSE 4.2 supported 可以看到我们的环境支持编译版本的。如果不支持的环境…...

常用的css样式

1&#xff1a;flex布局 .flex-between {display: flex;justify-content: space-between; }.flex-evenly {display: flex;justify-content: space-evenly; }.flex-end {display: flex;justify-content: flex-end; }.flex {display: flex; }.flex-center {display: flex;justify…...

小兔鲜儿 - 微信登录

目录 微信登录​ 登录方式 静态结构​ 获取登录凭证​ 获取手机号码​ 微信登录接口(生产环境) 模拟手机登录(开发环境) 用户信息持久化存储​ 涉及知识点&#xff1a;微信授权登录&#xff0c;文件上传&#xff0c;Store 状态管理等。 微信登录​ 微信小程序的开放…...

C++ Primer阅读笔记--对象移动(右值引用、移动迭代器和引用限定符的使用)

目录 1--右值引用 2--std::move 3--移动构造函数 4--移动赋值运算符 5--移动迭代器 6--引用限定符 1--右值引用 右值引用必须绑定到右值的引用&#xff0c;通过 && 获得右值引用&#xff1b; 右值引用只能绑定到临时对象&#xff08;即将被销毁的对象&#xff09…...

后进先出(LIFO)详解

LIFO 是 Last In, First Out 的缩写&#xff0c;中文译为后进先出。这是一种数据结构的工作原则&#xff0c;类似于一摞盘子或一叠书本&#xff1a; 最后放进去的元素最先出来 -想象往筒状容器里放盘子&#xff1a; &#xff08;1&#xff09;你放进的最后一个盘子&#xff08…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

DockerHub与私有镜像仓库在容器化中的应用与管理

哈喽&#xff0c;大家好&#xff0c;我是左手python&#xff01; Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库&#xff0c;用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

聊聊 Pulsar:Producer 源码解析

一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台&#xff0c;以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中&#xff0c;Producer&#xff08;生产者&#xff09; 是连接客户端应用与消息队列的第一步。生产者…...

条件运算符

C中的三目运算符&#xff08;也称条件运算符&#xff0c;英文&#xff1a;ternary operator&#xff09;是一种简洁的条件选择语句&#xff0c;语法如下&#xff1a; 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true&#xff0c;则整个表达式的结果为“表达式1”…...

srs linux

下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935&#xff0c;SRS管理页面端口是8080&#xff0c;可…...

ETLCloud可能遇到的问题有哪些?常见坑位解析

数据集成平台ETLCloud&#xff0c;主要用于支持数据的抽取&#xff08;Extract&#xff09;、转换&#xff08;Transform&#xff09;和加载&#xff08;Load&#xff09;过程。提供了一个简洁直观的界面&#xff0c;以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

ElasticSearch搜索引擎之倒排索引及其底层算法

文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

【Java_EE】Spring MVC

目录 Spring Web MVC ​编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 ​编辑参数重命名 RequestParam ​编辑​编辑传递集合 RequestParam 传递JSON数据 ​编辑RequestBody ​…...

聊一聊接口测试的意义有哪些?

目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开&#xff0c;首…...