Python-VBA函数之旅-memoryview函数

目录

一、memoryview函数的常见应用场景

二、memoryview函数使用注意事项

三、如何用好memoryview函数?

1、memoryview函数:

1-1、Python:

1-2、VBA:

2、推荐阅读:

个人主页:神奇夜光杯-CSDN博客

 

一、memoryview函数的常见应用场景

        memoryview函数在 Python 中有着广泛的实际应用场景,尤其是在处理大型数据集合时,其主要优势在于可以在不复制数据的情况下,提供一种以不同数据结构来访问和操作内存缓冲区的方式,这使得memoryview()函数在需要高效处理内存数据的场景中非常有用。常见的应用场景有:

1、大型数据处理:对于大型数据集,如图像、音频、视频或科学计算数据,复制这些数据可能会导致显著的性能下降和内存消耗,使用memoryview()函数可以避免不必要的数据复制,从而提高处理速度和效率。

2、共享内存:当多个数据结构需要共享同一块内存时,memoryview()函数可以方便地实现这一点。例如,你可能有一个大型字节数组,而多个不同的数据结构或视图需要访问或修改这个数组的不同部分,通过使用memoryview()函数,你可以创建多个视图来共享这块内存,而无需复制数据。

3、图像和数组操作:在图像处理或数值计算中,经常需要对大型数组或图像数据进行操作,memoryview()函数可以与PIL图片、NumPy数组等数据结构结合使用,以更高效的方式进行读写操作。

4、网络编程:在网络编程中,经常需要处理大量的字节数据。使用memoryview()函数,你可以在不复制数据的情况下直接操作这些数据,从而提高网络传输的效率。

5、与C语言扩展的交互:Python的memoryview()函数与C语言扩展(如 NumPy、Cython 等)可以很好地协作,这些扩展通常使用原生C数据结构来存储和操作大型数据集,通过memoryview()函数,Python代码可以高效地访问和修改这些数据结构,从而实现与C语言扩展的无缝集成。

6、内存优化:对于内存敏感的应用,如嵌入式系统或实时处理系统,memoryview()函数的内存有效性可以帮助减少内存占用和分配次数,从而提高系统的稳定性和性能。

7、数据库操作:在处理数据库时,特别是大型数据库,使用memoryview()函数可以允许开发者在不复制数据的情况下,对数据库中的数据进行直接操作,这对于读取、修改或分析数据库中的大量数据非常有用。

8、科学计算和数据分析:在科学计算和数据分析领域,如使用NumPy库进行数组操作时,memoryview()函数可以用来在不复制数据的情况下操作大型数组,这对于执行复杂的数学运算、统计分析或数据可视化等任务非常有帮助。

        总之,memoryview()函数通过提供对底层数据的直接访问,使得在处理大型数据集合或需要避免数据复制的场景中能够更高效地操作数据,然而,由于它直接操作内存数据,因此在使用时需要特别小心以避免意外修改或损坏数据。

二、memoryview函数使用注意事项

        在使用Python中的memoryview()函数时,有几个注意事项需要牢记,以确保正确使用并避免潜在的问题:

1、数据访问和修改:memoryview()函数提供的是对底层数据的直接访问,因此你可以直接修改内存中的数据,这意味着你需要非常小心,确保不会意外地修改不应该被修改的数据,在修改数据之前,请确保你完全理解数据结构和内存布局。

2、对象生命周期:当使用memoryview()函数创建内存视图时,它会引用原始对象(如 `bytes` 或 `bytearray`),因此,你需要确保在不再需要内存视图之前,原始对象不会被删除或修改,否则,可能会导致内存错误或不可预测的行为。

3、切片和子视图:memoryview()函数支持切片操作,可以创建原始数据的子集或子视图,这些子视图仍然引用相同的底层数据,因此修改子视图中的数据也会影响原始数据,请确保你理解切片和子视图的行为,并避免意外地修改数据。

4、数据格式和类型:当你使用memoryview()函数的cast()方法时,你需要确保指定的格式和类型与底层数据兼容,错误的格式或类型可能导致数据解析错误或内存访问违规,请仔细检查你的数据类型和格式,并参考相关文档以确保正确使用。

5、内存对齐:在某些情况下,内存对齐可能是一个问题,如果底层数据没有按照你期望的方式对齐,使用memoryview()函数进行访问或修改可能会导致性能下降或错误,确保你了解数据的对齐要求,并相应地处理内存访问。

