python:functools.partial和functools.wraps使用

python:functools.partial和functools.wraps使用

1 前言

python内置的functools模块,提供了一些非常好用的类或者方法,其中functools.partial和functools.wraps的使用频率较高,本文将针对其分析使用。

2 使用

2.1 functools.partial

functools.partial是提供的工具类,python源码如下(本文参考自python3.9源码):

class partial:
    """New function with partial application of the given arguments
    and keywords.
    """

    __slots__ = "func", "args", "keywords", "__dict__", "__weakref__"

    def __new__(cls, func, /, *args, **keywords):
        if not callable(func):
            raise TypeError("the first argument must be callable")

        if hasattr(func, "func"):
            args = func.args + args
            keywords = {**func.keywords, **keywords}
            func = func.func

        self = super(partial, cls).__new__(cls)

        self.func = func
        self.args = args
        self.keywords = keywords
        return self

    def __call__(self, /, *args, **keywords):
        keywords = {**self.keywords, **keywords}
        return self.func(*self.args, *args, **keywords)

    @recursive_repr()
    def __repr__(self):
        qualname = type(self).__qualname__
        args = [repr(self.func)]
        args.extend(repr(x) for x in self.args)
        args.extend(f"{k}={v!r}" for (k, v) in self.keywords.items())
        if type(self).__module__ == "functools":
            return f"functools.{qualname}({', '.join(args)})"
        return f"{qualname}({', '.join(args)})"

    def __reduce__(self):
        return type(self), (self.func,), (self.func, self.args,
               self.keywords or None, self.__dict__ or None)

    def __setstate__(self, state):
        if not isinstance(state, tuple):
            raise TypeError("argument to __setstate__ must be a tuple")
        if len(state) != 4:
            raise TypeError(f"expected 4 items in state, got {len(state)}")
        func, args, kwds, namespace = state
        if (not callable(func) or not isinstance(args, tuple) or
           (kwds is not None and not isinstance(kwds, dict)) or
           (namespace is not None and not isinstance(namespace, dict))):
            raise TypeError("invalid partial state")

        args = tuple(args) # just in case it's a subclass
        if kwds is None:
            kwds = {}
        elif type(kwds) is not dict: # XXX does it need to be *exactly* dict?
            kwds = dict(kwds)
        if namespace is None:
            namespace = {}

        self.__dict__ = namespace
        self.func = func
        self.args = args
        self.keywords = kwds

根据源码,我们以同样的逻辑定义part类(功能类似partial):

def add(x, y):
    return x + y


class part:
    def __new__(cls, func, /, *args, **keywords):
        if not callable(func):
            raise TypeError("the first argument must be callable")

        if hasattr(func, "func"):
            args = func.args + args
            keywords = {**func.keywords, **keywords}
            func = func.func

        self = super(part, cls).__new__(cls)

        self.func = func
        self.args = args
        self.keywords = keywords
        return self

    def __call__(self, /, *args, **keywords):

        keywords = {**self.keywords, **keywords}
        return self.func(*self.args, *args, **keywords)


print(hasattr(add, 'func'))
p = part(add, 10)
print(p(7))

效果和functools.partial类似,执行结果如下:

在这里插入图片描述

去掉__new__方法和__call__方法的“/,”和上述效果一致(去掉位置参数*args前加上的“/,”):

class part:
    def __new__(cls, func, *args, **keywords):
        if not callable(func):
            raise TypeError("the first argument must be callable")

        if hasattr(func, "func"):
            args = func.args + args
            keywords = {**func.keywords, **keywords}
            func = func.func

        self = super(part, cls).__new__(cls)

        self.func = func
        self.args = args
        self.keywords = keywords
        return self

    def __call__(self, *args, **keywords):

        keywords = {**self.keywords, **keywords}
        return self.func(*self.args, *args, **keywords)

执行结果不变:

在这里插入图片描述

但是注意,如果将“/,”替换为“_,”就会占据解构的位置参数,导致缺少参数而执行异常(python不像javascript,javascript可以使用空格加逗号(如[ , , a] = [1, 2, 3])来解构不需要的值,但是python一般是通过"_“来解构不需要的值,实际值是被”_"所占据了的(如 [ _, _, a] = [1, 2, 3])):

