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观察广播,在没连接的时候,点击一下࿰…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...
无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...
智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...