6、线程安全:如果你在多线程环境中使用memoryview()函数,需要注意线程安全问题,多个线程同时访问和修改同一内存视图可能会导致数据竞争和不一致,你需要使用适当的同步机制(如锁)来确保线程安全。

7、兼容性:尽管memoryview()函数是一个强大的工具,但并不是所有的对象都支持它,只有实现了缓冲区接口的对象(如 `bytes`、`bytearray` 和某些第三方库的对象)才能与memoryview()函数一起使用,在尝试使用memoryview()函数之前,请确保你的对象支持它。

        综上所述,使用memoryview()函数时需要谨慎处理内存访问和修改操作,确保理解数据结构和内存布局,并注意线程安全和兼容性问题。

三、如何用好memoryview函数?

        要充分利用Python中的memoryview()函数,你需要理解其工作原理、适用场景以及潜在的限制。为了帮助你更好地使用memoryview()函数,相关建议如下:

1、理解内存视图与原始数据的关系:memoryview()函数创建的是原始数据的视图,而不是数据的副本,这意味着对memoryview对象的修改会直接影响原始数据,因此,在使用memoryview()函数时,确保你清楚知道它引用的是哪块内存,以及修改它可能带来的后果。

2、选择适当的访问模式:memoryview()函数支持不同的访问模式,如只读、读写等,根据你对数据的操作需求,选择适当的访问模式,如果你不需要修改数据,使用只读模式可以提高安全性。

3、注意数据类型和格式:当使用memoryview()函数的cast()方法进行类型转换时,确保目标数据类型和格式与原始数据兼容,不正确的转换可能导致数据损坏或解释错误。

4、谨慎处理边界情况:在使用memoryview()函数进行切片或索引操作时,确保你的操作不会越界,越界访问可能导致程序崩溃或数据损坏。

5、结合其他库使用:memoryview()函数可以与Python的其他库和工具无缝集成,如NumPy、struct等,结合这些库使用,可以进一步扩展memoryview()函数的功能和应用范围。

6、性能优化:对于大型数据集或需要频繁进行内存操作的场景,使用memoryview()函数可以避免不必要的数据复制,从而提高性能。但在使用之前,最好进行性能测试,确保memoryview()函数在你的特定场景下确实带来了性能提升。

7、编写清晰的代码:由于memoryview()函数直接操作内存,代码可能会变得难以理解和维护,因此,在编写使用memoryview()函数的代码时,务必保持清晰和简洁,添加必要的注释和文档,以便其他人能够理解和维护你的代码。

8、注意内存管理:memoryview()函数对象依赖于其原始对象的存在,因此,在不再需要 memoryview()对象时,确保释放相关的内存资源,避免内存泄漏。

        总之,要充分利用memoryview()函数,你需要深入理解其工作原理和潜在的限制,并结合具体的应用场景和需求进行使用,通过谨慎处理内存访问和修改操作,结合其他库和工具的使用,你可以编写出高效、可靠的代码。

1、memoryview函数:
1-1、Python:
# 1.函数:memoryview
# 2.功能:用于返回指定对象的内存查看对象,实际上是内存查看对象的构造函数
# 3.语法:memoryview(object)
# 4.参数:object,要获取其内存查看对象的对象名,该对象必须支持缓冲区协议(buffer protocol),Python内置对象支持缓冲区协议的对象有bytes和bytearray
# 5.返回值:返回由给定实参创建的“内存视图”对象
# 6.说明:
# 6-1、所谓内存查看对象(memory view),就是符合缓冲区协议的对象,为了给别的代码使用缓冲区里的数据,不必复制就可以直接使用. memoryview()函数类似
#      C语言中的指针,如果某个对象支持缓冲区协议,那么就可以通过memoryview()函数去访问它内部的数据
# 6-2、 不能修改memoryview对象的大小
# 7.示例:
# 用dir()函数获取该函数内置的属性和方法
print(dir(memoryview))
# ['__class__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__',
# '__ge__', '__getattribute__', '__getitem__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__',
# '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__',
# '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'c_contiguous', 'cast', 'contiguous', 'f_contiguous', 'format',
# 'hex', 'itemsize', 'nbytes', 'ndim', 'obj', 'readonly', 'release', 'shape', 'strides', 'suboffsets', 'tobytes', 'tolist', 'toreadonly']

# 用help()函数获取该函数的文档信息
help(memoryview)

