python测试开发面试常考题:装饰器
目录
简介
应用
第一类对象
装饰器
描述器descriptor
资料获取方法
简介
Python 装饰器是一个可调用的(函数、方法或类),它获得一个函数对象 func_in 作为输入,并返回另一函数对象 func_out。它用于扩展函数、方法或类的行为。
装饰器模式通常用于扩展对象的功能。在日常生活中,这种扩展的例子有:在枪上加一个消音器,使用不同的相机镜头等等。

Django框架中有大量装饰器
- 限制某些HTTP请求对视图的访问
- 控制
- 按单个视图控制压缩
- 基于特定HTTP请求头控制缓存
Pyramid框架和Zope应用服务器也使用装饰器来实现各种目标。
- 将函数注册为事件订阅者
- 以特定权限保护一个方法
- 实现适配器模式
应用
装饰器模式在跨领域方面大放异彩:
- 数据验证
- 缓存
- 日志
- 监控
- 调试
- 业务规则
- 加密
使用修饰器模式的另一个常见例子是(Graphical User Interface,GUI)工具集。在GUI工具集中,我们希望能够将一些特性,比如边框、阴影、颜色以及滚屏,添加到组件/控件。
第一类对象
装饰器是Python中非常强大和有用的工具,它允许程序员修改函数或类的行为。装饰器允许我们封装另一个函数,以扩展被封装函数的行为,而不需要修改它。但在深入研究装饰器之前,让我们先了解一些概念,这些概念在学习装饰器时将会很有用。
在Python中,函数是第一类对象,这意味着 Python 中的函数可以作为参数使用或传递。
第一类函数的属性:
- 函数是对象类型的实例
- 可以将函数存储在变量
- 可以将函数作为参数传递给其他函数
- 可以从函数中返回函数。
- 可以将它们存储在数据结构中,如哈希表、列表、...
例1:将函数视为对象。
def shout(text):return text.upper()print(shout('Hello'))yell = shoutprint(yell('Hello'))
输出:
HELLO
HELLO
例2:将函数作为参数传递
def shout(text):return text.upper()def whisper(text):return text.lower()def greet(func):# storing the function in a variablegreeting = func("""Hi, I am created by a function passed as an argument.""")print (greeting)greet(shout)
greet(whisper)
输出:
HI, I AM CREATED BY A FUNCTION PASSED AS AN ARGUMENT.
hi, i am created by a function passed as an argument.
例3: 从函数中返回函数。
def shout(text):return text.upper()def whisper(text):return text.lower()def greet(func):# storing the function in a variablegreeting = func("""Hi, I am created by a function passed as an argument.""")print (greeting)greet(shout)
greet(whisper)
输出:
25
装饰器
如上所述,装饰器是用来修改函数或类的行为的。在装饰器中,函数被当作函数的参数,然后在封装函数中调用。
- 装饰器的语法:
@gfg_decorator
def hello_decorator():print("Gfg")'''Above code is equivalent to -def hello_decorator():print("Gfg")hello_decorator = gfg_decorator(hello_decorator)'''
gfg_decorator 是一个可调用的函数,它将在另一个可调用的函数hello_decorator函数上面添加一些代码,并返回封装函数。
- 装饰器可以修改行为:
# defining a decorator
def hello_decorator(func):# inner1 is a Wrapper function in# which the argument is called# inner function can access the outer local# functions like in this case "func"def inner1():print("Hello, this is before function execution")# calling the actual function now# inside the wrapper function.func()print("This is after function execution")return inner1# defining a function, to be called inside wrapper
def function_to_be_used():print("This is inside the function !!")# passing 'function_to_be_used' inside the
# decorator to control its behaviour
function_to_be_used = hello_decorator(function_to_be_used)# calling the function
function_to_be_used()
输出:
Hello, this is before function execution
This is inside the function !!
This is after function execution
让我们跳到另一个例子,在这个例子中,我们可以用装饰器轻松地找出函数的执行时间。
import time
import math
import functools# decorator to calculate duration
# taken by any function.
def calculate_time(func):# added arguments inside the inner1,# if function takes any arguments,# can be added like this.@functools.wraps(func) # 支持内省,一般可以不用,多用于文档def inner1(*args, **kwargs):# storing time before function executionbegin = time.time()func(*args, **kwargs)# storing time after function executionend = time.time()print("Total time taken in : ", func.__name__, end - begin)return inner1# this can be added to any function present,
# in this case to calculate a factorial
@calculate_time
def factorial(num):# sleep 2 seconds because it takes very less time# so that you can see the actual differencetime.sleep(2)print(math.factorial(num))# calling the function.
factorial(10)
@functools.wraps装饰器使用函数functools.update_wrapper()来更新特殊属性,如__name__和__doc__,这些属性在自省中使用。
输出:
3628800
Total time taken in : factorial 2.0061802864074707
- 如果函数有返回或有参数传递给函数,怎么办?
在上面所有的例子中,函数都没有返回任何东西,所以没有问题,但人们可能需要返回的值。
def hello_decorator(func):def inner1(*args, **kwargs):print("before Execution")# getting the returned valuereturned_value = func(*args, **kwargs)print("after Execution")# returning the value to the original framereturn returned_valuereturn inner1# adding decorator to the function
@hello_decorator
def sum_two_numbers(a, b):print("Inside the function")return a + ba, b = 1, 2# getting the value through return of the function
print("Sum =", sum_two_numbers(a, b))
输出:
before Execution
Inside the function
after Execution
Sum = 3
内部函数接收的参数是*args和**kwargs,这意味着可以传递任何长度的位置参数的元组或关键字参数的字典。这使得它成为通用的装饰器,可以装饰具有任何数量参数的函数。
- 链式装饰器
链式装饰器是指用多个装饰器来装饰函数。
# code for testing decorator chaining
def decor1(func):def inner():x = func()return x * xreturn innerdef decor(func):def inner():x = func()return 2 * xreturn inner@decor1
@decor
def num():return 10@decor
@decor1
def num2():return 10print(num())
print(num2())
输出
400
200
上面的例子类似于调用函数---
decor1(decor(num))
decor(decor1(num2))
一些常用的装饰器在 Python 中甚至是内建的,它们是 @classmethod, @staticmethod, 和 @property。@classmethod 和 @staticmethod 装饰器用于定义类命名空间中的方法,这些方法与该类的特定实例没有关系。@property装饰器是用来定制类属性的getters和setters的。
- 类装饰器
在 Python 3.7 中的新的 dataclasses 模块中完成:
from decorators import debug, do_twice@debug
@do_twice
def greet(name):print(f"Hello {name}")
语法的含义与函数装饰器相似。你可以通过写PlayingCard = dataclass(PlayingCard)来进行装饰。
类装饰器的一个常见用途是作为元类的一些使用情况的更简单的替代。
编写一个类装饰器与编写一个函数装饰器非常相似。唯一的区别是,装饰器将接收类而不是函数作为参数。事实上,你在上面看到的所有装饰器都可以作为类装饰器工作。
- 带参数与不带参数的装饰器
def repeat(_func=None, *, num_times=2):def decorator_repeat(func):@functools.wraps(func)def wrapper_repeat(*args, **kwargs):for _ in range(num_times):value = func(*args, **kwargs)return valuereturn wrapper_repeatif _func is None:return decorator_repeatelse:return decorator_repeat(_func)
使用functools.partial也可达到类似效果。
以下是slowdown的演进版本
import functools
import timedef slow_down(_func=None, *, rate=1):"""Sleep given amount of seconds before calling the function"""def decorator_slow_down(func):@functools.wraps(func)def wrapper_slow_down(*args, **kwargs):time.sleep(rate)return func(*args, **kwargs)return wrapper_slow_downif _func is None:return decorator_slow_downelse:return decorator_slow_down(_func)
- 有状态的装饰器
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!")
对函数的调用次数--存储在包装函数的函数属性 .num_calls 中。下面是使用它的效果:
>>> say_whee()
Call 1 of 'say_whee'
Whee!>>> say_whee()
Call 2 of 'say_whee'
Whee!>>> say_whee.num_calls
2
维护状态的典型方法是使用类装饰器。
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!")
- 单例模式
单例是只有一个实例的类。比如 None、True 和 False,可以使用 is 关键字来比较 None。
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
如你所见,这个类装饰器与我们的函数装饰器遵循相同的模板。唯一的区别是,我们使用 cls 而不是 func 作为参数名,以表明它是类装饰器。
让我们看看它是否有效:
>>> first_one = TheOne()
>>> another_one = TheOne()>>> id(first_one)
140094218762280>>> id(another_one)
140094218762280>>> first_one is another_one
True
注意:在Python中,单例其实并不像其他语言那样经常使用,通常用全局变量来实现更好。
- 缓存返回值
装饰器可以为缓存和备忘提供一个很好的机制。作为一个例子,让我们看一下斐波那契数列的递归定义:
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)
在标准库中,最近使用最少的缓存(LRU)可作为 @functools.lru_cache。
这个装饰器比你上面看到的那个有更多的功能。你应该使用@functools.lru_cache而不是写你自己的缓存装饰器:
import functools@functools.lru_cache(maxsize=4)
def fibonacci(num):print(f"Calculating fibonacci({num})")if num < 2:return numreturn fibonacci(num - 1) + fibonacci(num - 2)
maxsize参数指定了多少个最近的调用被缓存。默认值是128,但你可以指定maxsize=None来缓存所有函数调用。然而,要注意的是,如果你要缓存许多大的对象,这可能会导致内存问题。
描述器descriptor
任何定义了 __get__(), __set__() 或 __delete__() 方法的对象。当类属性为描述器时,它的特殊绑定行为就会在属性查找时被触发。通常情况下,使用 a.b 来获取、设置或删除属性时会在 a 的类字典中查找名称为 b 的对象,但如果 b 是描述器,则会调用对应的描述器方法。理解描述器的概念是更深层次理解 Python 的关键,因为这是许多重要特性的基础,包括函数、方法、属性、类方法、静态方法以及对超类的引用等等。
有关描述符的方法的详情可参看 实现描述器。
class property(fget=None, fset=None, fdel=None, doc=None)
fget 是获取属性值的函数。 fset 是用于设置属性值的函数。 fdel 是用于删除属性值的函数。并且 doc 为属性对象创建文档字符串。
class C():def __init__(self):self._x = Nonedef getx(self):return self._xdef setx(self, value):self._x = valuedef delx(self):del self._xx = property(getx, setx, delx, "I'm the 'x' property.")demo = C()
demo.x = 5
print(demo.x)
print(demo.getx())
执行结果
5
5
更快捷的方式:
class C():def __init__(self):self._x = None@propertydef x(self):"""I'm the 'x' property."""return self._x@x.setterdef x(self, value):self._x = value@x.deleterdef x(self):del self._xdemo = C()
demo.x = 5
print(demo.x)
@property 装饰器会将 x() 方法转化为同名的只读属性的 "getter",并将 x的文档字符串设置为 "I'm the 'x' property."
执行结果
5
资料获取方法
【留言777】