class part:
    def __new__(cls, _, func, *args, **keywords):
        if not callable(func):
            raise TypeError("the first argument must be callable")

        if hasattr(func, "func"):
            args = func.args + args
            keywords = {**func.keywords, **keywords}
            func = func.func

        self = super(part, cls).__new__(cls)

        self.func = func
        self.args = args
        self.keywords = keywords
        return self

    def __call__(self, _, *args, **keywords):

        keywords = {**self.keywords, **keywords}
        return self.func(*self.args, *args, **keywords)

执行报错(不能使用“_,”):

在这里插入图片描述

上面的part类,参考的functools.partial实现,核心是自定义实现__new__方法,达到自定义对象实例self的效果,同时为self添加属性func,即我们传入的函数,以及为self添加属性args和keywords,也就是上面我们的part(add, 10),除了第一个函数参数外,传入的固定的位置参数和关键字参数。同时第二个核心点是自定义实现__call__方法,__call__方法会使得对象成为callable类型的对象,也就是可以像函数一样调用,即func(),也就是当我们使用类名()来生成类的实例对象时,对实例对象再次调用实例对象()时,会调用__call__方法

__new__方法的了解可参考:python:__new__和__init__

下面是__call__方法的定义和使用示例:

class CallableClazz:
    def __call__(self, a, b):
        return a + b


func = CallableClazz()
result = func(4, 6)  # 实例对象,可以像函数一样调用,因为有实现__call__方法
print(result)
print(callable(func))
# 10
# True

由此可知,根据自定义实现__call__方法,解构关键字参数,即keywords = {**self.keywords, **keywords},self.keywords是我们固定的关键字参数,而keywords是后续调用函数对象,也就是上述调用p(7)中传入的关键字参数;同理,self.func(*self.args, args, **keywords)中传入的self.args, *args,self.args也是固定的位置参数,args是调用p(7)中传入的位置参数,这里是7,所以可以达到固定参数值的情况下,执行函数。

小结:partial通过实现"__new__“和”__call__"生成一个可调用对象,这个对象内部保存了被包装函数以及固定参数,这个对象可以像函数一样被调用,调用时,其实是执行了对象内部持有的被包装函数,其参数由固定参数和新传入的参数组合而来。

2.2 functools.wraps

有了上述functools.partial的源码分析,再参考python源码functools.wraps如下:

def wraps(wrapped,
          assigned = WRAPPER_ASSIGNMENTS,
          updated = WRAPPER_UPDATES):
    """Decorator factory to apply update_wrapper() to a wrapper function

       Returns a decorator that invokes update_wrapper() with the decorated
       function as the wrapper argument and the arguments to wraps() as the
       remaining arguments. Default arguments are as for update_wrapper().
       This is a convenience function to simplify applying partial() to
       update_wrapper().
    """
    return partial(update_wrapper, wrapped=wrapped,
                   assigned=assigned, updated=updated)

这里我们需要提前知道一个知识点,也就是python的装饰器中,def的函数中返回inner函数,同时def定义的函数的接收参数亦是函数,就可以达到装饰器的效果。而若inner函数是callable的,也就是类似类实现了__call__方法,那么也可以作为类似inner函数的效果,形成python装饰器

由此可知,上述的wraps函数,实际是python的装饰器,因为返回的是callable的类实例partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated),该类实例还可以执行:类实例(),也就是实现了__call__方法,可以类似函数调用,所以亦可充当装饰器

那么接下来分析其效果:

返回的partial实例对象,第一个参数是update_wrapper,其为函数,python functools源码如下:

def update_wrapper(wrapper,
                   wrapped,
                   assigned = WRAPPER_ASSIGNMENTS,
                   updated = WRAPPER_UPDATES):
    """Update a wrapper function to look like the wrapped function

       wrapper is the function to be updated
       wrapped is the original function
       assigned is a tuple naming the attributes assigned directly
       from the wrapped function to the wrapper function (defaults to
       functools.WRAPPER_ASSIGNMENTS)
       updated is a tuple naming the attributes of the wrapper that
       are updated with the corresponding attribute from the wrapped
       function (defaults to functools.WRAPPER_UPDATES)
    """
    for attr in assigned:
        try:
            value = getattr(wrapped, attr)
        except AttributeError:
            pass
        else:
            setattr(wrapper, attr, value)
    for attr in updated:
        getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
    # Issue #17482: set __wrapped__ last so we don't inadvertently copy it
    # from the wrapped function when updating __dict__
    wrapper.__wrapped__ = wrapped
    # Return the wrapper so this can be used as a decorator via partial()
    return wrapper

