Python高阶技巧(十二)

news2024/11/15 22:31:21

python学习之旅(十二)
👍查看更多可以关注查看首页或点击下方专栏目录

一.闭包

可以保存函数内变量,不会随着函数调用完而销毁

(1) 基本定义

  • 在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包。

  • 使用示例:

# 1.在函数嵌套(函数中定义函数)的前提下
def func_out(num1):
    def func_inner(num2):
        # 2.内部函数使用了外部函数的变量
        num = num1 + num2
        print(f"num的值为:{num}")

    # 3.外部函数返回了内部函数
    return func_inner

# 创建闭包实例
f = func_out(10)
# 执行闭包
f(6) # 打印 num的值为:16

(2) 修改外部函数变量的值

  • 在闭包函数(内部函数中)想要修改外部函数的变量值,必须用nonlocal声明这个外部变量
# 1.在函数嵌套(函数中定义函数)的前提下
def func_out(num1):
    def func_inner(num2):
        # 声明外部变量
        nonlocal num1
        # 2.内部函数使用了外部函数的变量
        num1 += num2
        print(f"num1的值为:{num1}")

    # 3.外部函数返回了内部函数
    return func_inner


# 创建闭包实例
f = func_out(10)
# 执行闭包
f(8)  # 打印 num的值为:18

(3) 小结

  • 优点:

    • 无需定义全局变量即可实现通过函数,持续的访问、修改某个值
    • 闭包使用的变量于所在的函数内,难以被错误的调用修改,可使变量更安全不易被恶意行为修改
  • 缺点:

    • 由于内部函数持续引用外部函数的值,所以会导致这一部分内存空间不被释放,一直占用内存(额外的内存占用)

二.装饰器

也是一种闭包,可在不破坏目标函数原有的代码和功能的前提下,为目标函数增加新功能

(1) 基本使用

  • 装饰器就是把一个函数当做参数传递给闭包中的外部函数,同时在内部函数中使用这个函数,并给他添加新的功能。
  • 外部函数只能有一个参数,往往是被装饰的函数
  • 内部函数可以根据被装饰的函数提供多个参数以及返回值
# 定义一个装饰器
def remind(func):
    # 为目标函数增加新功能
    def inner():
        print("我睡觉了")
        func()
        print("我起床了")

    return inner


# 需要被装饰的函数
def sleep():
    import random
    import time
    print("睡眠中...")
    time.sleep(random.randint(1, 5))
    
# 未装饰
sleep()
# 打印
# 睡眠中...

# 使用装饰器装饰函数(增加睡前起床提醒)
# 返回增强后的inner函数
fn = remind(sleep)
fn()
# 打印
# 我睡觉了
# 睡眠中...
# 我起床了

(2) 语法糖使用

  • 可直接在需要被装饰的函数上加@装饰器名字,解释器碰到时会自动执行装饰过程,简化使用流程
# 定义一个装饰器
def remind(func):
    def inner():
        print("我睡觉了")
        func()
        print("我起床了")

    return inner


# 需要被装饰的函数
# 解释器遇到@remind 会立即执行 sleep = remind(sleep)
@remind
def sleep():
    import random
    import time
    print("睡眠中...")
    time.sleep(random.randint(1, 5))

# 通过语法糖注解,直接调用即可达到效果
sleep()
# 打印
# 我睡觉了
# 睡眠中...
# 我起床了

(3) 多个装饰器使用

  • 将装饰器都写在需要被装饰的函数上面即可
  • 谁离被装饰的函数最近,谁就先去装饰函数
# 定义装饰器1
def remind(func):
    def inner():
        print("我睡觉了")
        func()
        print("我起床了")

    return inner


# 定义装饰器2
def study(func):
    def inner():
        func()
        print("我要敲代码啦")

    return inner


# 谁近谁先装饰
@study   # 2.执行 sleep = study(remind(sleep))
@remind  # 1.执行 sleep = remind(sleep)
def sleep():
    import random
    import time
    print("睡眠中...")
    time.sleep(random.randint(1, 5))


sleep()
# 打印
# 我睡觉了
# 睡眠中...
# 我起床了
# 我要敲代码啦