各位想获取源码等教程资料的朋友请点赞 + 评论 + 收藏,三连!
三连之后我会在评论区挨个私信发给你们~
相关文章:
python测试开发面试常考题:装饰器
目录 简介 应用 第一类对象 装饰器 描述器descriptor 资料获取方法 简介 Python 装饰器是一个可调用的(函数、方法或类),它获得一个函数对象 func_in 作为输入,并返回另一函数对象 func_out。它用于扩展函数、方法或类的行为。 装饰器模式通常用…...
语音同声翻译软件让你不再为语言障碍困扰
从前有一个叫黄俊的小伙子,他有一个大梦想:环游世界!但是,他只会说中文,而去到外国又怎么跟当地人交流呢?为了实现自己的梦想,黄俊开始了寻找能帮他解决问题的捷径。这时,方娜向他介…...
又有一个手艺人震惊了B站用户
飞瓜数据(B站版)【热门视频榜】周榜显示,霸占全站视频流量第一的是来自UP主爱捣鼓的邢志磊发布的作品《我花了半年时间给猫做了个房子》。 视频在一周时间内新增播放1232.2万,新增点赞139.4万。 根据视频详细数据显示,…...
HDFS的设计目标和重要特性
HDFS的设计目标和重要特性 设计目标HDFS重要特性主从架构分块存储机制副本机制namespace元数据管理数据块存储 设计目标 硬件故障(Hardware Failure)是常态,HDFS可能有成百上千的服务器组成,每一个组件都有可能出现故障。因此古见检测和自动快速恢复的H…...
【JMeter】JMeter添加插件
目录 一、前言 二、插件管理器 三、推荐插件 1.Custom Thread Groups (1)Ultmate Thread Group (2)Stepping Thread Group 2.3 Basic Graph 资料获取方法 一、前言 在我们的工作中,我们可以利用一些插件来帮…...
测牛学堂:车载测试面试总结之语音助手相关
车载语音助手的工作原理? 语音助手的工作原理总结下来可以分为4个步骤: 1 通过麦克风采集驾驶员的语音指令 2将语音信号转换为数字信号 3过语音识别技术将语音指令转换为计算机可以理解的指令 4 通过语音合成技术将计算机的回应转换为语音输出 车载…...
Android开发之Fragment动态添加与管理
文章目录 主界面布局资源两个工具Fragment主程序 主界面布局资源 在activity_main.xml中,声明两个按钮备用,再加入一个帧布局,待会儿用来展示Fragment。 <?xml version"1.0" encoding"utf-8"?> <LinearLayo…...
Python爬虫+数据可视化:分析唯品会商品数据
目录 前言数据来源分析1. 明确需求2. 抓包分析:通过浏览器自带工具: 开发者工具 代码实现步骤: 发送请求 -> 获取数据 -> 解析数据 -> 保存数据发送请求解析数据保存数据 数据可视化先读取数据泳衣商品性别占比商品品牌分布占比各大品牌商品售价平均价格各…...
el-tree数据渲染超出省略
el-tree数据渲染超出省略 问题 <el-tree:data"deptOptions":props"defaultProps":expand-on-click-node"false":filter-node-method"filterNode"ref"tree"default-expand-allhighlight-currentnode-click"handleNo…...
若依vue -【 44】
44 服务监控讲解 1 需求 显示CPU、内存、服务器信息、Java虚拟机信息、磁盘状态的信息 2 前端 RuoYi-Vue\ruoyi-ui\src\views\monitor\server\index.vue <script> import { getServer } from "/api/monitor/server";export default {name: "Server&quo…...
React 基础篇(一)
💻 React 基础篇(一)🏠专栏:React 👀个人主页:繁星学编程🍁 🧑个人简介:一个不断提高自我的平凡人🚀 🔊分享方向:目前主攻…...
Bean 的作用域和生命周期
目录 什么是 Bean 的作用域 ?Bean 的六种作用域Spring 的执行流程Bean 的生命周期 什么是 Bean 的作用域 ? Bean 的作⽤域是指 Bean 在 Spring 整个框架中的某种⾏为模式,⽐如 singleton 单例作⽤域,就表示 Bean 在整个 Spring 中只有⼀份,…...
STP和MTP(第二十二课)
2、如何实现 1)在MSTP网络种,引入了域的概念,称为MST域 2)每一个MST域中包含一个或多个“生成树”称为“实例” 3)每个“实例生成树”都可以绑定vlan,实现vlan数据流的负载分担/负载均衡 4)默认情况下,所有的vlan都属于“实例树0:即:instance 0” 5)不同的“实例…...
Java-WebSocket
请点击下面工程名称,跳转到代码的仓库页面,将工程 下载下来 Demo Code 里有详细的注释 TestWebSocket...
elementui的el-date-picker选择日期范围第二个不能早于第一个
选择日期范围第二个不能早于第一个 <el-form-item label"预计施工时间:" required><el-form:model"form":rules"constructionDateRules"ref"constructionRef"inline:hide-required-asterisk"false"><el-form…...
【NLP】无服务器问答系统
一、说明 在NLP的眼见的应用,就是在“ 当你在谷歌上提出一个问题并立即得到答案时会发生什么?例如,如果我们在谷歌搜索中询问谁是美国总统,我们会得到以下回答:Joe Biden;这是一个搜索问题,同时…...
Dubbo
Dubbo 简介Dubbo的快速入门Dubbo的基本架构安装DubboAdmin入门案例Dubbo的最佳实践 Dubbo的高级特性启动检查多版本超时与重试负载均衡SpringCloud整合Dubbo案例 简介 Dubbo是阿里巴巴公司开源的一个高性能、轻量级的Java RPC框架。 致力于提高性能和透明化的RPC远程服务调用方…...
Java设计模式之策略(Strategy)模式
策略(Strategy)设计模式定义了一系列算法,将它们封装起来,并且可以相互替换使用,从而使得算法可以独立于使用它的客户而变化。 什么是策略模式 策略(Strategy)设计模式是一种行为型设计模式&a…...
Vue引入CDN JS或本地JS文件之后 使用报错
加载问题 正常情况 在public引入script - js文件加载 - 写入内存 - 使用 但使用之前 有可能这个文件还没执行写入内存或者还未加载完毕 此时 需要一个promiss解决 1. 引入script 在 public / index.html 文件内引入你的script标签 <script type"text/javascript"…...
NRF52832-扩展广播
nordic论坛 我想要设置广播名称为 “一二三四五”,当广播名称为FULL_NAME时,但是广播显示还是“一?”,“?”是乱码,后来打开nrf connect观察广播,在没连接的时候,点击一下࿰…...
业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...
前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...
实战三:开发网页端界面完成黑白视频转为彩色视频
一、需求描述 设计一个简单的视频上色应用,用户可以通过网页界面上传黑白视频,系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观,不需要了解技术细节。 效果图 二、实现思路 总体思路: 用户通过Gradio界面上…...
mac:大模型系列测试
0 MAC 前几天经过学生优惠以及国补17K入手了mac studio,然后这两天亲自测试其模型行运用能力如何,是否支持微调、推理速度等能力。下面进入正文。 1 mac 与 unsloth 按照下面的进行安装以及测试,是可以跑通文章里面的代码。训练速度也是很快的。 注意…...
es6+和css3新增的特性有哪些
一:ECMAScript 新特性(ES6) ES6 (2015) - 革命性更新 1,记住的方法,从一个方法里面用到了哪些技术 1,let /const块级作用域声明2,**默认参数**:函数参数可以设置默认值。3&#x…...