而partial实例对象的第二个参数是wrapped,也就是装饰器修饰的原本函数对象;第三个参数是assigned,如下源码所示:

WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__',
                       '__annotations__')

partial实例对象的第四个参数是updated,源码如下:

WRAPPER_UPDATES = ('__dict__',)

所以实际返回的partial实例对象,是对方法update_wrapper,固定了位置参数

根据functools工具类的源码,我们实现下述的功能并分析效果:

class part:
    def __new__(cls, func, /, *args, **keywords):
        if not callable(func):
            raise TypeError("the first argument must be callable")

        if hasattr(func, "func"):
            args = func.args + args
            keywords = {**func.keywords, **keywords}
            func = func.func

        self = super(part, cls).__new__(cls)

        self.func = func
        self.args = args
        self.keywords = keywords
        return self

    def __call__(self, /, *args, **keywords):

        keywords = {**self.keywords, **keywords}
        return self.func(*self.args, *args, **keywords)


WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__',
                       '__annotations__')
WRAPPER_UPDATES = ('__dict__',)


def wrapsNew(wrapped,
             assigned=WRAPPER_ASSIGNMENTS,
             updated=WRAPPER_UPDATES):
    """Decorator factory to apply update_wrapper() to a wrapper function

       Returns a decorator that invokes update_wrapper() with the decorated
       function as the wrapper argument and the arguments to wraps() as the
       remaining arguments. Default arguments are as for update_wrapper().
       This is a convenience function to simplify applying partial() to
       update_wrapper().
    """
    return part(update_wrapper, wrapped=wrapped,
                assigned=assigned, updated=updated)


def update_wrapper(wrapper,
                   wrapped,
                   assigned=WRAPPER_ASSIGNMENTS,
                   updated=WRAPPER_UPDATES):
    """Update a wrapper function to look like the wrapped function

       wrapper is the function to be updated
       wrapped is the original function
       assigned is a tuple naming the attributes assigned directly
       from the wrapped function to the wrapper function (defaults to
       functools.WRAPPER_ASSIGNMENTS)
       updated is a tuple naming the attributes of the wrapper that
       are updated with the corresponding attribute from the wrapped
       function (defaults to functools.WRAPPER_UPDATES)
    """
    for attr in assigned:
        try:
            value = getattr(wrapped, attr)
        except AttributeError:
            pass
        else:
            setattr(wrapper, attr, value)
    for attr in updated:
        getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
    # Issue #17482: set __wrapped__ last so we don't inadvertently copy it
    # from the wrapped function when updating __dict__
    wrapper.__wrapped__ = wrapped
    # Return the wrapper so this can be used as a decorator via partial()
    return wrapper


def outer(func):
    @wrapsNew(func)
    def inner(*args, **kwargs):
        print(f"before...")
        func(*args, **kwargs)
        print("after...")

    return inner


@outer
def add(a, b):
    """
    求和运算
    """
    print(a + b)

对于add方法,我们执行如下:

if __name__ == '__main__':
    print(add(1, 6))

执行前分析如下:

对于代码:

@outer
def add(a, b):
    """
    求和运算
    """
    print(a + b)

根据装饰器语法糖的形式,实际是如下的效果:

add = outer(add)   
意即:相当于于我们调用的add方法,是outer(add)方法,即outer(add)(1, 6)

outer方法如下:

def outer(func):
    @wrapsNew(func)
    def inner(*args, **kwargs):
        print(f"before...")
        func(*args, **kwargs)
        print("after...")

    return inner

那么继续分析:

而outer(add) 方法的返回值,相当于: inner = wrapsNew(add)(inner)  return inner
即: outer(add) = wrapsNew(add)(inner)
即: outer(add) = part(update_wrapper, wrapped=wrapped,
                   assigned=assigned, updated=updated)(inner)