(4) 带参数的装饰器

  • 需要再增加一层函数嵌套来接收传递的参数
# 第一层:用于接收装饰器传递的参数
def logging(flag):
    # 第二层:外部函数用于接收待装饰函数
    def decorator(fn):
        # 第三层:内部函数用于装饰接收的函数
        def inner(num1, num2):
            # 使用参数
            if flag == "+":
                print(">正在进行加法运算<")
            elif flag == "-":
                print(">正在进行减法运算<")
            result = fn(num1, num2)
            return result
		
        return inner

    # 返回装饰器
    return decorator


# 被带有参数的装饰器装饰的函数
@logging('+')
def add(a, b):
    result = a + b
    return result


result = add(1, 3)
print(result)

(5) 类装饰器(了解即可)

  • 一个类里面一旦实现了__call__方法,那么这个类创建的对象就是一个可调用对象,可以像调用函数一样进行调用
# 定义类
class Login:
    def __call__(self, *args, **kwargs):
        print("登录中。。。")

# 创建实例
login = Login()
# 如函数般调用
login()  # 打印 登录中。。。
  • 类装饰器装饰函数的功能通过call方法实现
# 定义类装饰器
class Check:
    # 接收待装饰的函数
    def __init__(self, fn):   # fn = comment
        self.__fn = fn
	
    def __call__(self, *args: object, **kwargs: object) -> object:
        print("登录")
        self.__fn()   # comment()


# 被装饰的函数
@Check  # comment = Check(comment)
def comment():
    print("发表评论")


comment()

三.property属性

把类中的一个方法当作属性进行使用,简化开发

  • 例如我们如果想获取和修改私有属性必须通过类方法修改,示例代码:

    class Person:
        def __init__(self):
            self.__age = 18
    
        def age(self):
            return self.__age
    
        def set_age(self, new_age):
            self.__age = new_age
    
    
    p = Person()
    age = p.age()
    print(f"修改前年龄是:{age}") # 打印 修改前年龄是:18
    p.set_age(66)
    age = p.age()
    print(f"修改后年龄是:{age}")  # 打印 修改后年龄是:66
    
  • 通过使用如下两种方式可简化上述代码的使用

(1) 装饰器方式使用

  • @property表示把方法当作属性使用,表示当获取属性时执行下面修饰的方法
    • property修饰的方法名要与属性名一样
  • @方法名.setter表示把方法当作属性使用,表示当设置属性值时会执行下面修饰的方法
class Person:
    def __init__(self):
        self.__age = 18

    @property
    def age(self):
        return self.__age

    @age.setter
    def age(self, new_age):
        self.__age = new_age


p = Person()
# 可直接通过对象.属性使用
print(f"修改前年龄是:{p.age}") # 打印 修改前年龄是:18
p.age = 66
print(f"修改后年龄是:{p.age}") # 打印 修改后年龄是:66

(2) 类属性方式使用

  • property的参数说明:
    • 属性名 = property(获取值方法,设置值方法)
    • 第一个参数:获取属性时要执行的方法
    • 第二个参数:设置属性时要执行的方法
class Person:
    def __init__(self):
        self.__age = 18

    def get_age(self):
        return self.__age

    def set_age(self, new_age):
        self.__age = new_age

    # 类属性方式的property属性
    age = property(get_age, set_age)


p = Person()
print(f"修改前年龄是:{p.age}") # 打印 修改前年龄是:18
p.age = 66
print(f"修改后年龄是:{p.age}") # 打印 修改后年龄是:66

四.上下文管理器

由实现了__enter__()__exit__()方法的类创建的对象

  • 在文件操作篇提到过使用with语句可以自动调用关闭文件操作,即使出现异常也会自动调用关闭文件操作。
with open("guanzhi.txt", "w") as f:
    f.write("hello world")
  • 使用with语句简化操作是建立在上下文管理器上的,open函数创建的f文件对象就是一个上下文管理器对象

  • __enter表示上文方法,需要返回一个操作文件对象

  • __exit__表示下文方法,with语句执行完成会自动执行,即使出现异常也会执行该方法

