简单的python-核心篇-对象与引用

内容分享2个月前发布
0 1 0

在Python中,一切都是对象。每个对象都有身份、类型和值,通过引用系统进行管理。

# 一切都是对象
number = 42
string = "Hello"
function = print
my_list = [1, 2, 3]

print(f"number: id={id(number)}, type={type(number)}, value={number}")
print(f"string: id={id(string)}, type={type(string)}, value={string}")
print(f"function: id={id(function)}, type={type(function)}")
print(f"list: id={id(my_list)}, type={type(my_list)}, value={my_list}")

引用与对象的关系

基本概念

# 变量名是引用,指向内存中的对象
a = [1, 2, 3]  # a是引用,指向列表对象
b = a          # b是另一个引用,指向同一个对象

print(f"a is b: {a is b}")        # True - 同一个对象
print(f"a == b: {a == b}")        # True - 值一样
print(f"id(a): {id(a)}")          # 对象的内存地址
print(f"id(b): {id(b)}")          # 一样的内存地址

# 修改对象会影响所有引用
a.append(4)
print(f"a: {a}")  # [1, 2, 3, 4]
print(f"b: {b}")  # [1, 2, 3, 4] - b也改变了!

可变对象与不可变对象

# 不可变对象:数字、字符串、元组
x = 42
y = x
print(f"x is y: {x is y}")  # True

x = 43  # 创建新对象,x指向新对象
print(f"x: {x}, y: {y}")    # x: 43, y: 42
print(f"x is y: {x is y}")  # False

# 可变对象:列表、字典、集合
list1 = [1, 2, 3]
list2 = list1
list1.append(4)
print(f"list1: {list1}")    # [1, 2, 3, 4]
print(f"list2: {list2}")    # [1, 2, 3, 4] - 也改变了!

深拷贝与浅拷贝

浅拷贝

import copy

original = [[1, 2], [3, 4]]
shallow = copy.copy(original)

print(f"original is shallow: {original is shallow}")           # False
print(f"original == shallow: {original == shallow}")           # True
print(f"original[0] is shallow[0]: {original[0] is shallow[0]}")  # True - 内部对象一样

# 修改内部对象会影响两个列表
original[0].append(5)
print(f"original: {original}")    # [[1, 2, 5], [3, 4]]
print(f"shallow: {shallow}")      # [[1, 2, 5], [3, 4]] - 也改变了!

深拷贝

original = [[1, 2], [3, 4]]
deep = copy.deepcopy(original)

print(f"original is deep: {original is deep}")                 # False
print(f"original == deep: {original == deep}")                 # True
print(f"original[0] is deep[0]: {original[0] is deep[0]}")     # False - 完全独立

# 修改内部对象不会影响另一个
original[0].append(5)
print(f"original: {original}")    # [[1, 2, 5], [3, 4]]
print(f"deep: {deep}")            # [[1, 2, 3], [3, 4]] - 不受影响

内存管理机制

引用计数

import sys

def show_ref_count(obj, name):
    """显示对象的引用计数"""
    count = sys.getrefcount(obj) - 1  # 减去函数参数的引用
    print(f"{name}的引用计数: {count}")

# 创建对象
my_list = [1, 2, 3]
show_ref_count(my_list, "my_list")  # 1

# 增加引用
another_ref = my_list
show_ref_count(my_list, "my_list")  # 2

# 删除引用
del another_ref
show_ref_count(my_list, "my_list")  # 1

循环引用

class Node:
    def __init__(self, value):
        self.value = value
        self.next = None

# 创建循环引用
node1 = Node(1)
node2 = Node(2)
node1.next = node2
node2.next = node1

# 删除引用
del node1, node2
# 对象依旧存在,但无法访问 - 内存泄漏!

# 使用弱引用避免循环引用
import weakref

class NodeWithWeakRef:
    def __init__(self, value):
        self.value = value
        self.next = None
    
    def __del__(self):
        print(f"节点 {self.value} 被删除")

node1 = NodeWithWeakRef(1)
node2 = NodeWithWeakRef(2)
node1.next = weakref.ref(node2)
node2.next = weakref.ref(node1)

del node1, node2  # 对象会被正确删除

对象比较

is vs ==

# is 比较对象身份(内存地址)
# == 比较对象值

# 小整数缓存
a = 256
b = 256
print(f"a is b: {a is b}")    # True - 小整数被缓存

c = 257
d = 257
print(f"c is d: {c is d}")    # False - 大整数不缓存

# 字符串缓存
s1 = "hello"
s2 = "hello"
print(f"s1 is s2: {s1 is s2}")  # True - 字符串被缓存

# 列表不缓存
list1 = [1, 2, 3]
list2 = [1, 2, 3]
print(f"list1 is list2: {list1 is list2}")  # False
print(f"list1 == list2: {list1 == list2}")  # True

自定义比较

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __eq__(self, other):
        """自定义相等比较"""
        if not isinstance(other, Person):
            return False
        return self.name == other.name and self.age == other.age
    
    def __hash__(self):
        """自定义哈希值"""
        return hash((self.name, self.age))
    
    def __str__(self):
        return f"Person({self.name}, {self.age})"