即: add = part(update_wrapper, wrapped=add方法,
                   assigned=assigned, updated=updated)(inner)

上述拆解分析后可知:

  • wrapped是 add 方法,也就是我们原生定义的add方法,不加任何修饰改变的
  • assigned是 WRAPPER_ASSIGNMENTS = (‘__module__’, ‘__name__’,‘__qualname__’, ‘__doc__’, ‘__annotations__’)
  • updated是 WRAPPER_UPDATES = (‘__dict__’,)

也就是说,add经装饰后的新方法,是通过我们自定义的part类绑定了3个关键字参数(kwargs),然后传入参数outer.inner并调用得来的,关键字参数如下:

  • wrapped(add定义的原方法);
  • assigned,即(‘__module__’, ‘__name__’, ‘__qualname__’,‘__doc__’, ‘__annotations__’);
  • updated,即(‘__dict__’,);

所以下述执行__new__方法时,func是update_wrapper方法,而self.args为空元组,self.keywords是关键字参数字典dict,值也就是上述提到的绑定的3个关键字参数:

class part:
    def __new__(cls, func, /, *args, **keywords):
        if not callable(func):
            raise TypeError("the first argument must be callable")

        if hasattr(func, "func"):
            args = func.args + args
            keywords = {**func.keywords, **keywords}
            func = func.func

        self = super(part, cls).__new__(cls)

        self.func = func
        self.args = args
        self.keywords = keywords

然后执行part(…)(inner)的时候,就会调用如下part类的call方法:

def __call__(self, /, *args, **keywords):

    keywords = {**self.keywords, **keywords}
    return self.func(*self.args, *args, **keywords)

debug结果如下可见,keywords就是上述的3个关键字参数:

在这里插入图片描述

args是调用的part(…)(inner)传入的inner方法:

在这里插入图片描述

而self.args因为调用part类时没有传入需要固定的位置参数,所以是空元组:

在这里插入图片描述

也就是说,上述的调用,最终表现形式如下:

update_wrapper(
inner方法(位置参数), 
{wrapped: add原方法, 
assigned: ('\_\_module\_\_', '\_\_name\_\_', '\_\_qualname\_\_','\_\_doc\_\_', '\_\_annotations\_\_'), 
updated: ('\_\_dict\_\_',)}(关键字参数))

故而如下参数中,wrapper是inner方法,wrapped是add原方法:

def update_wrapper(wrapper,
                   wrapped,
                   assigned=WRAPPER_ASSIGNMENTS,
                   updated=WRAPPER_UPDATES):

所以核心逻辑是update_wrapper方法:

def update_wrapper(wrapper,
                   wrapped,
                   assigned=WRAPPER_ASSIGNMENTS,
                   updated=WRAPPER_UPDATES):
    for attr in assigned:
        try:
            value = getattr(wrapped, attr)
        except AttributeError:
            pass
        else:
            setattr(wrapper, attr, value)
    for attr in updated:
        getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
    # Issue #17482: set __wrapped__ last so we don't inadvertently copy it
    # from the wrapped function when updating __dict__
    wrapper.__wrapped__ = wrapped
    # Return the wrapper so this can be used as a decorator via partial()
    return wrapper

