你相信吗??Python把数字也当做对象!@@@对象,名称绑定,引用计数
本文学习自:http://blog.csdn.net/yockie/article/details/8474408
1.对象
Python中, 万物皆对象,包括12345等int常量。不信吗??用dir()命令看一看就知道
当然了,既然他们都叫做对象。那么肯定有共同点了!所有的对象都有下面的三个特征
a = 1
b = 1
print(id(a))
print(id(b))
print(id(1)) # 结果:
#
#
#
有感觉了,int类似于string一样,是一个不可变的对象,内部可能有一个常量池的东西?
* 都有唯一的标识码 id()
* 都有确定的类型
* 有内容(或称为值)
一旦对象被创建,标识码就不能更改,对象类型也是不可更改的,内容可以改变(实际上不是内容改变而是把名字取给另一个不可变对象)
一个名称只能对应一个对象,一个对象可能有0或1或n个名称 这个0,1,n叫引用计数
(可变对象如dict、list 。恒定对象如int、string)
而一个对象有可能:
* 肯定有属性
* 有0个或者n个方法
* 有0个或者n个名字(引用计数为0,或者为n)
2.名字
我悄悄的认为,名字就是引用。不知道对不对
对象自己不知道有多少名字,叫什么,只有名字本身知道它所指向的是个什么对象。
Python将赋值语句认为是一个命名操作(或名称绑定)
一个对象的引用计数可以为0或者为n,要访问对象必须通过名字(引用),Python中赋值操作就是一个命名操作(或名字绑定)。
名字在一定的名字空间内有效。而且唯一,就是说一个名字只能对应一个对象,(在同一个名字空间内)而一个对象却可以有多个名字。
a = 1 在Python中的含义:
* 创建一个值为1的对象
* a是指向该对象的名字
3.绑定
绑定就是用引用指向对象,会增加该对象的引用计数。
a = a + 1 在Python中的含义:
* 创建一个新的对象,值为 a + 1
* a 这个名字指向新对象,新对象的引用计数 + 1 ,而a以前指向的对象引用计数 - 1
* a以前指向的对象值没有变
什么操作导致引用计数的变化?
* 赋值
* 在一个容器(list、 dict、seq)中包含该对象
——将增加对象的引用计数
* 离开当前的名字空间(该名字空间中的本地名字都会被销毁)
* 对象的一个名字被绑定到另外一个对象
* 对象被从包含它的容器中删除
* 用del()方法
——将减少对象的引用计数
区别
a = 1
b = a
a = 2
print(b) # 1 恒定对象
----------
a = [1, 2, 3]
b = a
a[0] = 2
print (b) # [2,2,3] 可变对象
-----------------------------
为什么修改字典d的值不用global关键字先声明呢?
s = 'foo'
d = {'a':1}
def f():
s = 'bar'
d['b'] = 2
f()
print s # foo
print d # {'a': 1, 'b': 2} s = 'bar'这句话可以认为是创建新对象'bar'绑定到f函数的名字空间认为是新名称s,或者是解绑外层s的引用,绑定到新对象'bar'
就产生了歧义~~~Python默认是执行第一种,
d['b'] = 2这句话是修改可变对象,不存在创建新对象的问题,没有歧义。
python里面一个方法内是一个名称空间,循环里面不是。旧名字->新对象,会被认为是使用新名称空间,修改可变对象如上例就是被认为是修改了老对象(新对象也没有啊)
-----------------------------------再看下面的代码-------------
list_a = []
def a():
list_a = [1] ## 语句1
a()
print list_a # []
print "======================"
list_b = []
def b():
list_b.append(1) ## 语句2
b()
print list_b # [1]
大家可以看到为什么 语句1 不能改变 list_a 的值,而 语句2 却可以?他们的差别在哪呢?
因为 = 创建了局部变量,而 .append() 或者 .extend() 重用了全局变量。
4.函数的传参问题
函数的参数传递也是一个名字与对象绑定的过程。(传参即增加了该对象的引用计数)而且是绑定到另外一个名字空间(即函数内部的名字空间)。
Python所有参数传递都是引用传递,也就是传址。函数内部修改可变对象的值会影响外部
因此在Python中,我们应该抛开传递参数这种概念,时刻牢记函数的调用参数是将对象用另外一个名字空间的名字绑定。在函数中不过是用了另外一个名字,但还是对同一个对象进行操作,。
-------------缺省参数的问题---------
Python的缺省参数暗藏玄机。看下面的代码:
>>> def foo(par=[]):
... par.append(0)
... print par
...
>>> foo() # 第一次调用
[0]
>>> foo() # 第二次调用
[0, 0]
par在执行结束就销毁,两次调用结果不是应该一样吗?为什么会出现这种结果????
问题就出在没有搞清楚缺省参数的生存周期。。。
这都是“对象,名字,绑定”这些思想惹的祸,“万物皆对象”,这里函数foo当然也是一个对象,可以称为函数对象(与一般对象没有什么不同),先看看它的属性
python2.x中
>>> dir(foo)
['__call__', '__class__', '__delattr__', '__dict__', '__doc__', '__get__', '__getattribute__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']
python 3.x中
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
defaults可能与缺省参数有关:看看它的值
foo()
print(foo.__defaults__) # 第一次调用 ([0],)
foo()
print(foo.__defaults__) # 第二次调用([0, 0],)
验证一下:
def foo1(par=[] , st = "s", a = 1):
par.append(0)
print (par, st, a) foo1()
print(foo1.__defaults__) # 第一次调用 ([0], 's', 1)
foo1()
print(foo1.__defaults__) # 第二次调用(([0, 0], 's', 1)
可以看出,这个函数对象的属性__defaults__中存放了这个函数的所有缺省参数。
在函数定义中有几个缺省参数,__defaults__中就会包括几个对象,暂且称之为缺省参数对象(如上列中的[]、“s”和1)。
这些缺省参数对象的生命周期与函数对象相同,从函数使用def定义开始,直到其消亡(如用del)。所以即便是在这些函数没有被调用的时候,但只要定义了,缺省参数对象就会一直存在。(☆☆☆☆☆)
前面讲过,函数调用的过程就是对象在另外一个名字空间的绑定过程。当在每次函数调用时,如果没有传递任何参数给这个缺省参数,那么这个缺省参数的名字就会绑定到在func_defaults中一个对应的缺省参数对象上。
函数foo1内的对象par就会绑定到__defaults__中的第[0]个名称,st绑定到第[1]个,a则是第[2]个。
所以我们看到在函数foo中出现的累加现象,就是由于par绑定到缺省参数对象上,而且它是一个可变对象(列表),par.append(0)就会每次改变这个缺省参数对象的内容。
将函数foo改进一下,可能会更容易帮助理解:
>>> def foo(par=[]):
... print id(par) # 查看该对象的标识码
... par.append(0)
... print par
...
>>> foo.func_defaults # 缺省参数对象的初始值
([],)
>>> id(foo.func_defaults[0]) # 查看第一个缺省参数对象的标识码
11279792 # 你的结果可能会不同
>>> foo()
11279792 # 证明par绑定的对象就是第一个缺省参数对象
[0]
>>> foo()
11279792 # 依旧绑定到第一个缺省参数对象
[0, 0] # 该对象的值发生了变化
>>> b=[1]
>>> id(b)
11279952
>>> foo(b) # 不使用缺省参数
11279952 # 名字par所绑定的对象与外部名字b所绑定的是同一个对象
[1, 0]
>>> foo.func_defaults
([0, 0],) # 缺省参数对象还在那里,而且值并没有发生变化
>>> foo()
11279792 # 名字par又绑定到缺省参数对象上
([0, 0, 0],)
为了预防此类“问题”的发生,python建议采用下列方法:
>>> def foo(par = None):
... if par is None:
... par = []
... par.append(0)
... print par 或者: >>> def foo(par = []):
... if len(args) <= 0:
... par = []
... par.append(0)
... print par 或者: >>> def foo(par = []):
... if not par:
... par = []
... par.append(0)
... print par
永远不要使用可变的默认参数,可以使用None作为哨兵,以判断是否有参数传入,如果没有,就新创建一个新的列表对象,而不是绑定到缺省
参数对象上。
6.总结
* python是一种纯粹的面向对象语言。
* 赋值语句是名字和对象的绑定过程。
* 函数的传参是对象到不同名字空间的绑定。
你相信吗??Python把数字也当做对象!@@@对象,名称绑定,引用计数的更多相关文章
- Python 对象的引用计数和拷贝
Python 对象的引用计数和拷贝 Python是一种面向对象的语言,包括变量.函数.类.模块等等一切皆对象. 在python中,每个对象有以下三个属性: 1.id,每个对象都有一个唯一的身份标识自己 ...
- python赋值和拷贝----一切皆对象,参数皆引用
摘要: 1 python中的一切事物皆为对象,并且规定参数的传递都是对象的引用. 2 python参数传递都是"传对象引用"方式.实际上相当于c++中传值和传引用的结合. 3 如 ...
- Python的垃圾回收机制(引用计数+标记清除+分代回收)
一.写在前面: 我们都知道Python一种面向对象的脚本语言,对象是Python中非常重要的一个概念.在Python中数字是对象,字符串是对象,任何事物都是对象,而它们的核心就是一个结构体--PyOb ...
- python的 del 函数是删对象还是删引用
1.首先介绍下python的对象引用 1)Python中不存在传值调用,一切传递的都是对象引用,也可以认为是传址调用.即Python不允许程序员选择采用传值或传引用.Python参数传递采用的是“传对 ...
- 【python测试开发栈】python内存管理机制(一)—引用计数
什么是内存 在开始进入正题之前,我们先来回忆下,计算机基础原理的知识,为什么需要内存.我们都知道计算机的CPU相当于人类的大脑,其运算速度非常的快,而我们平时写的数据,比如:文档.代码等都是存储在磁盘 ...
- python基础——数字&集合&布尔类型
Python的核心数据类型 内置对象 对象类型 例子 数字 123,3.1415,3+4j,Decimal(小数),Fraction(分数) 字符串 'dodo',"guido's" ...
- python中数字类型与处理工具
python中的数字类型工具 python中为更高级的工作提供很多高级数字编程支持和对象,其中数字类型的完整工具包括: 1.整数与浮点型, 2.复数, 3.固定精度十进制数, 4.有理分数, 5.集合 ...
- python 基础-----数字,字符串,列表,字典类型简单介绍
一.第一个python小程序 1.下载安装python2.7和python3.6的版本及pycharm,我们可以再解释器中输入这样一行代码: 则相应的就打出了一句话.这里的print是打印的意思.你输 ...
- 【笔记】基于Python的数字图像处理
[博客导航] [Python相关] 前言 基于Python的数字图像处理,离不开相关处理的第三方库函数.搜索网络资源,列出如下资源链接. Python图像处理库到底用哪家 python计算机视觉编程— ...
随机推荐
- bzoj 1826 缓存交换
题目大意: 一些数,需要将他们依次取入一个集合中 集合的容量有限 若该数已经在集合中则不用再取 每次取的时候可以将集合中与元素替换为另一个,也可以不换,直接加入前提是不超过集合容量 求最后最小的取数次 ...
- bzoj1854 [Scoi2010]游戏——匈牙利算法
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1854 这题...据说可以用并查集做,但没有去看... 用二分图匹配的话,就把装备和它的两个属 ...
- EasyUI之树形结构tree
转自:https://blog.csdn.net/ya_1249463314/article/details/70305730 版权声明:本文为博主原创文章,未经博主允许不得转载. https://b ...
- bzoj 4808: 马【匈牙利算法】
网格图黑白染色,然后能互相攻击到的点之间连边,跑匈牙利算法最大匹配,答案是好点个数-最大匹配(最大独立集) 注意pao的时候只从一种颜色的格子统计即可 #include<iostream> ...
- linux下jdk环境变量配置深度分析----解决环境变量不生效的问题
1.linux下jdk环境变量配置 是否需要配置环境变量,主要看java -version 显示的版本是否为你期望的版本 1.1 不需要配置环境变量的情况 使用java -version查看,版本显示 ...
- LOJ#510. 「LibreOJ NOI Round #1」北校门外的回忆(线段树)
题面 传送门 题解 感谢\(@M\_sea\)的代码我总算看懂题解了-- 这个操作的本质就是每次把\(x\)的\(k\)进制最低位乘\(2\)并进位,根据基本同余芝士如果\(k\)是奇数那么最低位永远 ...
- spring Cache /Redis 缓存 + Spring 的集成示例
spring Cache https://www.ibm.com/developerworks/cn/opensource/os-cn-spring-cache/ spring+redis 缓存 ht ...
- linux rpm 安装
1.rpm 安装rpm -ivh package_name-i:install的意思-v:查看更详细的安装信息-h:以安装信息栏显示安装进度rpm -ivh package_name --test 2 ...
- jquery 菜单展开与收缩参考脚本
/* * metismenu - v1.1.3 * Easy menu jQuery plugin for Twitter Bootstrap 3 * https://github.com/onoku ...
- 372 Super Pow 超级次方
你的任务是计算 ab 对 1337 取模,a 是一个正整数,b 是一个非常大的正整数且会以数组形式给出.示例 1:a = 2b = [3]结果: 8示例 2:a = 2b = [1,0]结果: 102 ...