Python中的装饰器是什么?装饰器是如何工作的?

2023-11-10

Python很早就引入了装饰器——在PEP-318中,作为一种简化函数和方法定义方式的机制,这些函数和方法在初始定义之后必须进行修改。

这样做的最初动机之一是,使用classmethod和staticmethod等函数来转换方法的原始定义,但是它们需要额外的一行代码来修改函数的初始定义。

一般来说,每次必须对函数应用转换时,我们必须使用modifier函数调用它,然后将它重新分配到函数初始定义时的名称中。

例如,假设有一个叫作original的函数,在它上面有一个改变original行为的函数(叫作modifier),那么我们必须这样写:

def original(...):
    ...
original = modifier(original)

请注意我们是如何更改函数并将其重新分配到相同的名称中去的。这是令人困惑的,很容易出错(假设有人忘记重新分配函数,或者重新分配了函数,但不在函数定义之后的行中,而是在更远的地方),而且很麻烦。出于这个原因,Python语言增加了一些语法支持。

前面的示例可以改写为如下样式:

@modifier
def original(...):
   ...

这意味着装饰器只是语法糖,用于调用装饰器之后的内容作为装饰器本身的第一个参数,结果将是装饰器返回的内容。

为了与Python的术语一致,在我们的示例中modifier称为装饰器,original是装饰函数,通常也被称为包装对象。

虽然该功能最初被认为是用于方法和函数的,但实际的语法允许它修饰任何类型的对象,因此我们将研究应用于函数、方法、生成器和类的装饰器。

最后一点需要注意的是,虽然装饰器的名称是正确的(毕竟,装饰器实际上是在对包装函数进行更改、扩展或处理),但不要将它与装饰器设计模式混淆。

5.1.1 装饰器函数

函数可能是对可以装饰的Python对象的最简单的表示形式。我们可以在函数上使用装饰器来应用各种逻辑——我们可以验证参数、检查前置条件、完全改变行为、修改其签名、缓存结果(创建原始函数的内存版本)等。

例如,我们将创建一个实现retry机制的基本装饰器,控制一个特定的域级异常并重试一定的次数:

# decorator_function_1.py
class ControlledException(Exception):
    """A generic exception on the program's domain."""

def retry(operation):
    @wraps(operation)
    def wrapped(*args, **kwargs):
        last_raised = None
        RETRIES_LIMIT = 3
        for _ in range(RETRIES_LIMIT):
            try:
                return operation(*args, **kwargs)
            except ControlledException as e:
                logger.info("retrying %s", operation.__qualname__)
                last_raised = e
        raise last_raised

    return wrapped

现在可以忽略@wrap的使用,因为它将在另一节中讨论。在for循环中使用“_”,意味着这个数字被分配给一个我们目前不感兴趣的变量,因为它不在for循环中使用(在Python中,将被忽略的值命名为“_”是一个常见的习惯用法)。

retry装饰器不接收任何参数,所以它可以很容易地应用于任何函数,如下所示:

@retry
def run_operation(task):
    """Run a particular task, simulating some failures on its execution."""
    return task.run()

正如一开始所解释的,在run_operation之上@retry的定义只是Python提供的语法糖,用于实际执行run_operation = retry(run_operation)。

在这个有限的示例中,我们可以看到如何用装饰器创建一个通用的retry操作,在某些确定的条件下(在本示例中,表示为可能与超时相关的异常),该操作将允许多次调用装饰后的代码。

5.1.2 装饰类

类也可以被装饰(PEP-3129),其装饰方法与语法函数的装饰方法相同。唯一的区别是,在为装饰器编写代码时,我们必须考虑到所接收的是一个类,而不是一个函数。

一些实践者可能会认为装饰类是相当复杂的事情,这样的场景可能会损害可读性,因为我们将在类中声明一些属性和方法,但是在幕后,装饰器可能会应用一些变化,从而呈现一个完全不同的类。