debug可知,下述逻辑,是把wrapped原方法(也就是@wrapsNew装饰器的方法参数,这里是add方法)的一些assigned属性获取到(即’__module__‘, ‘__name__’, ‘__qualname__’,’__doc__', ‘__annotations__’),然后将结果赋值给wrapper方法(也就是@wrapsNew装饰的方法inner):

__module__属性:

在这里插入图片描述

__name__属性:

在这里插入图片描述

__qualname__属性:

在这里插入图片描述

__doc__属性:

在这里插入图片描述

也就是add方法的doc:

在这里插入图片描述

__annotations__属性:

在这里插入图片描述

然后针对@wrapsNew装饰的方法inner的__dict__属性进行更新:

inner的__dict__属性:

在这里插入图片描述

add的__dict__属性:

在这里插入图片描述

for attr in updated:
    getattr(wrapper, attr).update(getattr(wrapped, attr, {}))

将inner方法的属性更新部分为wrapped方法的属性后,最后将wrapper(inner)方法的__wrapped__属性设置为wrapped(add)方法:

# Issue #17482: set __wrapped__ last so we don't inadvertently copy it
# from the wrapped function when updating __dict__
wrapper.__wrapped__ = wrapped

最后返回wrapper(inner)方法:

# Return the wrapper so this can be used as a decorator via partial()
return wrapper

到此全部分析已完成接下来就是执行如下逻辑,func就是wrapped方法,add:

def add(a, b):
    """
    求和运算
    """
    print(a + b)
def inner(*args, **kwargs):
    print(f"before...")
    func(*args, **kwargs)
    print("after...")

最终执行结果如下:

在这里插入图片描述

上述可知,原方法我们也可以获取并执行,通过装饰后方法.__wrapped__获取到原方法并执行:

print(add.__wrapped__(2, 8))

结果:

10
None

同时,装饰后的add方法的一些属性,也被更新为原方法add的属性,这样更加贴合原有方法的语义:

print(add.__module__)
print(add.__name__)
print(add.__qualname__)
print(add.__doc__)
print(add.__annotations__)
print(add.__dict__)

结果如下:

在这里插入图片描述

若为add方法增加annotation信息(注意:python的annotation仅提示作用,不是强制类型限制,且和java的注解Annotation是完全不同,反编译可知java的注解Annotation是继承了Annotation接口的特殊接口),如下为add原方法的参数和返回值添加注解annotation:

@outer
def add(a: int, b: int) -> int:
    """
    求和运算
    """
    print(a + b)
    return a + b

执行如下:

在这里插入图片描述

结果如下,可见inner方法的annotation注解信息(inner方法实际是没有注解信息的),实际是使用的add方法的注解元信息:

在这里插入图片描述

获取的注解信息如下:

在这里插入图片描述

结果:

在这里插入图片描述

或者上述的annotation注解信息获取,可以使用inspect模块中的Signature对象,对可调用对象进行内省

在这里插入图片描述

import inspect

signature = inspect.signature(add)
print(signature.parameters["a"])
print(signature.parameters["a"].annotation)
print(signature.parameters["a"].annotation == int)
print(signature.parameters["a"].annotation is int)

结果和上述类似:

在这里插入图片描述

再针对多个装饰器举个栗子

import functools


def decorator1(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('decorator 1 begin.')
        return func(*args, **kwargs)

    return wrapper


def decorator2(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('decorator 2 begin.')
        return func(*args, **kwargs)

    return wrapper


@decorator1
@decorator2
def add(x: int, y: int) -> int:
    print("开始累加:")
    print(x + y)


if __name__ == '__main__':
    print("1**********\n")
    add(1, 2)
    print("2**********\n")
    add.__wrapped__(1, 2)
    print("3**********\n")
    add.__wrapped__.__wrapped__(1, 2)

执行结果如下:

在这里插入图片描述

注意分析如下代码:

@decorator1
@decorator2
def add(x: int, y: int) -> int:
    print("开始累加:")
    print(x + y)

其本质如下:

decorator1 = decorator1(add1)

注意这里的add1是下面的add2,而add2其实是decorator2(add):

add2 = decorator2(add)

所以公式如下:

add = decorator1(decorator2(add))

所以执行完,一定是decorator1先打印,再是decorator2打印。同时基于此,由上述我们可知,add可以多次调用__wrapped__方法。

add方法调用一次__wrapped__时,实际返回的是decorator1的包裹原方法,所以原方法是decorator2(add),故而执行可见,打印了:decorator 2 begin.语句。

而add方法连续调用两次__wrapped__时,实际返回的decorator2(add)的包裹原方法,也就是我们自定义的原方法add,故而decorator 1 begin.和decorator 2 begin.语句均未打印。

小结:

1)functools.wraps 旨在消除装饰器对原函数造成的影响,即对原函数的相关属性进行拷贝,以达到装饰器不修改原函数的目的(保存函数的元数据,更加符合原方法语义、属性)。

2)wraps内部通过partial对象和update_wrapper函数实现。

3)partial是一个类,核心通过实现__new__和__call__方法,自定义实例化对象过程,使得对象内部保留原函数和固定参数,__call__方法使得对象可以像函数一样被调用,再通过内部保留的原函数和固定参数以及传入的其它参数进行原函数调用,wraps函数实现返回partial类实例,使得其可视作装饰器使用。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

