day17:一文弄懂“无参装饰器”、“有参装饰器”和“叠加装饰器”

news2024/9/20 23:05:23

目录

  • 一、无参装饰器
    • 1. 什么是装饰器
    • 2. 为何要用装饰器
    • 3. 如何用
      • 解决方案一:失败,优化见↓↓↓
      • 解决方案二:失败,优化见↓↓↓
      • 解决方案三:失败,优化见↓↓↓
        • 方案三的优化一:将index的参数写活了
        • 方案三的优化二:在优化一的基础上把被装饰对象写活了
        • 方案三的优化三:将wrapper做的跟被装饰对象一模一样,以假乱真
    • 4. 语法糖:让你开心的语法
    • 5. 总结无参装饰器模板
    • 6. 叠加多个装饰器,加载顺序与运行顺序
    • 7. 案例讲解:@语法糖底层逻辑
    • 8.wraps装饰器介绍
    • 9. 思考:为什么会有两次时间计算?
  • 二、有参装饰器
    • 1. 知识储备
    • 2.不使用有参装饰器的山炮玩法 <案例1>
    • 3.使用有参装饰器的山炮玩法 <案例2>
    • 3.使用有参装饰器的最好玩法 <案例3>
    • 4.有参装饰器模板
  • 三、叠加装饰器
    • ↑↑↑通过案例引出结论,结论见上方↑↑↑

一、无参装饰器

1. 什么是装饰器

装饰:指的是为其他事物添加额外的东西点缀
器:指的是工具,可以定义成成函数

合到一起的解释:
装饰器指的定义一个函数,该函数是用来为其他函数添加额外的功能

2. 为何要用装饰器

开放封闭原则
开放:指的是对拓展功能是开放的(新增功能)
封闭:指的是对修改源代码是封闭的

<装饰器>:就是在不修改被装饰器对象源代码以及调用方式的前提下为被装饰对象添加新功能

3. 如何用

需求:在不修改index函数的源代码以及调用方式的前提下为其添加统计运行时间的功能

'''案例:思考如何为该函数添加统计时间的功能'''

def index(x,y):
    print('index函数打印:%s %s'%(x, y))

index(11, 22)
index(11, y=22)
index(x=11, y=22)
index(y=22, x=22)

解决方案一:失败,优化见↓↓↓

问题:没有修改被装饰对象的调用方式,但是修改了其源代码

import time

def index(x,y):
    start_time = time.time()
    time.sleep(1)
    print('index函数打印:%s %s'%(x, y))
    stop_time = time.time()
    print('函数index运行时间为:{}'.format(stop_time-start_time))

index(111,222)

解决方案二:失败,优化见↓↓↓

问题:没有修改被装饰对象的调用方式,也没有修改了其源代码,并且加上了新功能
但是代码冗余,index被调用的位置有很多,每个位置都上下夹击的写

import time

def index(x,y):
    print('index函数打印:%s %s'%(x, y))

start_time = time.time()
index(11,22)
stop_time = time.time()
print(f'函数index运行时间为{stop_time-start_time}')

start_time = time.time()
index(333,444)
stop_time = time.time()
print(f'函数index运行时间为{stop_time-start_time}')

解决方案三:失败,优化见↓↓↓

问题:解决了方案二代码冗余问题,但带来一个新问题即函数的调用方式改变了

import time

def index(x,y):
    time.sleep(1)
    print('index函数打印:%s %s'%(x, y))

def wrapper():
    start_time = time.time()
    index(111, 222)  # 这里参数写死了
    stop_time = time.time()
    print(f'函数index运行时间为{stop_time-start_time}')

wrapper()
方案三的优化一:将index的参数写活了
import time

def index(x,y):
    time.sleep(1)
    print('index函数打印:%s %s'%(x, y))

def wrapper(*args,**kwargs): # 接收参数,整合
    start_time = time.time()
    index(*args,**kwargs)  # 打散
    stop_time = time.time()
    print(f'函数index运行时间为{stop_time-start_time}')

wrapper(111,222)
wrapper(333,444)

在这里插入图片描述

方案三的优化二:在优化一的基础上把被装饰对象写活了

原来wrapper里面函数index名写死了,只能装饰index,其实可将函数名当做参数传给wrapper