# 定义一个File类
class File:
    def __init__(self, file_name, file_model):
        self.file_name = file_name
        self.file_model = file_model

    # 实现__enter__()和__exit__()方法
    def __enter__(self):
        print("这是上文")
        self.file = open(self.file_name, self.file_model)
        return self.file

    def __exit__(self, exc_type, exc_val, exc_tb):
        print("这是下文")
        self.file.close()


# 使用with语句来完成文件操作
with File("1.txt", "w") as f:
    f.write("hello world")

五.深拷贝浅拷贝

开辟新的内存空间接收变量

  • 调用id()可获得变量的内存地址

(1) 浅拷贝

(1.1) 可变类型浅拷贝

请添加图片描述

  • 使用copy函数进行浅拷贝,只对可变类型的第一层对象进行拷贝
    • 对拷贝的对象开辟新的内存空间进行存储
    • 不会拷贝对象内部的子对象
import copy

a = [1, 2, 3]
b = [11, 22, 33]
c = [a, b]
# 普通赋值,指向同一空间
d = c
print(f"c内存地址:{id(c)}")  # 打印 c内存地址:2265505547072
print(f"d内存地址:{id(d)}")  # 打印 d内存地址:2265505547072


a = [1, 2, 3]
b = [11, 22, 33]
c = [a, b]
# 浅拷贝,指向不同空间
d = copy.copy(c)
print(f"c内存地址:{id(c)}")  # 打印 c内存地址:2265505547648
print(f"d内存地址:{id(d)}")  # 打印 d内存地址:2265505548608

# 不会拷贝对象内部的子对象
print(id(a)) # 打印 2135734964288
print(id(c[0])) # 打印 2135734964288
print(id(d[0])) # 打印 2135734964288

(1.2) 不可变类型浅拷贝

  • 不可变类型进行浅拷贝不会给拷贝的对象开辟新的内存空间,只是拷贝了这个对象的引用
a = (1, 2, 3)
b = (11, 22, 33)
c = (a, b)
# 浅拷贝效果与普通赋值一样
d = c
e = copy.copy(c)
print(f"c内存地址:{id(c)}") # c内存地址:1536064302016
print(f"d内存地址:{id(d)}") # d内存地址:1536064302016
print(f"e内存地址:{id(e)}") # e内存地址:1536064302016

(2) 深拷贝

保障数据的独立性

请添加图片描述

(2.1) 可变类型深拷贝

  • 使用deepcopy函数进行深拷贝,会对可变类型内每一层可变类型对象进行拷贝,开辟新的内存空间进行存储

    import copy
    
    a = [1, 2, 3]
    b = [11, 22, 33]
    c = [a, b]
    
    d = copy.deepcopy(c)
    print(f"c内存地址:{id(c)}") # 打印 c内存地址:2603978212160
    print(f"d内存地址:{id(d)}") # 打印 d内存地址:2603978215488
    
    # 内部的可变类型也会拷贝
    print(id(a)) # 打印 2603978215104
    print(id(c[0])) # 打印 2603978215104
    print(id(d[0])) # 打印 2603978212992
    

(2.2) 不可变类型深拷贝

  • 不可变类型进行深拷贝不会给拷贝的对象开辟新的内存空间,只是拷贝了这个对象的引用
a = (1, 2, 3)
b = (11, 22, 33)
c = (a, b)

d = copy.deepcopy(c)
print(f"c内存地址:{id(c)}")  # 打印  c内存地址:1312354282432
print(f"e内存地址:{id(d)}")  # 打印  e内存地址:1312354282432

六.eval函数

  • eval()函数可将字符串当成有效的表达式求值并返回计算结果
# 基本的数学运算
res = eval("(1+9)*5")
print(res) 
# 打印 50

# 字符串重复
res = eval("'*'*10")
print(res) 
# 打印 **********

# 字符串转换成列表
print(type(eval("[1,2,3,4]")))
# 打印 <class 'list'>

# 字符串转成字典 
print(type(eval("{'name':'guanzhi','age':20}")))
# 打印 <class 'dict'>
  • 注意事项:开发时千万不要使用eval直接转换input的结果

    • 用户可能恶意输入有危害的终端指令
    input_str = input() # 输入 __import__('os').system('rm -rf /*')
    eval(input_str) # 直接运行可能导致主机崩溃
    
    # 等价于
    import os
    os.system("终端命令")
    

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

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