No module named ‘sklearn.metrics.ranking‘ 解决方法

错误代码 from sklearn.metrics.classification import * from sklearn.metrics.ranking import * 错误原因 sklearn这个文件夹下的_classification和_ranking前面有下划线! 解决方法 第一步:找到sklearn位置,可以打开命令行输入 pip sh…

ASTM通信协议校验和计算方法

Lis通信接口开发 <STX> FN <Frame> <ETB>or<ETX> <CS><CR> <LF> 其中&#xff1a; <STX>&#xff1a;起始帧头&#xff08;0x02&#xff09; FN&#xff1a;帧号&#xff08;范围0&#xff5e;7&#xff0c;1&#xff5e;7完…

软考--试题六--抽象工厂模式(Abstract Factory)

抽象工厂模式(Abstract Factory) 意图 提供一个创建一系列相关或相互依赖对象的接口&#xff0c;而无须指定他们具体的类 结构 适用性 1、一个系统要独立于它的产品的创建、组合和表示时 2、一个系统要由多个产品系统中的一个来配置时 3、当要强调一系列相关的产品对象的设…

问界新M5交付,「975」组合站稳中国豪华智电定位

‍作者 |老缅 编辑 |德新 5月15日&#xff0c;问界新M5已正式开启全国用户交付。从网传图片可以看到&#xff0c;华为余承东以及赛力斯AITO问界BU总裁何利扬亲自出席了首批交车仪式。 4月23日&#xff0c;在不到1个月前&#xff0c;新M5发布。新M5共推出三款车型&#xff1a; …

基于ASN.1的RSA算法公私钥存储格式解读

1.概述 RFC5958主要定义非对称密钥的封装语法&#xff0c;RFC5958用于替代RFC5208。非对称算法会涉及到1对公私钥&#xff0c;例如按照RSA算法&#xff0c;公钥是n和e&#xff0c;私钥是d和n。当需要将公私钥保存到文件时&#xff0c;需按照一定的格式保存。本文主要定义公私钥…

leetcode刷题(6):二叉树的使用

文章目录 104. 二叉树的最大深度解题思路c 实现 94. 二叉树的中序遍历解题思路c 实现 101. 对称二叉树解题思路c 实现 96. 不同的二叉搜索树解题思路c 实现 102. 二叉树的层序遍历解题思路c 实现 104. 二叉树的最大深度 题目: 给定一个二叉树 root &#xff0c;返回其最大深度…

一文读懂deepSpeed:深度学习训练的并行化

引言 在深度学习领域&#xff0c;模型训练的过程不仅资源密集&#xff0c;而且技术复杂。近年来&#xff0c;随着模型规模和数据量的不断增长&#xff0c;深度学习训练面临着越来越多的挑战。这些挑战主要体现在计算资源的需求、训练效率、模型复杂度以及内存管理等多个方面。…

postgres 修改系统时间测试

修改系统时间 [rootmmsql01 ~]# date 2024年 05月 16日 星期四 13:07:02 CST [rootmmsql01 ~]# timedatectl set-time "2024-05-16 13:30:00" [rootmmsql01 ~]# date 2024年 05月 16日 星期四 13:30:03 CST [rootmmsql01 ~]# timedatectl set-time "2024-05-16…

基于QEMU-aarch64学习UEFI(EDK2)-2安装操作系统

1 基于QEMU-aarch64学习UEFI(EDK2)-2安装操作系统 文章目录 1 基于QEMU-aarch64学习UEFI(EDK2)-2安装操作系统1.1 二、基于qemu固件安装操作系统1.1.1 1、virt-manager安装1.1.2 2、创建虚拟机1.1.2.1 Ubuntu系统开机等待时间长问题解决 1.1.3 3、virt-manager日常使用1.1.4 4、…

GAN实例基于神经网络

目录 1.前言 2.实验 1.前言 需要了解GAN的原理查看对抗生成网络&#xff08;GAN&#xff09;&#xff0c;DCGAN原理。 采用手写数字识别数据集 2.实验 import argparse import os import numpy as np import mathimport torchvision.transforms as transforms from torchvi…