import time

def index(x,y):
    time.sleep(1)
    print('index函数打印:%s %s'%(x, y))

def outter(func):
    def wrapper(*args,**kwargs): # 接收参数,整合
        start_time = time.time()
        func(*args,**kwargs)  # 打散
        stop_time = time.time()
        print(f'函数index运行时间为{stop_time-start_time}')
    return wrapper  # 返回wrapper函数的内存地址

# fff = outter(index) # 1、outter()返回的是wrapper函数内存地址,加()表示调用函数wrapper
                      # 2、其中fff可以自定义,也可定义成index;
# fff(111,222)
index = outter(index) 
index(111,222)

在这里插入图片描述

方案三的优化三:将wrapper做的跟被装饰对象一模一样,以假乱真

①如果需要获取装饰的函数中有返回值,怎么办?使用res接收。

import time

def index(x,y):
    time.sleep(1)
    print('index函数打印:%s %s'%(x, y))

def home(name):
    time.sleep(1)
    print('welcome %s to home page' %name)
    return 123

def outter(func):
    def wrapper(*args,**kwargs): # 接收参数,整合
        start_time = time.time()
        res = func(*args,**kwargs)  # 先打散,再执行func函数,最后将返回值给res
        stop_time = time.time()
        print(f'函数index运行时间为{stop_time-start_time}')
        return res
    return wrapper 

home = outter(home)  # 偷梁换柱:home这个名字指向的wrapper函数的内存地址
res = home('linhui')
print('res==》', res)

在这里插入图片描述

4. 语法糖:让你开心的语法

【注】:需要在定义函数前先定义好装饰器,如果调用的函数在装饰器前面,运行程序时会提示名为’xxx’的装饰器未定义

import time

# 装饰器
def timmer(func):
    def wrapper(*args,**kwargs):
        start=time.time()
        res=func(*args,**kwargs)
        stop=time.time()
        print(stop - start)
        return res
    return wrapper

# # 在被装饰对象正上方的单独一行写@装饰器名字
@timmer  # index=timmer(index)
def index(x,y,z):
    time.sleep(1)
    print('index %s %s %s' %(x,y,z))

@timmer # home=timmer(ome)
def home(name):
    time.sleep(1)
    print('welcome %s to home page' %name)

index(x=1,y=2,z=3)
home('大飞飞')

5. 总结无参装饰器模板

def outter(func):
    def wrapper(*args,**kwargs):
        # 1、调用原函数
        # 2、为其增加新功能
        res=func(*args,**kwargs)
        return res
    return wrapper

6. 叠加多个装饰器,加载顺序与运行顺序

@deco1  # index=deco1(deco2.wrapper的内存地址)
@deco2  # deco2.wrapper的内存地址=deco2(deco3.wrapper的内存地址)
@deco3  # deco3.wrapper的内存地址=deco3(index)
def index():
    pass

7. 案例讲解:@语法糖底层逻辑

注:为有参装饰器做知识储备

【案例1说明】:@print==>等价于index = print(index),即print(index)打印的是index的函数内存地址

'''案例1:举例@print作为装饰器'''

@print    # @print==>等价于index = print(index)
def index(x,y):
    print(1111)
    pass

输出:
<function index at 0x7f2d6e3f01e0>

【案例2说明】:@print==>index = print(index),先打印index内存地址,然后将print(index)函数运行后的返回值赋给了index,这里的print()装饰器本身没有返回值,所以print(index)=None

'''案例2:举例@print作为装饰器'''

@print
def index(x,y):
    print(1111)
    pass

print(index)

输出:
function index at 0x7fa2763151e0>
None

【案例3说明】:
这里的@print(‘hello’) 等价于:
第一步:先运行print(hello),输出hello
第二步:print(hello)的返回结果为None
第三步:@print(‘hello’) 等价于 @None
第四步:@None 等价于 index=None(index) ,故最终执行报错

'''案例3:举例@print作为装饰器'''
@print('hello')
def index(x,y):
    print(1111)
    pass

hello
Traceback (most recent call last):
File “/home/xionglinhui/my_python_project/temp.py”, line 15, in
@print(‘hello’)
TypeError: ‘NoneType’ object is not callable