这种评定是正确的,但只有在装饰类技术被严重滥用的情况下成立。客观上,这与装饰功能没有什么不同;毕竟,类和函数一样,都只是Python生态系统中的一种类型的对象而已。在5.4节中,我们将再次审视这个问题的优缺点,但是这里只探索装饰器的优点,尤其是适用于类的装饰器的优点。

(1)重用代码和DRY原则的所有好处。类装饰器的一个有效情况是,强制多个类符合特定的接口或标准(通过只在将应用于多个类的装饰器中进行一次检查)。

(2)可以创建更小或更简单的类——这些类稍后将由装饰器进行增强。

(3)如果使用装饰器,那么需要应用到特定类上的转换逻辑将更容易维护,而不会使用更复杂的(通常是不鼓励使用的)方法,如元类。

在装饰器的所有可能应用程序中,我们将探索一个简单的示例,以了解装饰器可以用于哪些方面。记住,这不是类装饰器的唯一应用程序类型,而且给出的代码还可以有许多其他解决方案。所有这些解决方案都有优缺点,之所以选择装饰器,是为了说明它们的用处。

回顾用于监视平台的事件系统,现在需要转换每个事件的数据并将其发送到外部系统。然而,在选择如何发送数据时,每种类型的事件可能都有自己的特殊性。

特别是,登录事件可能包含敏感信息,例如我们希望隐藏的凭据。时间戳等其他领域的字段可能也需要一些转换,因为我们希望以特定的格式显示它们。符合这些要求的第一次尝试很简单,就像有一个映射到每个特定事件的类,并知道如何序列化它那样:

class LoginEventSerializer:
    def __init__(self, event):
        self.event = event

    def serialize(self) -> dict:
        return {
            "username": self.event.username,
            "password": "**redacted**",
            "ip": self.event.ip,
            "timestamp": self.event.timestamp.strftime("%Y-%m-%d
             %H:%M"),
        }

class LoginEvent:
    SERIALIZER = LoginEventSerializer

    def __init__(self, username, password, ip, timestamp):
        self.username = username
        self.password = password
        self.ip = ip
        self.timestamp = timestamp

    def serialize(self) -> dict:
        return self.SERIALIZER(self).serialize()

在这里,我们声明一个类。该类将直接映射到登录事件,其中包含它的一些逻辑——隐藏密码字段,并根据需要格式化时间戳。

虽然这是可行的,可能开始看起来是一个不错的选择,但随着时间的推移,若要扩展系统,就会发现一些问题。

(1)类太多。随着事件数量的增多,序列化类的数量将以相同的量级增长,因为它们是一一映射的。

(2)解决方案不够灵活。如果我们需要重用部分组件(例如,需要把密码藏在也有类似需求的另一个类型的事件中),就不得不将其提取到一个函数,但也要从多个类中调用它,这意味着我们没有重用那么多代码。

(3)样板文件。serialize()方法必须出现在所有事件类中,同时调用相同的代码。尽管我们可以将其提取到另一个类中(创建mixin),但这似乎没有很好地使用继承。

另一种解决方案是能够动态构造一个对象:给定一组过滤器(转换函数)和一个事件实例,该对象能够通过将过滤器应用于其字段的方式序列化它。然后,我们只需要定义转换每种字段类型的函数,并通过组合这些函数创建序列化器。

一旦有了这个对象,我们就可以装饰类以添加serialize()方法。该方法只会调用这些序列化对象本身:

def hide_field(field) -> str:
    return "**redacted**"

def format_time(field_timestamp: datetime) -> str:
    return field_timestamp.strftime("%Y-%m-%d %H:%M")

def show_original(event_field):
    return event_field

class EventSerializer:
    def __init__(self, serialization_fields: dict) -> None:
        self.serialization_fields = serialization_fields

    def serialize(self, event) -> dict:
        return {
            field: transformation(getattr(event, field))
            for field, transformation in
            self.serialization_fields.items()
        }

class Serialization:
    def __init__(self, **transformations):
        self.serializer = EventSerializer(transformations)

    def __call__(self, event_class):
        def serialize_method(event_instance):
                return self.serializer.serialize(event_instance)
            event_class.serialize = serialize_method
            return event_class

@Serialization(
    username=show_original,
    password=hide_field,
    ip=show_original,
    timestamp=format_time,
)
class LoginEvent:

    def __init__(self, username, password, ip, timestamp):
        self.username = username
        self.password = password
        self.ip = ip
        self.timestamp = timestamp

注意,装饰器让你更容易知道如何处理每个字段,而不必查看另一个类的代码。仅通过读取传递给类装饰器的参数,我们就知道用户名和IP地址将保持不变,密码将被隐藏,时间戳将被格式化。

现在,类的代码不需要定义serialize()方法,也不需要从实现它的mixin类进行扩展,因为这些都将由装饰器添加。实际上,这可能是创建类装饰器的唯一理由,因为如果不是这样的话,序列化对象可能是LoginEvent的一个类属性,但是它通过向该类添加一个新方法来更改类,这使得创建该类装饰器变得不可能。

我们还可以使用另一个类装饰器,通过定义类的属性来实现init方法的逻辑,但这超出了本例的范围。

通过使用Python 3.7+ 中的这个类装饰器(PEP-557),可以按更简洁的方式重写前面的示例,而不使用init的样板代码,如下所示:

from dataclasses import dataclass
from datetime import datetime

@Serialization(
    username=show_original,
    password=hide_field,
    ip=show_original,
    timestamp=format_time,
)
@dataclass
class LoginEvent:
    username: str
    password: str
    ip: str
    timestamp: datetime

5.1.3 其他类型的装饰器

既然我们已经知道了装饰器的@语法的实际含义,就可以得出这样的结论:可以装饰的不仅是函数、方法或类;实际上,任何可以定义的东西(如生成器、协同程序甚至是装饰过的对象)都可以装饰,这意味着装饰器可以堆叠起来。

前面的示例展示了如何链接装饰器。我们先定义类,然后将@dataclass应用于该类——它将该类转换为数据类,充当这些属性的容器。之后,通过@Serialization把逻辑应用到该类上,从而生成一个新类,其中添加了新的serialize()方法。

装饰器另一个好的用法是用于应该用作协同程序的生成器。我们将在第7章中探讨生成器和协同程序的细节,其主要思想是,在向新创建的生成器发送任何数据之前,必须通过调用next()将后者推进到下一个yield语句。这是每个用户都必须记住的手动过程,因此很容易出错。我们可以轻松创建一个装饰器,使其接收生成器作为参数,调用next(),然后返回生成器。

5.1.4 将参数传递给装饰器

至此,我们已经将装饰器看作Python中的一个强大工具。如果我们可以将参数传递给装饰器,使其逻辑更加抽象,那么其功能可能会更加强大。

有几种实现装饰器的方法可以接收参数,但是接下来我们只讨论最常见的方法。第一种方法是将装饰器创建为带有新的间接层的嵌套函数,使装饰器中的所有内容深入一层。第二种方法是为装饰器使用一个类。

通常,第二种方法更倾向于可读性,因为从对象的角度考虑,其要比3个或3个以上使用闭包的嵌套函数更容易。但是,为了完整起见,我们将对这两种方法进行探讨,以便你可以选择使用最适合当前问题的方法。

1.带有嵌套函数的装饰器

粗略地说,装饰器的基本思想是创建一个返回函数的函数(通常称为高阶函数)。在装饰器主体中定义的内部函数将是实际被调用的函数。

现在,如果希望将参数传递给它,就需要另一间接层。第一个函数将接收参数,在该函数中,我们将定义一个新函数(它将是装饰器),而这个新函数又将定义另一个新函数,即装饰过程返回的函数。这意味着我们将至少有3层嵌套函数。

如果你到目前为止还不明白上述内容的含义,也不用担心,待查看下面给出的示例之后,就会明白了。

第一个示例是,装饰器在一些函数上实现重试功能。这是个好主意,只是有个问题:实现不允许指定重试次数,只允许在装饰器中指定一个固定的次数。

现在,我们希望能够指出每个示例有多少次重试,也许甚至可以为这个参数添加一个默认值。为了实现这个功能,我们需要用到另一层嵌套函数——先用于参数,然后用于装饰器本身。

这是因为如下代码:

@retry(arg1, arg2,... )

必须返回装饰器,因为@语法将把计算结果应用到要装饰的对象上。从语义上讲,它可以翻译成如下内容:

<original_function> = retry(arg1, arg2, ....)(<original_function>)

除了所需的重试次数,我们还可以指明希望控制的异常类型。支持新需求的新版本代码可能是这样的:

RETRIES_LIMIT = 3

def with_retry(retries_limit=RETRIES_LIMIT, allowed_exceptions=None):
    allowed_exceptions = allowed_exceptions or (ControlledException,)

    def retry(operation):

        @wraps(operation)
        def wrapped(*args, **kwargs):
            last_raised = None
            for _ in range(retries_limit):
                try:
                    return operation(*args, **kwargs)
                except allowed_exceptions as e:
                    logger.info("retrying %s due to %s", operation, e)
                    last_raised = e
            raise last_raised

        return wrapped

    return retry

下面是这个装饰器如何应用于函数的一些示例,其中显示了它接收的不同选项:

# decorator_parametrized_1.py
@with_retry()
def run_operation(task):
    return task.run()

@with_retry(retries_limit=5)
def run_with_custom_retries_limit(task):
    return task.run()

@with_retry(allowed_exceptions=(AttributeError,))
def run_with_custom_exceptions(task):
    return task.run()

@with_retry(
    retries_limit=4, allowed_exceptions=(ZeroDivisionError, AttributeError)
)
def run_with_custom_parameters(task):
    return task.run()

2.装饰器对象

前面的示例需要用到3层嵌套函数。首先,这将是一个用于接收我们想要使用的装饰器的参数。在这个函数中,其余的函数是使用这些参数和装饰器逻辑的闭包。

更简洁的实现方法是用一个类定义装饰器。在这种情况下,我们可以在__init__方法中传递参数,然后在名为__call__的魔法方法上实现装饰器的逻辑。

装饰器的代码如下所示:

class WithRetry:

    def __init__(self, retries_limit=RETRIES_LIMIT,
allowed_exceptions=None):
        self.retries_limit = retries_limit
        self.allowed_exceptions = allowed_exceptions or
(ControlledException,)

    def __call__(self, operation):

        @wraps(operation)
        def wrapped(*args, **kwargs):
            last_raised = None

            for _ in range(self.retries_limit):
                try:
                    return operation(*args, **kwargs)
                except self.allowed_exceptions as e:
                    logger.info("retrying %s due to %s", operation, e)
                    last_raised = e
            raise last_raised

        return wrapped

这个装饰器可以像之前的一样应用,就像这样:

@WithRetry(retries_limit=5)
def run_with_custom_retries_limit(task):
    return task.run()

注意Python语法在这里是如何起作用的,这一点很重要。首先,我们创建对象,这样在应用@操作之前,对象已经创建好了,并且其参数传递给它了,用这些参数初始化这个对象,如init方法中定义的那样。在此之后,我们将调用@操作,这样该对象将包装名为run_with_custom_reries_limit的函数,而这意味着它将被传递给call这个魔法方法。

在call这个魔法方法中,我们定义了装饰器的逻辑,就像通常所做的那样——包装了原始函数,返回一个新的函数,其中包含所要的逻辑。

5.1.5 充分利用装饰器

本节介绍一些充分利用装饰器的常见模式。在有些常见的场景中使用装饰器是个非常好的选择。

可用于应用程序的装饰器数不胜数,下面仅列举几个最常见或相关的。

(1)转换参数。更改函数的签名以公开更好的API,同时封装关于如何处理和转换参数的详细信息。

(2)跟踪代码。记录函数及其参数的执行情况。

(3)验证参数

(4)实现重试操作

(5)通过把一些(重复的)逻辑移到装饰器中来简化类

接下来详细讨论前两个应用程序。

1.转换参数

前文提到,装饰器可以用来验证参数(甚至在DbC的概念下强制一些前置条件或后置条件),因此你可能已经了解到,这是一些处理或者操控参数时使用装饰器的常用方法。

特别是,在某些情况下,我们会发现自己反复创建类似的对象,或者应用类似的转换,而我们希望将这些转换抽象掉。大多数时候,我们可以通过简单地用装饰器实现这一点。

2.跟踪代码

在本节中讨论跟踪时,我们将提到一些更通用的内容,这些内容与处理所要监控的函数的执行有关,具体是指:

(1)实际跟踪函数的执行(例如,通过记录函数执行的行);

(2)监控函数的一些指标(如CPU使用量或内存占用);

(3)测量函数的运行时间;

(4)函数被调用时的日志,以及传递给它的参数。

我们将在5.2节剖析一个简单的装饰器示例,该示例记录了函数的执行情况,包括函数名和运行时间。

本文摘自《编写整洁的Python代码》

 

本书介绍Python软件工程的主要实践和原则,旨在帮助读者编写更易于维护和更整洁的代码。全书共10章:第1章介绍Python语言的基础知识和搭建Python开发环境所需的主要工具;第2章描述Python风格代码,介绍Python中的第一个习惯用法;第3章总结好代码的一般特征,回顾软件工程中的一般原则;第4章介绍一套面向对象软件设计的原则,即SOLID原则;第5章介绍装饰器,它是Python的**特性之一;第6章探讨描述符,介绍如何通过描述符从对象中获取更多的信息;第7章和第8章介绍生成器以及单元测试和重构的相关内容;第9章回顾Python中最常见的设计模式;第10章再次强调代码整洁是实现良好架构的基础。

本书适合所有Python编程爱好者、对程序设计感兴趣的人,以及其他想学习更多Python知识的软件工程的从业人员。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python中的装饰器是什么?装饰器是如何工作的? 的相关文章

  • 微服务实践--微服务方法论00

    思想 在接收到一个新的新项目时 架构师的职责是建立项目的业务与技术实现之间的桥梁 在翻译业务到技术实现的过程中需要进行业务建模 技术设计等方面的工作 业务建模和技术设计过程中都有各自领域的知识体系 基本上每个知识体系都是由上层的理论 概念和
  • 解决k8s集群环境内存不足导致容器被kill问题

    背景 最近线上环境上出现了一个问题 k8s集群环境Pod中的tomcat容器运行一段时间后直接被killd 但有时一切看起来正常 不能准确判断在什么时机出现被Killd问题 本文就此问题介绍了Linux内存不足原因以及为什么特定进程会被杀死
  • C语言入门

    什么是C语言 C语言是一门通用计算机编程语言 广泛应用于底层开发 C语言的设计目标是提供一种能以简易 的方式编译 处理低级存储器 产生少量的机器码以及不需要任何运行环境支持便能运行的编程 语言 尽管C语言提供了许多低级处理的功能 但仍然保持
  • 数据分析整体框架之落地全流程讲解

    小飞象 交流会 人生没有四季 只有两季 努力就是旺季 不努力就是淡季 内部交流 11期 数据分析整体框架 之落地全流程 data analysis 分享人 刘珍珍 数据分析的目的是把隐藏在杂乱无章的数据背后的信息集中和提炼出来 总结出研究对
  • Python实现“鸟脸识别”系统,看看什么鸟最贪吃~ 初学者也能学会

    梦晨 发自 凹非寺 量子位 报道 公众号 QbitAI 网友cldud1245是一个鸟类爱好者 以下简称喂鸟哥 最近打算自学Python 拥有其他语言编程经验的他 可不打算按部就班从Hello World做起 一上来就挑战图像识别 他用一个
  • [学习笔记]Matlab(持续更新)

    文章目录 一 Matlab知识学习 1 输入输出语句 注意 Matlab中disp fprintf及sprintf三者之间的区别 2 exist函数的使用 3 matlab中的注释 4 几种常用的清除命令 5 MAT文件如何操作 6 dir
  • 如何写一篇简洁易懂的测试报告?

    一 什么是测试报告 测试报告是指把测试的过程和结果写成文档 对发现的问题和缺陷进行分析 为纠正软件的存在的质量问题提供依据 同时为软件验收和交付打下基础 二 测试报告的内容 测试报告的内容可以总结为以下目录 首页 引言 目的 背景 缩略语
  • 送一个2022年最赚钱的方法!包含操作方法!

    在互联网上 可恶的人有很多 值得我们学习的人也有很多 有的人做起事来不讲武德 而有的人却是我们值得学习一生的榜样 在赚钱的路上 信息就是金钱 你掌握了信息的源头 就掌握的金矿 拥有足够的信息来源 那么你就有足够的金钱 假如你想在一个行业里快
  • 与Miriam Suzanne一起进行Sass,Susy,单元测试和寻找声音

    In this episode of the Versioning Show Tim and David are joined by Miriam Suzanne best known for Susy a responsive layou
  • C++基础之纯虚函数

    一 纯虚函数的定义 纯虚函数是一种特殊的虚函数 在许多情况下 在基类中不能对虚函数给出有意义的实现 而把它声明为纯虚函数 它的实现留给该基类的派生类去做 这就是纯虚函数的作用 C 中的纯虚函数 一般在函数名后使用 0作为此类函数的标志 前面
  • Gavin Wood Web3峰会最新演讲:波卡不是智能合约平台,而是平台的平台(全文)...

    在波卡上 每个平台都在用高性能 高效率和最优的方式做着自己擅长的事 而不必让它们的用户用底层平台的货币进行支付 从而将可定制性和灵活性提高了一个台阶 本文谨代表作者个人观点 不代表火星财经立场 该内容旨在传递更多市场信息 不构成任何投资建议
  • 程序员如何做副业?35岁前,千万别让死工资绊住你赚钱的步伐

    近年来互联网行情下降 好多人都在思考要不要搞个副业来抵御风险 这不又来事了 这两天又爆了互联网大裁员 继阿里 向社会输送人才 之后 京东又搞了个 毕业礼 整的小伙伴们人心惶惶 副业的关注度又一波升级 那今天我们就来聊聊 程序员做副业这件事
  • 每日一问:你想如何破坏单例模式?

    前言 1 单例是什么 单例模式 是一种创建型设计模式 目的是保证全局一个类只有一个实例对象 分为懒汉式和饿汉式 所谓懒汉式 类似于懒加载 需要的时候才会触发初始化实例对象 而饿汉式正好相反 项目启动 类加载的时候 就会创建初始化单例对象 1
  • n行Python代码系列:两行代码去除抖音快手短视频尾部Logo

    老猿Python博文目录 https blog csdn net LaoYuanPython 一 引言 最近看到好几篇类似 n行Python代码 的博文 看起来还挺不错 简洁 实用 传播了知识 带来了阅读量 撩动了老猿的心 决定跟风一把 推
  • python 文件、文件夹和路径操作笔记

    记录python关于文件夹 文件和路径的一些常用操作 方便用时查询 常用的函数备注 os listdir 列出文件夹中所有文件 os path splitext 获取文件的后缀名 返回list 后缀在list 1 中 os path joi
  • 野外偷拍_野外紧急设计

    关于本系列 本系列文章旨在为人们经常讨论但难以捉摸的软件体系结构和设计概念提供新的视角 通过具体的示例 尼尔 福特为您提供了进化架构和紧急设计的敏捷实践的坚实基础 通过将重要的架构和设计决策推迟到最后一个负责任的时刻 可以防止不必要的复杂性
  • 前端趋势 2022

    大家好 我是若川 持续组织了近一年的源码共读活动 感兴趣的可以 加我微信lxchuan12 参与 每周大家一起学习200行左右的源码 共同进步 同时极力推荐订阅我写的 学习源码整体架构系列 包含20余篇源码文章 历史面试系列 另外 目前建有
  • 快速学习Python基础知识(3)

    一 输入输出 1 1 input输入函数的使用 input函数 是获取键盘输出 保存成一个字符串 注意 input 函数的返回值是一个字符串类型 即便你输入的是数字 返回的也会以一个字符串的形式返回给我们 inputStr input 提示
  • C++ 智能指针详解

    点击蓝字 关注我们 参考资料 C Primer中文版 第五版 我们知道除了静态内存和栈内存外 每个程序还有一个内存池 这部分内存被称为自由空间或者堆 程序用堆来存储动态分配的对象即那些在程序运行时分配的对象 当动态对象不再使用时 我们的代码
  • jvm之栈、堆

    1 Java Virtual Machine 人群当中 一位叫java的小伙子正向周围一众人群细数着自己取得的荣耀与辉煌 就在此时 c老头和c 老头缓步走来 看着被众人围住的java c老头感叹地对着身旁的c 说道 原以为你就可以挑起我的梁

随机推荐

  • 汽车变排量空调压缩机的工作原理

    不同于定排量压缩机 fixed displacement compressor FDC 变排量压缩机 variable displacement compressor VDC 可自动改变其泵送能力以满足空调的需求 当车厢温度高时 它会提高其
  • 《Perl语言入门》读书笔记(四)子程序

    1 子程序 1 1 定义子程序 使用关键字sub开头 在写上子程序名 字母 数字和下划线组成 不能以数字开头 大括号框柱子程序主体 子程序可以定义在文件的任意位置 为了方便代码阅读 一般建议放在开头或结尾处 sub marine n 1 全
  • WebSocket的使用指南---前端

    1 WebSocket概述 WebSocket 是 HTML5 开始提供的一种浏览器与服务器间进行全双工通讯的网络技术 WebSocket 通信协议于2011年被IETF定为标准RFC 6455 WebSocketAPI 被 W3C 定为标
  • String

    String是一个对象 不是基本数据类型 String的特点 字符串对象一旦初始化 便不能被修改 改变的只是引用型变量的指向 例如 String str abc String str ert abc 依然存在 只是str的指向变了 Stri
  • Mysql系列 - 第4天:DDL常见操作汇总

    这是Mysql系列第4篇 环境 mysql5 7 25 cmd命令中进行演示 DDL Data Define Language数据定义语言 主要用来对数据库 表进行一些管理操作 如 建库 删库 建表 修改表 删除表 对列的增删改等等 文中涉
  • 机器学习中的特征变量及处理总结

    文章目录 1 定性特征变量 1 1 定类变量处理 1 2 定序变量处理 2 定量特征变量 3 总结 牢记一句话 数据和特征决定了机器学习的上限 而模型和算法只是逼近这个上限而已 机器学习的根本目标 就是用数据的特征变量去对目标变量进行预测
  • Github 榜首!B 站疯传!程序员思维导图 48 张!!!

    介绍在下面 整个内容包括 程序员史上最强编程思维导图 48 张 800 份求职简历模板 我写的 图解算法小册 解析 150 道高频算法面试题目 25k star Github 榜首项目 资料获取地址 无套路 直接可以下载 Github 榜首
  • Jmeter快速上手之接口测试

    目录 1 前言 2 简介 3 安装 4 环境变量 4 1 Windows环境 4 2 Mac环境 5 启动程序 6 目录说明 7 操作示例 7 1 Get请求 7 2 Post请求 7 3 依赖请求 1 前言 压测工具 Jmeter 除了可
  • 什么是接口?

    1 什么是接口 接口是一种特殊的内部类 它里面的所有方法都没有实现 2 接口的特点 1 接口中成员默认访问修饰符都是public 即便你不写 2 定义接口必须interface关键字完成 3 接口中可以定义变量 但是变量必须有固定的修饰符修
  • JUC并发编程--------线程安全篇

    目录 什么是线程安全性问题 如何实现线程安全 1 线程封闭 2 无状态的类 3 让类不可变 4 加锁和CAS 并发环境下的线程安全问题有哪些 1 死锁 2 活锁 3 线程饥饿 什么是线程安全性问题 我们可以这么理解 我们所写的代码在并发情况
  • java自引用/类的递归调用问题

    Java的自引用问题 什么是自引用 递归调用 代码示例和分析 自引用情况 类比C 什么是自引用 递归调用 在编写代码的过程中 我们经常看到类中出现该类的声明 示例 class A int data A a 这种情况就被称为自引用 代码示例和
  • Android架构项目代码结构规范--组件化代码

    前言 组件化和插件化有什么区别 虽说网上有很多文章但是讲清的聊聊无几 这也是这篇文章的由来 大方向 组件化是一个项目主管设计管理项目架构方案 而插件化有商务上的合作和局部功能热更换修复等 小方向 如果是公司app合作 组件化也就是插件化作为
  • CLion Bug集合1:windows下导入openssl库方法以及踩过的坑

    系统 win10 64位 工具链 MinGW IDE CLion openssl库下载 下载方法 主要有两种方式 本文主要讲解方式1 方式1 下载地址 2022 6 1补充 该方法的动态库是MinGW64位用的 32位见方法2 方式2 编译
  • linux常用命令案例总结wc,top,free,df -h,head,sed,awk,netstat -antp,ps -aux, ethtool eth0

    1 wc的使用 统计一个目录下的文件个数 root localhost etc cd var log root localhost log ll grep wc l 53 root localhost log 拓展 关于命令wc的使用 wc
  • 使用冻结层进行迁移学习

    使用冻结层进行迁移学习 在yolov5的训练过程中 作者介绍了如何使用冻结层实现迁移学习的策略 具体可以参考官方话题 Transfer Learning with Frozen Layers Issue 1314 ultralytics y
  • JS中的Date数据类型

    JS的Date数据类型 在JavaScript中 Date数据类型用于处理日期和时间 它可以表示自1970年1月1日00 00 00 UTC 协调世界时 以来的毫秒数 Date对象在许多应用程序中都非常有用 例如在Web应用程序中显示当前时
  • Python实现HBA混合蝙蝠智能算法优化循环神经网络回归模型(LSTM回归算法)项目实战

    说明 这是一个机器学习实战项目 附带数据 代码 文档 视频讲解 如需数据 代码 文档 视频讲解可以直接到文章最后获取 1 项目背景 蝙蝠算法是2010年杨教授基于群体智能提出的启发式搜索算法 是一种搜索全局最优解的有效方法 该算法基于迭代优
  • eMMC简介

    eMMC是embedded MultiMediaCard的简称 MultiMediaCard 即MMC 是一种闪存卡 Flash Memory Card 标准 它定义了MMC的架构以及访问Flash Memory的接口和协议 而eMMC则是
  • 全屏dialog

    下面是iOS里面做全屏Dialog的代码 调用show时Dialog会覆盖当前的controller 全屏显示 可以用来做蒙板效果 欢迎转载 转载请注明出处 http blog csdn net tadican article detail
  • Python中的装饰器是什么?装饰器是如何工作的?

    Python很早就引入了装饰器 在PEP 318中 作为一种简化函数和方法定义方式的机制 这些函数和方法在初始定义之后必须进行修改 这样做的最初动机之一是 使用classmethod和staticmethod等函数来转换方法的原始定义 但是