对象不是个盒子

class Gizmo:

def __init__(self):

print('Gizmo id: %d' % id(self))

x = Gizmo()

print(x)

y = Gizmo() * 10

print(y)

print(dir())

❶ 输出的 Gizmo id: ... 是创建 Gizmo 实例的副作用。

❷ 在乘法运算中使用 Gizmo 实例会抛出异常。

❸ 这里表明,在尝试求积之前其实会创建一个新的 Gizmo 实例。

❹ 但是,肯定不会创建变量 y,因为在对赋值语句的右边进行求值时抛出了异常。

为了理解 Python 中的赋值语句,应该始终先读右边。对象在右边创建或获取,在此之后左边的变量才会绑定到对象上,

标识、相等性和别名

longe = {'name': 'longe', 'born': 1993}

liang = longe

print(liang is longe)

print(id(liang), id(longe))

longe['balance'] = 950

print(liang)

## 冒充的longe信息

other = {'name': 'longe', 'born': 1993, 'balance': 950}

print(other)

print(other is longe)

❶ liang 是 longe 的别名。

❷ is 运算符和 id 函数确认了这一点。

❸ 向 liang 中添加一个元素相当于向 longe 中添加一个元素。

在那段代码中,liang 和 longe 是别名,即两个变量绑定同一个对象。

而 other 不是 longe 的别名,因为二者绑定的是不同的对象。

other 和longe 绑定的对象具有相同的值(== 比较的就是值),但是它们的标识不同。

每个变量都有标识、类型和值。对象一旦创建,它的标识绝不会变;

你可以把标识理解为对象在内存中的地址。

is 运算符比较两个对象的标识;

id() 函数返回对象标识的整数表示。

在==和is之间选择

== 运算符比较两个对象的值(对象中保存的数据),而 is 比较对象的标识。

is 运算符比 == 速度快,因为它不能重载,所以 Python 不用寻找并调用特殊方法,而是 直接比较两个整数 ID

eq 方法,会考虑对象属性的值。相等性测试可能涉及大量处理工作,例如,比较大型集合或嵌套层级深的结构时。

元组的相对不可变性

元组的不可变性其实是指 tuple 数据结构的物理内容(即保存的引用)不可变,与引用的对象无关

元组的值会随着引用的可变对象的变化而变。

元组中不可变的是元素的标识。内存地址

>>> t1 = (1, 2, [30, 40]) ➊

>>> t2 = (1, 2, [30, 40]) ➋

>>> t1 == t2 ➌

True

>>> id(t1[-1]) ➍

4302515784

>>> t1[-1].append(99) ➎

>>> t1

(1, 2, [30, 40, 99])

>>> id(t1[-1]) ➏

4302515784

>>> t1 == t2 ➐

False

基础理解!!!还是可以的

默认浅复制

>>> l1 = [3, [55, 44], (7, 8, 9)]

>>> l2 = list(l1) ➊

>>> l2

[3, [55, 44], (7, 8, 9)]

>>> l2 == l1 ➋

True

>>> l2 is l1 ➌

False

然而,构造方法或 [:] 做的是浅复制(即复制了最外层容器,副本中的元素是源容器中

元素的引用)。如果所有元素都是不可变的,那么这样没有问题,还能节省内存。

为任意对象做深复制和浅复制

import copy

class Bus:

def __init__(self, passengers=None):

if passengers is None:

self.passengers = []

else:

self.passengers = list(passengers)

def pick(self, name):

self.passengers.append(name)

def drop(self, name):

self.passengers.remove(name)

bus1 = Bus(['Alice', 'Bill', 'Claire', 'David'])

bus2 = copy.copy(bus1)

bus3 = copy.deepcopy(bus1)

print(id(bus1), id(bus2), id(bus3))

bus1.drop('Bill')

print(bus2.passengers)

print(id(bus1.passengers), id(bus2.passengers), id(bus3.passengers))

print(bus3.passengers)

❸ 审查 passengers 属性后发现,bus1 和 bus2 共享同一个列表对象,因为 bus2 是

bus1 的浅复制副本。

❹ bus3 是 bus1 的深复制副本,因此它的 passengers 属性指代另一个列表。

注意,一般来说,深复制不是件简单的事。如果对象有循环引用,那么这个朴素的算法会进入无限循环

深复制

>>> a = [10, 20]

>>> b = [a, 30]

>>> a.append(b)

>>> a

[10, 20, [[...], 30]]

>>> from copy import deepcopy

>>> c = deepcopy(a)

>>> c

[10, 20, [[...], 30]]

深复制有时可能太深了。例如,对象可能会引用不该复制的外部资源或单例值。我们可以实现特殊方法 __copy__() 和 __deepcopy__(),控制 copy 和 deepcopy 的行为

函数的参数作为引用时