怎么把照片变小做头像?多种方法教你图片改尺寸

现在在社交媒体平台或者是社交软件上&#xff0c;我们经常会去更改头像来展示自己&#xff0c;但是有时候我们拍摄的照片太大无法直接用作头像&#xff0c;这时候就需要去修改图片尺寸&#xff0c;将图片改大小到合适的数值才能使用&#xff0c;那么如何快速的将图片改大小呢&a…

在UBuntu上安装QT环境

一、UBuntu环境 二、官网下载QT https://download.qt.io/archive/qt/ 安装所需选择版本下载&#xff0c;可以现在windows下载在复制进去 三、安装QT 1、复制到ubuntu 2、打开终端&#xff0c;改变刚下载文件的权限 权限代号 r&#xff1a;读取权限&#xff0c;数字代号为 “…

手机图片恢复不求人:手动找回丢失的照片!

无论是外出旅行、聚会还是日常点滴&#xff0c;我们总是习惯用手机记录下来&#xff0c;让美好的瞬间定格在一张张照片中。然而&#xff0c;有时因为误删、清空缓存或是更换手机&#xff0c;那些珍贵的照片突然消失了。手机图片恢复有什么简单易行、容易上手的方法吗&#xff1…

MySQL创建存储过程函数(2)

DDL CREATE TABLE student (id int(11) NOT NULL AUTO_INCREMENT COMMENT 学号,createDate datetime DEFAULT NULL,userName varchar(20) DEFAULT NULL,pwd varchar(36) DEFAULT NULL,phone varchar(11) DEFAULT NULL,age tinyint(3) DEFAULT NULL,sex char(2) DEFAULT NULL,i…

上海初中生古诗文大会倒计时4个月:单选题真题示例和独家解析

现在距离2024年初中生古诗文大会还有4个多月时间&#xff0c;备考要趁早&#xff0c;因为知识点还是相对比较多的。这些知识点对于初中语文的学习也是很有帮助的。 今天我们继续来看10道选择题真题和详细解析&#xff0c;以下题目截取自我独家制作的在线真题集&#xff0c;都是…

教你五行代码实现大批量文件重命名

问题背景 文件夹里的大量文件&#xff0c;命名很乱&#xff0c;并且要重新命名为固定长度顺序的文件很麻烦。这里采用5行python实现大批量文件按要求统一命名。 现有文件夹列表 tulips 代码实现 main.py import os path rtulips/ for num, file in enumerate(os.listdir(…

狙击策略专用术语以及含义,WeTrade3秒讲解

想必各位交易高手对狙击策略不会陌生吧!但你想必不知道狙击策略的开发者为了推广狙击策略&#xff0c;在狙击策略基础的经典技术分析理论引入了自己的术语。今天WeTrade众汇和各位投资者继续了解狙击策略专用术语以及含义。 一.BL 银行级别(BL)是前一日线收盘的级别。时间是格…

央视的AI动画《AI我中华》宣传视频,原来用AI工具Stable Diffusion制作,竟然这么简单?

大家好&#xff0c;我是向阳。 前段时间&#xff0c;央视的《爱我中华》AI宣传短片火爆全网&#xff0c;有一个穿越转场效果非常惊艳&#xff01;先来回顾回顾&#xff1a; 今天就先来详细讲解&#xff0c;如何利用Stable Diffusion制作这样的穿越转场视频。 如你还没有安装St…

脑中风也会出现眩晕?快速识别中风,一定要牢记这些!

眩晕是许多人都会经历的不适感&#xff0c;发作时仿佛整个世界都在旋转&#xff0c;可能还伴随着站立不稳、脚步虚浮、恶心等症状。然而&#xff0c;你可能不知道的是&#xff0c;这些症状在某些情况下可能是脑中风的前兆。如果不及时关注并采取相应措施&#xff0c;一旦发展为…

linux ndk编译搭建测试

一、ndk下载 NDK 下载 | Android NDK | Android Developers 二、ndk环境变量配置 ndk解压&#xff1a; unzip android-ndk-r26d-linux.zip 环境变量配置&#xff1a; export NDK_HOME/rd/own/test/android-ndk-r26d/ export PATH$PATH:$NDK_HOME 三、编译测试验证 …