相关文章

万字长文,带你从0到1的了解商业智能BI

借助互联网技术的发展&#xff0c;每天我们都会接触到大量的信息&#xff0c;信息的增长速度可以说是海啸级的。在这样一个信息爆炸的时代&#xff0c;掌握怎样利用数据就相当于掌握了一项生存技能&#xff0c;很多可以发掘并充分利用数据的企业会发现自己远远领先于竞争对手。…

Android 反编译初探-基础篇

前言 本文目标&#xff1a; 工具&#xff1a;介绍反编译需要用到的工具原理&#xff1a;反编译基本原理实践&#xff1a;替换一个未混淆&未加固apk的启动页面 工具 1.Android Studio 版本&#xff1a;Android Studio Dolphin | 2021.3.1 Patch 1 2.Jadx Class Decomp…

go 数组(array)和切片(slice)

文章目录数组ArraySlice 切片appendcopy&#xff08;切片复制&#xff09;goto数组Array 和以往的数组有很大的不同 数组时值类型&#xff0c;复制和传参会复制整个数组&#xff0c;而不是指针数组长度必须是常量&#xff0c;且是类型的组成部分。[2]int和[3]int是不同的数据…

Vue中Vue.use()的原理及基本使用

目录 &#x1f525; 前言 1. 举例理解 2. 源码分析 &#x1f525; 小结 相信很多人在用Vue使用别人的组件时,会用到 Vue.use() ,例如&#xff1a;Vue.use(VueRouter)、Vue.use(MintUI)&#xff0c;这篇文章主要给大家介绍了关于Vue中Vue.use()的原理及基本使用的相关资料&a…

Mysql索据-Mysql的innodb引擎为什么要使用b+tree作为索引数据结构?

目录 索引&#xff1f; 什么是索引&#xff1f;索引有什么优点&#xff1f;索引有什么缺点&#xff1f; 索引的分类 按照功能分类&#xff1a; 按照数据结构分类 相关数据结构&#xff08;b-tree、btree&#xff09; b-tree btree b-tree和btree的区别 为什么Innodb要…

65. 锚框的代码实现

目标检测算法通常会在输入图像中采样大量的区域&#xff0c;然后判断这些区域中是否包含我们感兴趣的目标&#xff0c;并调整区域边界从而更准确地预测目标的真实边界框&#xff08;ground-truth bounding box&#xff09;。 不同的模型使用的区域采样方法可能不同。 这里我们…

TiDB学习笔记(八)-数据库故障处理

一、数据丢失快速恢复 数据恢复前置条件-GC&#xff0c;tidb_gc_life_time 查询GC已经清理的时间点tikv_gc_safe_point 数据快速恢复操作方式 DML->tidb_snapshot参数 &#xff08;在tikv_gc_safe_point范围内&#xff09; DDL->flashback table/recover table (flas…

AIGC与搜索深度融合,百度定义“生成式搜索”

设想一下&#xff0c;当你搜索“公司活动通知怎么写”时&#xff0c;搜索引擎直接“写”了一篇送到眼前是什么体验&#xff1f;百度的“生成式搜索”正在让这样的场景成为现实。日前&#xff0c;百度宣布&#xff0c;百度搜索将升级“生成式搜索”能力&#xff0c;基于百度自研…

项目管理工具dhtmlxGantt甘特图入门教程(七):在服务器上使用甘特图

dhtmlxGantt是用于跨浏览器和跨平台应用程序的功能齐全的Gantt图表&#xff0c;可满足项目管理控件应用程序的所有需求&#xff0c;是最完善的甘特图图表库。 这篇文章给大家讲解如何在服务器上使用DHTMLX Gantt 。 DhtmlxGantt正版试用下载&#xff08;qun&#xff1a;764…

Cadence PCB仿真使用Allegro PCB SI元器件类别设置为IO,IC和Discrete的方法图文教程

⏪《上一篇》   🏡《总目录》   ⏩《下一篇》 目录 1,概述2,配置方法3,总结1,概述 本文简单介绍使用Allegro PCB SI软件配置电压地网络电压的方法。 2,配置方法 第1步:打开待仿真的PCB文件,并确认软件为Allegro PCB SI 如果,打开软件不是Allegro PCB SI则可这样…