8.wraps装饰器介绍

举例说明:列举不调用装饰器和调用装饰器时函数index的属性区别,通过案例1和案例2引出进一步被装饰对象在被装饰器调用前和调用后的区别,从而优化下装饰器代码,最终目的是将wrapper函数做的和原函数一模一样。

''' 案例1 没有调用装饰器,查看原函数的函数名和功能说明 '''


def outter(func):
    def wrapper(*args, **kwargs):
        res = func(*args, **kwargs)
        return res

    return wrapper

def index(x, y):
    '''我是index,我的功能是打印参数'''
    print(f'打印index的x,y ==> {x} {y}')
    return '我是index返回值'

# 注释掉@outter装饰器,查看index函数地址和函数功能说明
index = outter(index)
print(index)  # 输出:<function outter.<locals>.wrapper at 0x00000263568EF910>
# 翻译即是函数outter的局部函数wrapper的内存地址
index(666, 999)  # 打印index的x,y ==> 666 999

print(index.__name__)  # 获取函数index的名字,即index
print(index.__doc__)  # 获取函数index的功能说明,即'''xxx'''里面的内容,即“我是index,我的功能是打印参数”
''' 案例2 调用装饰器,查看原函数的函数名和功能说明 '''
def outter(func):
    def wrapper(*args, **kwargs):
        res = func(*args, **kwargs)
        return res
    return wrapper

@outter
def index(x, y):
    '''我是index,我的功能是打印参数'''
    print(f'打印index的x,y ==> {x} {y}')
    return '我是index返回值'

#调用装饰器@outter后,查看index函数地址和函数功能说明
print(index)  # <function outter.<locals>.wrapper at 0x0000022FC505F880>
print(index.__name__)   # wrapper
print(index.__doc__)   # None

分析

  1. index(或定义为xxx) = outter(index)相当于把wrapper函数的内存地址赋给index,这里的index可以是任意变量;
  2. 这里的index实际上是wrapper,当我们查看index.__name__函数名和index.__doc__功能说明时,实际上查看的时wrapper的功能说明,与我们的偷梁换柱目的还差一点。

解决思路:优化方案请见《案例3》

  1. index调用outter装饰器后,index.__name__值为wrapper,
    而index调用outter装饰器前,index.__name__值为index,
    所以在调用装饰器前,即 定义阶段 需要将原函数index.__name____的名字传给wrapper.__name__名字,即:见下图
wrapper.__name__ = func.__name__   #函数名
wrapper.__doc__ = func.__doc__     #函数功能说明,即注释
''' 案例3 将index函数的属性值赋给wrapper,即例如wrapper.__name__ = func.__name__'''

def outter(func):
    def wrapper(*args, **kwargs):
        res = func(*args, **kwargs)
        return res
    '''我是wrapper函数内的'''
    #模板:
    #函数wrapper.__name__  = 被装饰对象(原函数).__name__
    #函数wrapper.__doc__  = 被装饰对象(原函数).__doc__
    wrapper.__name__ = func.__name__  ##代码优化部分
    wrapper.__doc__ = func.__doc__
    return wrapper

@outter
def index(x, y):
    '''我是index,我的功能ABCDEFG是打印参数'''
    print(f'打印index的x,y ==> {x} {y}')
    return '我是index返回值'

#优化一:将index函数的属性值赋给wrapper
print(index)  # <function outter.<locals>.wrapper at 0x0000022FC505F880>
print(index.__name__)   # index
print(index.__doc__)   # 我是index,我的功能ABCDEFG是打印参数

存在的问题和解决方法
1.问题:函数的属性有很多,见下图,如果将每个属性都传给wrapper,那么代码会过于冗余。
2.解决方法:解释器考虑到这一点,特定封装了wraps装饰器用于解决该问题,该函数封装在functools,调用方式为from functools import wraps,见**《案例4》**
在这里插入图片描述

''' 案例4 使用wraps内置装饰器,优化代码'''

from functools import wraps