# 应用一:大型数据处理
# 示例1:使用memoryview()处理字节数据
# 创建一个大的字节串
large_bytes = bytes(range(256)) * 10000
# 创建一个memoryview对象
mv = memoryview(large_bytes)
# 访问特定位置的字节
print(mv[100])
# 100

# 修改特定位置的字节(如果底层对象是可变的,如bytearray)
large_bytes = bytearray(range(256)) * 10000
mv = memoryview(large_bytes)
mv[100] = 200
print(large_bytes[100])
# 200

# 示例2:使用memoryview()进行高效的数据交换
import numpy as np
# 创建两个大的NumPy数组
array1 = np.arange(1000000)
array2 = np.zeros(1000000, dtype=np.int64)
# 创建memoryview对象
mv1 = memoryview(array1)
mv2 = memoryview(array2)
# 交换两个数组的内容(通过交换memoryview对象)
mv1, mv2 = mv2, mv1
# 现在array1包含零,array2包含原始数据
print(np.all(array1 == 0))
print(np.all(array2 == np.arange(1000000)))
# False
# False

# 应用二:共享内存
# 示例1:使用memoryview()在同一个进程中的两个函数之间共享内存
def process_data(data_view):
    # 在这个函数内部,我们直接操作memoryview对象,从而修改原始数据
    for i in range(len(data_view)):
        data_view[i] *= 2  # 假设我们要对每个元素乘以2
def main():
    # 创建一个大的字节数组
    large_data = bytearray(range(128))
    # 创建一个memoryview对象,它提供了对large_data的直接访问
    data_view = memoryview(large_data)
    # 将memoryview对象转换为可写的bytearray对象
    editable_data = bytearray(data_view)
    # 调用函数处理数据,该函数接收bytearray对象作为参数
    process_data(editable_data)
    # 打印处理后的数据,可以看到原始large_data已经被修改
    print(large_data)
if __name__ == "__main__":
    main()
# bytearray(b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\
# x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f')

# 示例2:使用multiprocessing的Array来在进程之间共享内存
from multiprocessing import Process, Array
import ctypes
import time
def worker(shared_array):
    # 在子进程中访问共享数组
    for i in range(len(shared_array)):
        shared_array[i] *= 2
def main():
    # 创建一个共享数组,用于在进程间共享内存
    shared_array = Array(ctypes.c_int, [i for i in range(10)])
    # 创建一个子进程,并传递共享数组给它
    p = Process(target=worker, args=(shared_array,))
    p.start()
    # 等待子进程完成
    p.join()
    # 打印共享数组的内容,它已经被子进程修改
    print(shared_array[:])
if __name__ == "__main__":
    main()
# [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

# 应用三:图像和数组操作
# 示例1:使用memoryview修改NumPy数组
import numpy as np
# 创建一个NumPy数组
arr = np.arange(18, dtype=np.int32)
print("Original array:", arr)
# Original array: [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17]

# 创建一个memoryview对象来访问数组的数据
memview = memoryview(arr)
# 使用memoryview修改数组的元素
for i in range(len(memview)):
    memview[i] = memview[i] * 3  # 将每个元素乘以3
# NumPy数组已经被修改,因为memoryview是直接引用
print("Modified array:", arr)
# Modified array: [ 0  3  6  9 12 15 18 21 24 27 30 33 36 39 42 45 48 51]

# 示例2:使用memoryview处理图像数据
from PIL import Image
import numpy as np
# 加载一个灰度图像
img = Image.open('A5.png').convert('L')
# 将图像数据转换为NumPy数组
img_array = np.array(img)
# 创建一个memoryview对象来访问图像数据
memview_img = memoryview(img_array)
# 修改像素值(例如,将每个像素值增加50)
for i in range(len(memview_img)):
    memview_img[i] = min(255, memview_img[i] + 50)  # 确保值不超过 255
# 将修改后的数据转换回图像
modified_img = Image.fromarray(np.asarray(memview_img.tobytes().reshape(img_array.shape), dtype=np.uint8))
modified_img.show()

# 应用四:内存优化
# 创建一个较大的字节串
data = bytearray(range(256))  # 创建一个包含0到255的字节串
# 创建一个memoryview对象
memview = memoryview(data)
# 现在,memview是对data的一个引用,而不是data的副本
# 所以它们共享相同的内存区域
assert memview.tobytes() == data
# 修改memoryview中的一个元素会同时修改原始数据
memview[10] = 42
assert data[10] == 42
# 使用memoryview切片来处理数据的子集,同样不复制数据
subset = memview[10:20]
# 修改子集中的一个元素也会修改原始数据
subset[3] = 100
assert data[13] == 100

