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_message和set_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装饰器? 顾名思义,从字面意思就可以理解,它是用来"装饰"Python的工具,使得代码更具有Python简洁的风格。换句话说,它是一种函数的函数,因为装饰器传入的参数就是一个函数࿰…...
【Spring】使用自定义注解方式实现AOP鉴权
AOP,是一种面向切面编程,可以通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。 在软件开发中,鉴权(Authentication)是一项非常重要的安全措施,用于验证用户身份和权限。在应用程序中&…...

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

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

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

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号端口拒绝连接的问题,您可以尝试以下几个步骤来解决问题: 确认SSH服务已启动: 确保Ubuntu服务器上的SSH服务已经正确启动。您可以在服务器上运行以下命令来检查SSH服务的状态:…...

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 ,请你找出其中不含有重复字符的 最长子串 的长度 示例 1: 输入: s "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。示例 2: 输入: s "bbbbb" 输出: 1 解释: 因为…...

【SQL】关系模型与查询和更新数据
一、关系模型 1.1 主键 主键是关系表中记录的唯一标识。主键的选取非常重要:主键不要带有业务含义,而应该使用BIGINT自增或者GUID类型。主键也不应该允许NULL。 可以使用多个列作为联合主键,但联合主键并不常用。 1.2 外键 FOREIGN KEY …...

【Centos8_配置单节点伪分布式Spark环境】
安装centos8 jdk部署伪分布式spark环境 安装Centos8 环境下的JDK 下载jdk linux版本 下载链接: jdk-8u381-linux-x64.tar.gz 将该文件上传到Centos8 主机 部署配置jdk(java8) # 解压到指定路径 [lhangtigerkeen Downloads]$ sudo tar …...

【软考】系统集成项目管理工程师(三)信息系统集成专业技术知识①【16分】
一、系统集成的特点 官方解释: 1、信息系统建设的内容主要包括设备采购、系统集成、软件开发和运维服务等; 2、信息系统集成是指将计算机软件、硬件、网络通信、信息安全等技术和产品集成为能够满足用户特定需求的信息系统;显著特点如下&am…...
揭秘特权账号潜在风险,你中招了吗?
什么是特权账号? 特权账号指在企业运营过程中,为相关业务运营、系统管理、系统运维等人员赋予的系统维护、权限增加、数据修改删除、导出等高级权限的系统账号。这些账号多数连接企业核心资源,保障企业内部各项业务正常运作。然而࿰…...

线性代数的学习和整理13: 定义域,值域,到达域 和单射,满射,双射,反函数,逆矩阵
目录 1 函数与 向量/矩阵 2 初等数学的函数 2.1 函数 2.2 函数的定义:定义域 →映射→ 值域 3 高等数学里的函数:定义域和陪域/到达域(非值域)的映射关系 3.1 函数 3.2 单射,满射,双射等都是针对…...

深入MaxCompute -第十一弹 -QUALIFY
简介: MaxCompute支持QUALIFY语法过滤Window函数的结果,使得查询语句更简洁易理解。Window函数和QUALIFY语法之间的关系可以类比聚合函数GROUP BY语法和HAVING语法。 MaxCompute(原ODPS)是阿里云自主研发的具有业界领先水平的分…...
Mysql定时备份事件
创建了一个名为backup_database的定时任务,每天自动在当前时间的后一天开始执行。备份数据库的代码使用mysqldump命令将数据库导出为sql文件保存在指定的备份目录中。 需要注意的是,上述代码中的用户名 (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: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…...

小兔鲜儿 - 微信登录
目录 微信登录 登录方式 静态结构 获取登录凭证 获取手机号码 微信登录接口(生产环境) 模拟手机登录(开发环境) 用户信息持久化存储 涉及知识点:微信授权登录,文件上传,Store 状态管理等。 微信登录 微信小程序的开放…...
C++ Primer阅读笔记--对象移动(右值引用、移动迭代器和引用限定符的使用)
目录 1--右值引用 2--std::move 3--移动构造函数 4--移动赋值运算符 5--移动迭代器 6--引用限定符 1--右值引用 右值引用必须绑定到右值的引用,通过 && 获得右值引用; 右值引用只能绑定到临时对象(即将被销毁的对象)…...

React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
安卓基础(Java 和 Gradle 版本)
1. 设置项目的 JDK 版本 方法1:通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分,设置 Gradle JDK 方法2:通过 Settings File → Settings... (或 CtrlAltS)…...
书籍“之“字形打印矩阵(8)0609
题目 给定一个矩阵matrix,按照"之"字形的方式打印这个矩阵,例如: 1 2 3 4 5 6 7 8 9 10 11 12 ”之“字形打印的结果为:1,…...
【HarmonyOS 5】鸿蒙中Stage模型与FA模型详解
一、前言 在HarmonyOS 5的应用开发模型中,featureAbility是旧版FA模型(Feature Ability)的用法,Stage模型已采用全新的应用架构,推荐使用组件化的上下文获取方式,而非依赖featureAbility。 FA大概是API7之…...

java高级——高阶函数、如何定义一个函数式接口类似stream流的filter
java高级——高阶函数、stream流 前情提要文章介绍一、函数伊始1.1 合格的函数1.2 有形的函数2. 函数对象2.1 函数对象——行为参数化2.2 函数对象——延迟执行 二、 函数编程语法1. 函数对象表现形式1.1 Lambda表达式1.2 方法引用(Math::max) 2 函数接口…...