共享传参指函数的各个形式参数获得实参中各个引用的副本。也就是说,函数内部的形参

是实参的别名。

def f(a, b):

a += b

return a

a = [1, 2]

b = [3, 4]

print(f(a, b))

print(a, b)

这里变量全都是引用,无论局部变量还是全局.

所以上面案例中,a会变化

不要使用可变类型作为参数的默认值

class HauntedBus:

"""备受幽灵乘客折磨的校车"""

def __init__(self, passengers=[]): #别使用这种可变类型 作为默认参数

self.passengers = passengers

防御性编程(对待可变类型)

class TwilightBus:

"""正常的校车"""

def __init__(self, passengers=None):

if passengers is None:

self.passengers = []

else:

self.passengers = list(passengers) ##这里会产生副本(可以理解为深拷贝)

def pick(self, name):

self.passengers.append(name)

def drop(self, name):

self.passengers.remove(name)

bus1 = TwilightBus(("sfs", 'sdf'))

bus2 = TwilightBus(["sdfsdfsfd111"])

bus1.pick("ppxia")

bus1.drop("sfs")

print(bus1.passengers)

bus2.drop("sdfsdfsfd111")

print(bus2.passengers)

尽量别用可变类型做默认参数值, 实在要用,必须使其产生副本

del和垃圾回收

有个 del 特殊方法,但是它不会销毁实例,不应该在代码中调用。

即将销毁实例时,Python 解释器会调用 del 方法,给实例最后的机会,释放外资源。

自己编写的代码很少需要实现 del 代码,有些 Python 新手会花时间实现,但却吃力不讨好,因为 del 很难用对。

垃圾计数器

在 CPython 中,垃圾回收使用的主要算法是引用计数。

实际上,每个对象都会统计有多少引用指向自己。

当引用计数归零时,对象立即就被销毁:CPython 会在对象上调用__del__ 方法(如果定义了),然后释放分配给对象的内存。

为了演示对象生命结束时的情形,示例 8-16 使用 weakref.finalize 注册一个回调函数,在销毁对象时调用。

>>> import weakref

>>> s1 = {1, 2, 3}

>>> s2 = s1 ➊

>>> def bye(): ➋

... print('Gone with the wind...')

...

>>> ender = weakref.finalize(s1, bye) ➌

>>> ender.alive ➍

True

>>> del s1

>>> ender.alive ➎

True

>>> s2 = 'spam' ➏

Gone with the wind...

>>> ender.alive

False

❺ 如前所述,del 不删除对象,而是删除对象的引用。

❻ 重新绑定最后一个引用 s2,让 {1, 2, 3} 无法获取。对象被销毁了,调用了 bye 回

调,ender.alive 的值变成了 False。

弱引用

正是因为有引用,对象才会在内存中存在。当对象的引用数量归零后,垃圾回收程序会把对象销毁。但是,有时需要引用对象,而不让对象存在的时间超过所需时间。

弱引用不会增加对象的引用数量。引用的目标对象称为所指对象(referent)。因此我们说,弱引用不会妨碍所指对象被当作垃圾回收。

弱引用在缓存应用中很有用,因为我们不想仅因为被缓存引用着而始终保存缓存对象。

弱引用是可调用的对象,返回的是被引用的对象;

>>> import weakref

>>> a_set = {0, 1}

>>> wref = weakref.ref(a_set) ➊

>>> wref

>>> wref() ➋

{0, 1}

>>> a_set = {2, 3, 4} ➌

>>> wref() ➍

{0, 1}

>>> wref() is None ➎

False

>>> wref() is None ➏

True

❷ 调用 wref() 返回的是被引用的对象,{0, 1}。因为这是控制台会话,所以 {0, 1}

会绑定给 _ 变量。

❸ a_set 不再指代 {0, 1} 集合,因此集合的引用数量减少了。但是 _ 变量仍然指代

它。

❹ 调用 wref() 依旧返回 {0, 1}。

❺ 计算这个表达式时,{0, 1} 存在,因此 wref() 不是 None。但是,随后 _ 绑定到结

果值 False。现在 {0, 1} 没有强引用了。

❻ 因为 {0, 1} 对象不存在了,所以 wref() 返回 None。

弱引用到此为止,用到再来查 page 289

总结

变量的不是盒子,是便利贴(就是c的指针)

==是值相等 is是(内存地址相等)

默认是浅复制,就内存地址复制.深复制会有一些过深危险(可以重写特殊方法 __copy__() 和 __deepcopy__())

尽量别用可变类型做默认参数值, 实在要用,必须使其产生副本

实际上,每个对象都会统计有多少引用指向自己。 Cpython中, 当引用计数归零时,对象立即就被销毁:CPython会在对象上调用__del__ 方法(如果定义了),然后释放分配给对象的内存