# 应用五:数据库操作
# 首先,确保你已经安装了数据库驱动,例如psycopg2(用于PostgreSQL)或 mysql-connector-python(用于MySQL)
# 以PostgreSQL为例,假设我们有一个表,其中包含二进制数据:
# CREATE TABLE binary_data (
# #     id serial PRIMARY KEY,
# #     data bytea
# # )
import psycopg2
from psycopg2 import sql
import io
# 连接到数据库
conn = psycopg2.connect(database="your_database", user="your_user", password="your_password", host="localhost",
                        port="5432")
cur = conn.cursor()
# 插入二进制数据到数据库
def insert_binary_data(data):
    with cur as cursor:
        query = sql.SQL("INSERT INTO binary_data (data) VALUES (%s)").format(sql.Placeholder())
        cursor.execute(query, (psycopg2.Binary(data),))
    conn.commit()
# 从数据库中读取二进制数据并使用 memoryview
def read_binary_data(id):
    with cur as cursor:
        query = sql.SQL("SELECT data FROM binary_data WHERE id = {}").format(sql.Placeholder())
        cursor.execute(query, (id,))
        row = cursor.fetchone()
        if row:
            # 使用 memoryview 引用二进制数据,而不是复制它
            memview = memoryview(row[0])
            # 现在你可以对 memview 进行操作,它是对数据库中数据的直接引用
            # 例如,你可以将其转换为字节串或其他格式
            data_bytes = memview.tobytes()
            return data_bytes
    return None
# 示例:插入数据
binary_data = b'This is some binary data'
insert_binary_data(binary_data)
# 示例:读取数据
retrieved_data = read_binary_data(1)  # 假设我们刚刚插入的数据的 id 是 1
if retrieved_data:
    print("Retrieved data:", retrieved_data)
else:
    print("No data found.")
# 关闭数据库连接
conn.close()

# 应用六:科学计算和数据分析
import numpy as np
import os
# 假设二进制文件包含单精度浮点数(32位)
FLOAT_SIZE = 4
# 打开二进制文件
with open('data.bin', 'rb') as f:
    # 获取文件大小
    file_size = os.path.getsize(f.name)
    # 计算浮点数数量
    num_floats = file_size // FLOAT_SIZE
    # 创建一个空的NumPy数组来存储数据
    data_array = np.empty(num_floats, dtype=np.float32)
    # 使用memoryview来读取文件内容到NumPy数组
    memview = memoryview(data_array)
    # 分块读取文件,避免一次性加载整个文件到内存
    chunk_size = 1024 * 1024  # 例如,每次读取 1MB
    for i in range(0, file_size, chunk_size):
        # 读取一个数据块
        chunk = f.read(min(chunk_size, file_size - i))
        # 使用memoryview将数据块写入到NumPy数组的相应位置
        memview[i // FLOAT_SIZE: (i + len(chunk)) // FLOAT_SIZE] = np.frombuffer(chunk, dtype=np.float32)
# 现在你可以使用NumPy数组进行科学计算和数据分析
# 例如,计算平均值
mean_value = np.mean(data_array)
print(f"Mean value: {mean_value}")

# 应用七:获取bytearray数组在内存中的数据
b = bytearray('myelsa', 'utf-8')
mv = memoryview(b)
print('可变字节数组b的内存对象为:', mv)
print('缓存中的数据为:', mv.tobytes())
print('缓存中的内存对象为:', mv.tolist())
print('字节数组中m的内存对象为:', mv[0])
print('字节数组中e的内存对象为:', mv[2])
print('字节数组中els的内存对象为:', mv[2:4])
# 可变字节数组b的内存对象为: <memory at 0x00000201D5AC6500>
# 缓存中的数据为: b'myelsa'
# 缓存中的内存对象为: [109, 121, 101, 108, 115, 97]
# 字节数组中m的内存对象为: 109
# 字节数组中e的内存对象为: 101
# 字节数组中els的内存对象为: <memory at 0x00000201D5AC65C0>
1-2、VBA:
略,待后补。
2、推荐阅读:

2-1、Python-VBA函数之旅-globals()函数

Python算法之旅:Algorithm

Python函数之旅:Functions

个人主页:神奇夜光杯-CSDN博客
  • 48
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

神奇夜光杯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值