def outter(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        '''我是wrapper函数内的'''
        res = func(*args, **kwargs)
        return res
    return wrapper

@outter
def index(x, y):
    '''我是index,我的功能ABCDEFG是打印参数'''
    print(f'打印index的x,y ==> {x} {y}')
    return '我是index返回值'

#优化二:使用wraps内置装饰器,优化代码
print(index)  # <function index at 0x000001F86E5C65F0>
print(index.__name__)   # index
print(index.__doc__)   # 我是index,我的功能ABCDEFG是打印参数

9. 思考:为什么会有两次时间计算?

1.要求:详细说明代码执行流程
==========================
2.分析过程: print(outter(index)(11,22))
①outter(index) ==> 注意哈,这里的index就是被装饰对象index函数的内存地址 ;outter(index) ==>等价于index = wrapper函数的内存地址
②wrapper(11,22) ==>执行wrapper函数,当遇到func(当前的func就是被装饰对象index)时代码会跳转到index()函数,识别到了装饰器@outter,此时会优先执行@outter,@outter等价于outter(被装饰对象index),先去执行一遍wrapper函数且执行index函数内部代码
③跳过装饰器,继续执行步骤2中的wrapper(11,22)

'''案例:存在装饰器却不直接使用装饰器,分析输出结果!!'''

import time

def outter(func):
    def wrapper(*args,**kwargs):
        start = time.time()
        res = func(*args,**kwargs)
        end = time.time()
        print(f'代码运行时间:{end-start}')
        return res
    return wrapper

@outter  # index=outter(index)=wrapper函数内存地址
def index(x, y):
    time.sleep(1)
    print(f'打印index的输出:{x} {y}')
    return 12345678

print(outter(index)(11,22))

在这里插入图片描述

二、有参装饰器

1. 知识储备

① 由于语法糖@的限制,outter函数只能有一个参数,并且该参数只用来接收被装饰对象的内存地址

② 案例需求:实现一个用来为被装饰对象添加认证功能的装饰器。认证成功后,如果文件类型为file,则执行func;如果文件类型为mysql,则打印’基于mysql的验证’;;如果文件类型为ldap,则打印’基于ldap的验证’。

def outter(func):
    # func = 函数的内存地址
    def wrapper(*args,**kwargs):
        res=func(*args,**kwargs)
        return res
    return wrapper

# @outter # index=outter(index) # index=>wrapper
@outter # outter(index)
def index(x,y):
    print(x,y)

偷梁换柱之后,我们的正真目的是:↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
1.index的参数什么样子,wrapper的参数就应该什么样子
2.index的返回值什么样子,wrapper的返回值就应该什么样子
3.index的属性什么样子,wrapper的属性就应该什么样子==》from functools import wraps

2.不使用有参装饰器的山炮玩法 <案例1>

'''案例1:纯函数的调用'''
def auth(func,db_type):
    def wrapper(*args, **kwargs):
        name=input('your name>>>: ').strip()
        pwd=input('your password>>>: ').strip()

        if db_type == 'file':
            print('基于文件的验证')
            if name == 'dafeifei' and pwd == '123':
                res = func(*args, **kwargs)
                return res
            else:
                print('user or password error')
        elif db_type == 'mysql':
            print('基于mysql的验证')
            res = func(*args, **kwargs)
            return res

        elif db_type == 'ldap':
            print('基于ldap的验证')
        else:
            print('不支持该db_type')

    return wrapper

# @auth  # 账号密码的来源是文件
def index(x,y):
    print('index->>%s:%s' %(x,y))

# @auth # 账号密码的来源是数据库
def home(name):
    print('home->>%s' %name)

# @auth # 账号密码的来源是ldap
def transfer():
    print('transfer')
    
# index=auth(index,'file')
# index(11,22)
home=auth(home,'mysql')
home('dafeifei')
# transfer=auth(transfer,'ldap')
# transfer()

3.使用有参装饰器的山炮玩法 <案例2>

'''案例2:deco=auth(db_type='file'),'''
def auth(db_type):
    def deco(func):
        def wrapper(*args, **kwargs):
            name=input('your name>>>: ').strip()
            pwd=input('your password>>>: ').strip()

            if db_type == 'file':
                print('基于文件的验证')
                if name == 'egon' and pwd == '123':
                    res = func(*args, **kwargs)
                    return res
                else:
                    print('user or password error')
            elif db_type == 'mysql':
                print('基于mysql的验证')
            elif db_type == 'ldap':
                print('基于ldap的验证')
            else:
                print('不支持该db_type')

        return wrapper
    return deco

deco=auth(db_type='file')
@deco # 账号密码的来源是文件
def index(x,y):
    print('index->>%s:%s' %(x,y))

deco=auth(db_type='mysql')
@deco # 账号密码的来源是数据库
def home(name):
    print('home->>%s' %name)

deco=auth(db_type='ldap')
@deco # 账号密码的来源是ldap
def transfer():
    print('transfer')


index(1,2)
# home('egon')
# transfer()

3.使用有参装饰器的最好玩法 <案例3>

【解释】:@auth(db_type=‘file’) ,见到()表示函数调用,先忽略@
第一步:auth(db_type=‘file’) ==>调用auth()返回的是deco函数的内存地址,即 index = deco的内存地址 ,得到了@deco,包含了对外部作用域名字db_type的引用,@deco的语法意义与无参装饰器一样;
第二步:@deco ==> 等价于index = deco(index),调用deco(index)返回的是wrapper函数的内存地址,即index = wrapper函数地址

'''案例3:语法糖'''

def auth(db_type):
    def deco(func):
        def wrapper(*args, **kwargs):
            name = input('your name>>>: ').strip()
            pwd = input('your password>>>: ').strip()

            if db_type == 'file':
                print('基于文件的验证')
                if name == 'egon' and pwd == '123':
                    res = func(*args, **kwargs)  # index(1,2)
                    return res
                else:
                    print('user or password error')
            elif db_type == 'mysql':
                print('基于mysql的验证')
            elif db_type == 'ldap':
                print('基于ldap的验证')
            else:
                print('不支持该db_type')
        return wrapper
    return deco


@auth(db_type='file')  # @deco # index=deco(index) # index=wrapper
def index(x, y):
    print('index->>%s:%s' % (x, y))

@auth(db_type='mysql')  # @deco # home=deco(home) # home=wrapper
def home(name):
    print('home->>%s' % name)


@auth(db_type='ldap')  # 账号密码的来源是ldap
def transfer():
    print('transfer')

index(1, 2)
# home('egon')
# transfer()

4.有参装饰器模板

'''******有参装饰器模板******'''

def 有参装饰器(x,y,z):
    def outter(func):
        def wrapper(*args, **kwargs):
            res = func(*args, **kwargs)
            return res
        return wrapper
    return outter

@有参装饰器(1,y=2,z=3)
def 被装饰对象():
    pass

三、叠加装饰器

记住结论!记住结论!记住结论!
【叠加装饰器的加载顺序】:自下而上(了解)
【叠加装饰器的执行顺序】:自上而下(重点),最下面那个装饰器调用的才是原函数

↑↑↑通过案例引出结论,结论见上方↑↑↑

'''案例:通过案例详细了解下叠加装饰器的加载和执行逻辑,最后我们只需记住结论即可'''

def deco1(func1): #func1 = wrapper2的内存地址
    def wrapper1(*args, **kwargs):
        print('正在运行===>deco1.wrapper1')
        res1 = func1(*args, **kwargs)
        print('已结束===>deco1.wrapper1')
        return res1
    return wrapper1

def deco2(func2): # func2 = wrapper3的内存地址
    def wrapper2(*args, **kwargs):
        print('正在运行===>deco2.wrapper2')
        res2 = func2(*args, **kwargs)
        print('已结束===>deco2.wrapper2')
        return res2
    return wrapper2

def deco3(x): # @deco3(111)==>得到了outter函数的内存地址,@+outter语法,含义是通过调用outter函数将正下方的函数地址传入,得到的返回值赋值给原函数名
    def outter(func3):    # index = outter(index),故func3=被装饰对象index函数的内存地址
        def wrapper3(*args, **kwargs):
            print('正在运行===>deco3.wrapper3')
            res3 = func3(*args, **kwargs)
            print('已结束===>deco3.wrapper3')
            return res3
        return wrapper3
    return outter

#加载顺序:自下而上(了解)
@deco1          #1、@deco1 等价于==>index=deco1(wrapper2的内存地址)=wrapper1的内存地址,即index=wrapper1的内存地址
@deco2          #1、@deco2 等价于==>index=deco2(wrapper3的内存地址)=wrapper2的内存地址,即index=wrapper2的内存地址
@deco3(111)     #1、deco3(111)等价于==> index = outter  2、@outter 等价于==> index = outter3(func)= wrapper3的内存地址 ,即index = wrapper3的内存地址
def index(x,y):
    print('from index %s:%s' %(x,y))
    return '我是最原始的index'


'''综上所述,index函数的内存地址被换成了wrapper1函数的内存地址,打印index查看'''
print(index)   # >输出:<function deco1.<locals>.wrapper1 at 0x7f409d9e01e0>

# 执行顺序自上而下的,即wraper1-》wrapper2-》wrapper3(记结论)
a = index(666,888)  # 等价==>wrapper1(666, 888) 
print('=**==**==**==**==**==**=')
print(a)

输出:<function deco1..wrapper1 at 0x7fbb3a0051e0>
正在运行===>deco1.wrapper1
正在运行===>deco2.wrapper2
正在运行===>deco3.wrapper3
from index 666:888
已结束===>deco3.wrapper3
已结束===>deco2.wrapper2
已结束===>deco1.wrapper1
===**====**=
我是最原始的index

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2059060.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

重磅发布!天途多自由度无人机调试台

无人机调试、测试和试飞很容易受空域、场地、环境、失控炸机和操作失误等限制。天途TE55多自由度无人机整机调试台应运而生&#xff01; 突破空域限制 天途TE55多自由度无人机整机调试台&#xff0c;突破场地空域限制&#xff0c;不到0.7平米的空间&#xff0c;即可完成小型无人…

[数据集][目标检测]建筑工地楼层空洞检测数据集VOC+YOLO格式2588张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2588 标注数量(xml文件个数)&#xff1a;2588 标注数量(txt文件个数)&#xff1a;2588 标注…

基于vue框架的办公用品管理系统i52wc(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;部门,员工,办公用品,入库记录,出库记录,申领信息 开题报告内容 基于Vue框架的办公用品管理系统 开题报告 一、引言 随着企业规模的扩大和日常运营的复杂化&#xff0c;办公用品的管理成为了一个不容忽视的重要环节。传统的办公用品管…

Java中接口

接口的定义和使用 练习 public abstract class Animal {private String name;private int age;public Animal() {}public Animal(String name, int age) {this.name name;this.age age;}public String getName() {return name;}public void setName(String name) {this.name…

ChatGPT、Claude 和 Gemini 在数据分析方面的比较(第 2 部分)

欢迎来到雲闪世界。欢迎回到我的系列文章的第二部分&#xff0c;ChatGPT、Claude 和 Gemini 在数据分析方面的比较&#xff01;在本系列中&#xff0c;我旨在比较这些 AI 工具在各种数据科学和分析任务中的表现&#xff0c;以帮助其他数据爱好者和专业人士根据自己的需求选择最…

告别图片堆成山, 图片转pdf工具:你的整理小能手来啦!

嘿&#xff0c;各位小伙伴&#xff01;你们有没有觉得&#xff0c;现在拍照比吃饭还日常&#xff0c;手机、电脑里堆满了照片&#xff0c;找起来简直跟大海捞针似的&#xff1f;别急&#xff0c;我今儿个就来给你们支个招——图片转PDF大法&#xff0c;一键变成整整齐齐的PDF文…

【Java-异常】

异常&#xff1a;程序在运行期间产生的一些错误 Java通过API中Throwable类的众多子类描述各种不同的异常。Java异常都是对象&#xff0c;是Throwable子类的实例。 Throwable可以划分为两个大类&#xff1a; Error错误类&#xff1a;会导致JVM虚拟机宕机 Exception异常类&…

Java---二维数组

一.数组的维数 假象&#xff1a;一维数组 二维数组&#xff1a;数组中的元素是一维数组 二.五子棋游戏 import javax.swing.*;public class Array06 {static String[][] matrix new String[15][15];static String black "⚫";static String white "⚪"…

SQL进阶技巧:多维分析之如何还原任意维度组合下的维度列簇名称?【利用grouping_id逆向分析】

目 录 0 需求描述 1 数据准备 2 问题分析 3 小结 0 需求描述 现有用户访问日志表 visit_log ,每一行数据表示一条用户访问日志。 需求: (1)按照如下维度组合 (province), (province, city), (province, city, device_type) 计算用户访问量,要求一条SQL语句统计所所…

[000-01-018].第3节:Linux环境下ElasticSearch环境搭建

我的后端学习笔记大纲 我的ElasticSearch学习大纲 1.Linux系统搭建ES环境&#xff1a; 1.1.单机版&#xff1a; a.安装ES-7.8版本 1.下载ES: 2.上传与解压&#xff1a;将下载的tar包上传到服务器software目录下&#xff0c;然后解压缩&#xff1a;tar -zxvf elasticsearch-7…

logstash入门学习

1、入门示例 1.1、安装 Redhat 平台 rpm --import http://packages.elasticsearch.org/GPG-KEY-elasticsearch cat > /etc/yum.repos.d/logstash.repo <<EOF [logstash-5.0] namelogstash repository for 5.0.x packages baseurlhttp://packages.elasticsearch.org…

以太坊 Pectra 升级四个月倒计时,哪些更新值得期待?

撰文&#xff1a;Ignas&#xff0c;DeFi Research 编译&#xff1a;J1N&#xff0c;Techub News 现在市场有充分的理由看空以太坊。因为自 2023 年初的市场低点以来&#xff0c; SOL 的涨幅比以太坊高 6.8 倍&#xff0c; 过去两年内 ETH/BTC 交易对的跌幅为 47%。 现在是以太…

Maven高级使用指南

在开发大型项目时&#xff0c;Maven作为一个强大的构建和项目管理工具&#xff0c;能显著提升项目管理和构建的效率。然而&#xff0c;随着项目的扩大&#xff0c;维护和管理的复杂性也随之增加。本文将探讨一些高级的Maven用法和解决方案&#xff0c;以帮助你更好地管理大型项…

在ISIS中什么是IP从地址

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

win11安装python及pycharm与webots联合仿真

1、查看Windows系统 是否已安装Python及版本 【冰糖Python】Windows系统 查看已安装的Python版本_怎么确定windows上成功安装python-CSDN博客 好&#xff0c;确认没装下面装python37 2、安装python3.7.9 参考教程Python 3.82安装教程&#xff08;Windows11、超详细版&#x…

Linux Shell 编程基础入门(一)

&#x1f600;前言 本篇博文是关于自动化工具Shell 编程的基本介绍&#xff0c;希望你能够喜欢 &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以帮助到大家&#xff0c;您的满意是我的动…

微服务:配置管理和配置热更新

参考&#xff1a;黑马程序员之微服务 &#x1f4a5; 该系列属于【SpringBoot基础】专栏&#xff0c;如您需查看其他SpringBoot相关文章&#xff0c;请您点击左边的连接 目录 一、引言 二、配置共享 1. 添加共享配置到nacos &#xff08;1&#xff09;jdbc的共享配置 shared…

点灯案例优化(三)宏定义特定位名称

前面利用位运算对代码进行二次优化以后&#xff0c;确实可读性更好&#xff0c;精确性更高了。但是吧&#xff0c;你乍一看这个代码 你可能一下都反应不过来这些的啥意思了&#xff0c;表达式右边上来就是一顿运算&#xff0c;可能刚开始大多数人都看不懂这写的什么&#xff0c…

用Python解决优化问题_模拟退火模板

一、模拟退火算法简介 模拟退火&#xff08;Simulated Annealing&#xff09;是一种启发式算法&#xff0c;用于在优化问题中找到一个好的解。启发式是指一种用于找到解决问题方法的原则或策略&#xff0c;它不保证找到最优解&#xff0c;但可以快速找到一个足够好的解。在许多…

资产全生命周期管理系统 固定资产全生命周期管理解决方案

什么是资产全生命周期管理系统 资产全生命周期管理系统是指对企业资产从采购、使用、维护、到报废的整个过程进行全方位管理的软件系统。通过这个系统&#xff0c;企业可以实现对资产的实时监控和管理&#xff0c;确保资产的利用率最大化。这个系统不仅能够跟踪资产的状态&…