ElementUI源码系列一-完整引入和按需引入

前言 本篇将介绍&#xff0c;ElementUI 是如何实现完整引入和按需引入的。 完整引入 官网使用 源码步骤 src/index.js 通过对外暴露 install()&#xff0c;让主项目通过 Vue.use(ElementUI) 引入&#xff0c;还需单独引入样式 import element-ui/lib/theme-chalk/index.c…

Selenium用法详解【Options选项】【JAVA爬虫】

简介本文主要讲解如何使用java代码利用selenium控制浏览器的启动选项Options的代码操作教程。Options选项这是一个Chrome的参数对象&#xff0c;在此对象中使用addArgument()方法可以添加启动参数&#xff0c;添加完毕后可以在初始化Webdriver对象时将此Options对象传入&#x…

minio分布式存储的go语言开发衔接

minio是分布式存储&#xff0c;可集群部署&#xff0c;阵列磁盘&#xff0c;纠错码等大数据存储必备的技术。由于它是go语言开发的&#xff0c;我们用go来与它衔接&#xff1a;上传文件&#xff0c;比如图片&#xff0c;然后预览。这里涉及几个重要的知识点。一是minio永久路径…

Vue学习笔记(二)

Vue学习笔记二脚手架利用脚手架软件生成项目包脚手架 随着时代的发展, WEB开发逐渐出现了 工程化 特征: 流水线作业! 脚本方式: 到饭店 自选点餐… 脚手架方式: 点 套餐, 一套完善的配置,扩展, 各种易用功能… 脚手架: 就是一款软件, 可以按照用户需求自动生成 开发环境: 包含…

[博士论文]基于图数据的可信赖机器学习

密歇根大学Towards Trustworthy Machine Learning on Graph Datahttps://deepblue.lib.umich.edu/handle/2027.42/174201摘要机器学习已经被应用于越来越多影响我们日常生活的与社会相关的场景&#xff0c;从社交媒体和电子商务到自动驾驶汽车和刑事司法。因此&#xff0c;为了…

7-2 洛希极限

科幻电影《流浪地球》中一个重要的情节是地球距离木星太近时&#xff0c;大气开始被木星吸走&#xff0c;而随着不断接近地木“刚体洛希极限”&#xff0c;地球面临被彻底撕碎的危险。但实际上&#xff0c;这个计算是错误的。 洛希极限&#xff08;Roche limit&#xff09;是一…

用Python实现十大经典排序算法(附动图)

排序算法是《数据结构与算法》中最基本的算法之一。 排序算法可以分为内部排序和外部排序&#xff0c;内部排序是数据记录在内存中进行排序&#xff0c;而外部排序是因排序的数据很大&#xff0c;一次不能容纳全部的排序记录&#xff0c;在排序过程中需要访问外存。常见的内部排…

69、CLIP-NeRF: Text-and-Image Driven Manipulation of Neural Radiance Fields

简介 官网&#xff1a;https://cassiepython.github.io/clipnerf/ 利用对比语言-图像预训练(CLIP)模型的联合语言-图像嵌入空间&#xff0c;提出了一个统一的框架&#xff0c;可以用短文本提示或示例图像以用户友好的方式操纵NeRF。改论文结合NeRF的新视图合成能力和生成模型潜…

【自学Python】Python缩进规则

Python缩进规则 Python缩进规则教程 Python 和其它程序设计语言采用大括号 {} 分隔代码块不同&#xff0c;Python 采用代码缩进和冒号来区分代码块之间的层次。 在 Python 中&#xff0c;对于 类定义、函数定义、流程控制语句、异常处理语句 等&#xff0c;行尾的冒号和下一…

【Linux篇】之TFTP服务配置

tftp是基于UDP协议的简单文本文件传输协议&#xff1b; 用途&#xff1a;使用网络的方式将文件传输(下载)到开发板中。 具体的tftp服务的安装步骤&#xff1a; 1> 安装tftp服务 (ubuntu必须可以上网) sudo apt-get update ----> 更新源 sudo apt-get install tftpd-hpa…