# 测试自定义比较
p1 = Person("Alice", 25)
p2 = Person("Alice", 25)
p3 = Person("Bob", 25)

print(f"p1 == p2: {p1 == p2}")  # True
print(f"p1 is p2: {p1 is p2}")  # False
print(f"p1 == p3: {p1 == p3}")  # False

# 可以作为字典键
people_dict = {p1: "first", p3: "second"}
print(people_dict[p2])  # "first" - 由于p1 == p2

高级内存技巧

1. 使用__slots__优化内存

class RegularClass:
    def __init__(self, name, age):
        self.name = name
        self.age = age

class OptimizedClass:
    __slots__ = ['name', 'age']  # 限制属性,节省内存
    
    def __init__(self, name, age):
        self.name = name
        self.age = age

# 比较内存使用
import sys

regular = RegularClass("Alice", 25)
optimized = OptimizedClass("Alice", 25)

print(f"RegularClass实例大小: {sys.getsizeof(regular)}")
print(f"OptimizedClass实例大小: {sys.getsizeof(optimized)}")

# OptimizedClass不能动态添加属性
# optimized.new_attr = "value"  # AttributeError

2. 内存视图

# 使用memoryview避免数据复制
data = bytearray(b'Hello World')
view = memoryview(data)

# 修改视图会影响原始数据
view[0] = ord('h')
print(data)  # bytearray(b'hello World')

# 切片视图不会复制数据
slice_view = view[6:]
print(slice_view.tobytes())  # b'World'

3. 对象池模式

class ObjectPool:
    def __init__(self, factory, max_size=10):
        self.factory = factory
        self.max_size = max_size
        self.pool = []
    
    def get(self):
        """获取对象"""
        if self.pool:
            return self.pool.pop()
        return self.factory()
    
    def put(self, obj):
        """归还对象"""
        if len(self.pool) < self.max_size:
            self.pool.append(obj)

# 使用对象池
def create_expensive_object():
    return {"data": [0] * 1000}

pool = ObjectPool(create_expensive_object)

# 获取对象
obj1 = pool.get()
obj2 = pool.get()

# 归还对象
pool.put(obj1)
pool.put(obj2)

调试内存问题

1. 使用gc模块

import gc

def show_gc_stats():
    """显示垃圾回收统计"""
    print(f"垃圾回收统计: {gc.get_stats()}")
    print(f"当前对象数: {len(gc.get_objects())}")

# 强制垃圾回收
gc.collect()
show_gc_stats()

2. 使用tracemalloc

import tracemalloc

def memory_trace_example():
    """内存跟踪示例"""
    tracemalloc.start()
    
    # 创建一些对象
    data = [i for i in range(10000)]
    result = sum(data)
    
    # 获取内存使用情况
    current, peak = tracemalloc.get_traced_memory()
    print(f"当前内存使用: {current / 1024 / 1024:.2f} MB")
    print(f"峰值内存使用: {peak / 1024 / 1024:.2f} MB")
    
    tracemalloc.stop()

memory_trace_example()

最佳实践

1. 避免不必要的对象创建

# 不好的做法
def bad_string_concat(strings):
    result = ""
    for s in strings:
        result += s  # 每次都创建新字符串
    return result

# 好的做法
def good_string_concat(strings):
    return "".join(strings)  # 一次性创建

# 测试性能
import time

strings = ["Hello"] * 1000

start = time.time()
bad_string_concat(strings)
bad_time = time.time() - start

start = time.time()
good_string_concat(strings)
good_time = time.time() - start

print(f"不好的方法: {bad_time:.4f}秒")
print(f"好的方法: {good_time:.4f}秒")

2. 合理使用生成器

# 不好的做法 - 创建大列表
def bad_fibonacci(n):
    result = []
    a, b = 0, 1
    for _ in range(n):
        result.append(a)
        a, b = b, a + b
    return result

# 好的做法 - 使用生成器
def good_fibonacci(n):
    a, b = 0, 1
    for _ in range(n):
        yield a
        a, b = b, a + b

# 比较内存使用
import sys

bad_result = bad_fibonacci(1000)
good_result = good_fibonacci(1000)

print(f"列表大小: {sys.getsizeof(bad_result)}")
print(f"生成器大小: {sys.getsizeof(good_result)}")

写在最后

理解Python的对象模型和内存管理机制,让我们能够:

  1. 写出更高效的代码:避免不必要的对象创建
  2. 调试内存问题:快速定位内存泄漏和性能瓶颈
  3. 设计更好的数据结构:合理使用可变和不可变对象
  4. 优化程序性能:通过理解内存分配模式

记住:在Python的世界里,变量名是引用,对象是实体。理解它们的关系,就是理解Python的核心。

© 版权声明

相关文章

1 条评论

  • 头像
    币圈陈默 读者

    收藏了,感谢分享

    无记录
    回复