python用可变参数求积_流畅的python读书笔记-第八章-对象引用、可变性和垃圾回收...相关推荐

  1. 流畅的python 对象引用 可变性和垃圾回收

    对象引用.可变性和垃圾回收 变量不是盒子 人们经常使用"变量是盒子"这样的比喻,但是这有碍于理解面向对象语言中的引用式变量.Python 变量类似于 Java 中的引用式变量,因此 ...

  2. 流畅的Python读书笔记-第八章-对象引用、可变性和垃圾回收

    第8章:对象引用,可变性和垃圾回收 在Python里面变量不是盒子,而是便利贴,类似于Java中的引用变量,因此最好把它们理解为附加在对象上的标注. 因为变量不过是标注,因此无法阻止为对象贴上多个标注 ...

  3. python 类初始化参数校验_如何规避python参数的初始化次数?

    我们在内存不足的时候,除了增加内存的可用度,可以进行一个清理内存的初始化操作,当然这种是最后迫不得已的选择.我们在python中也有需要用到初始化的地方,鉴于参数和函数的关系密不可分,本篇我们会简单的 ...

  4. python函数中可变参数的传递方式是_详解Python函数可变参数定义及其参数传递方式...

    Python函数可变参数定义及其参数传递方式详解 python中 函数不定参数的定义形式如下 1. func(*args) 传入的参数为以元组形式存在args中,如: def func(*args): ...

  5. python函数中可变参数的传递方式_详解Python函数可变参数定义及其参数传递方式...

    Python函数可变参数定义及其参数传递方式详解 python中 函数不定参数的定义形式如下 1. func(*args) 传入的参数为以元组形式存在args中,如: def func(*args): ...

  6. python中可变参数args_python 可变参数 *args, **kwds

    关键字可变参数-字典 #!/usr/bin/env python# -*- coding: UTF-8 -*-def dictVarArgs(arg1, arg2='defaultB', **theR ...

  7. python的可变参数 *args 和关键字参数**kw

    *args:非关键字参数,传入任意个不需要包含参数名的参数,参数实际以tuple形式传入 **kw:是关键字参数,传入任意个要带参数名的参数,参数实际以dict传入 例子 >>> d ...

  8. python中可变参数args传入函数时储存的类型是,Python函数可变参数定义及其参数传递方式实例详解...

    本文实例讲述了Python函数可变参数定义及其参数传递方式.分享给大家供大家参考.具体分析如下: python中 函数不定参数的定义形式如下: 1.func(*args) 传入的参数为以元组形式存在a ...

  9. python中可变参数*args传入函数时的存储方式为_python 中文读法详解Python函数可变参数定义及其参数传递方式...

    Python函数可变参数定义及其参数传递方式详解 python中 函数不定参数的定义形式如下 1. func(*args) 传入的参数为以元组形式存在args中,如: def func(*args): ...

最新文章

  1. 以AI制作AI,当AutoML加入AI研究员内卷大潮
  2. 【异常】 Ensure that config phoenix.schema.isNamespaceMappingEnabled is consistent on client and server.
  3. Java实现大数乘法_java实现大数加法、乘法(BigDecimal)
  4. jmeter http并发测试时报错
  5. Linux安装jdk学习
  6. liferay6.2导出excel
  7. mysql 最值复杂查询_MySQL高级查询
  8. bzoj 1046: [HAOI2007]上升序列
  9. MySQL 显示版本、端口、状态
  10. AJAX实现导航式多条件搜索
  11. 为什么c语言程序exe无法运行程序,这个程序怎么运行?为什么显示没有exe??...
  12. hibernate操作步骤(代码部分)
  13. 24张高清无码图,看到就停不下来了...
  14. J2EE--自定义mvc增删改查
  15. 学画画软件app推荐_可以学画画的APP有哪些?
  16. 通过access口加vlan标签吗_[转载]vlan与trunk打标签过程
  17. stanfordnlp使用自定义分词分句
  18. doc文档转docx格式,方便对文件进行批量处理【附代码】
  19. 浪潮存储:基于系统级可靠性设计,为数据存储保驾护航
  20. mysql memory 与redis_memory和redis

热门文章

  1. 如何提取论文中的表格数据(pdf转换excel)
  2. Qt窗口设置成透明色方法(窗口设置成透明色结果显示成黑色的解决办法)
  3. 将一个十进制数转化为二进制数,统计二进制数中1的个数
  4. kotlin 回调函数、let、also、run 、with、apply 使用总结
  5. 【Docker】——镜像
  6. 【解决方案】RTSP/Onvif安防视频直播解决方案EasyNVR在某省高速上云项目中的应用分析
  7. 基于 Apache Kylin 的微博舆情实时分析(内含 Demo)
  8. 为啥不建议写内联样式?
  9. 在计算机中 wan的中文意思,在计算机网络术语中,WAN的中文含义是()。
  10. Java 单例模式详解