怎么说万物皆对象呢?最常见的:
> class A: pass > a = A()
我们说a是一个对象。
那么既然是万物了,其实A也是对象。3 也是对象。True 也是对象。"hello" 也是对象。
> def Func(): pass
o~yee, Func 也是对象。
那么对象之间的传递是如何呢?我们看看下面两个简单的例子:
> a = 3 > b = a > b = 3 + 1 > print b 4 > print a 3 > a = [] > b = a > b.append(1) > print a [1] > print b [1]
不是都说python所有对象都是引用传递吗?为毛第一个b不是3?
好吧。事实是,在python的实现上,对象分为mutable 和 immutable。
这里说的对象分类,是说在实现上具备这样的特性。而非对象本身的属性。
什么是immutable?表示对象本身不可改变。这里先记住一点,是对象 本身 不可改变。
什么叫做对象本身不可改变呢?
一个简单的例子:
> a = (1,2,3) > a[0] = 10
TypeError: 'tuple' object does not support item assignment
元组的元素在初始化后就不能再被改变。也就是说,元组对象具备immutable的特性。
那么很简单,相对的,mutable 就是可变的。比如:
> a = {} > a[0] = 10
有了上面的两个例子,相信大家已经有了基本的认识。
那么,在python世界中,哪些是具备immutable特性,哪些又是mutable的呢?
简单讲,基本类型都是immutable, 而object都是mutable的。
比如说:int, float, bool, tuple 都是immutable。
再比如:dict, set, list, classinstance 都是mutable的。
那么问题来了。既然说基本类型是 immutable ,那么最上面的 b = 3 + 1 为什么不会像tuple一样,抛异常呢?
原因在于,int 对+操作会执行自己的__add__方法。而__add__方法会返回一个新的对象。
事实是,当基本类型被改变时,并不是改变其自身,而是创建了一个新的对象。最终返回的是新的对象的引用。
怎么证明?
我们可以使用一个叫做id()的函数。该函数会返回对象的一个唯一id(目前的实现可以间接理解为对象的内存地址)。
那么我们看下:
> a = 3 > id(a) 140248135804168 > id(3) 140248135804168 > id(4) 140248135804144 > a = a + 1 > id(a) 140248135804144
you see ? 当我们执行a=a+1 后,id(a) 已经改变了。
深究一点,为什么会这样呢?
其实,a = a + 1 经历了两个过程:
- 1、a + 1
- 2、a 赋值
第2步只是一个引用的改变。重点在第1步。a + 1,那么python实际上会调用a.__add__(1)。
对于int类型__add__函数的实现逻辑,是创建了一个新的int对象,并返回。
不知道细心的你有没有发现一个特别的地方?
id(4)的值等于id(3+1) 。这个只是python对int,和bool做的特殊优化。不要以为其他基本类型只要值一样都会指向相同的对象。
有个特殊的例子,str。做个简单的实验:
> a = "hello" > id(a) 4365413232 > b = "hell" > id(b) 4365386208 > id(a[:-1]) 4365410928 > id(a[:-1]) 4365413760
看到了吗?虽然值相同,但是还是指向(创建)了不同的对象,尤其是最后两句,哪怕执行相同的操作,依然创建了不同的对象。
python这么傻,每次都创建新的对象?
no no no 他只是缓存了“一些”结果。我们可以再试试看:
> a = "hello" > ret = set() > for i in range(1000): ret.add(id(a[:-1])) > print ret {4388133312, 4388204640}
看到了吗?python还是挺聪明的。不过具体的缓存机制我没有深究过,期望有同学能分享下。
再次回到我们的主题,python中参数是如何传递的?
答案是,引用传递。
平时使用静态语言的同学(比如我),可能会用下面的例子挑战我了:
def fun(data): data = 3 a = 100 func(a) print a # 100
不是尼玛引用传递吗?为毛在执行func(a)后,a 的值没有改变呢?这里犯了一个动态语言基本的错误。
data=3,语义上是动态语言的赋值语句。千万不要和C++之类的语言一个理解。
看看我们传入一个mutable 的对象:
> def func(m): m[3] = 100 > a = {} > print a {} > func(a) > print a {3:100}
现在同学们知道该如何进行参数传递了吧?好嘞,进阶!
像很多语言如C++,js,swift... 一样,python 的函数声明支持默认参数:
def func(a=[]): pass
不知道什么意思?自己看书去!
我这里要说的是,如果我们的默认参数是mutable类型的对象,会有什么黑魔法产产生?
我们看看下面的函数:
def func(a=[]): a.append(3) return a
可能有同学会说了:我去!这么简单?来骗代码的吧?
但是,真的这么简单吗?我们看下下面的调用结果:
> print func() [3] > print func() [3,3] > print func() [3,3,3]
这真的是你想要的结果吗?
No,我要的是[3],[3],[3]!
原因?好吧,我们再用下id()神奇看看:
def func(a=[]): print id(a) a.append(3) return a > print func() 4365426272 [3] > print func() 4365426272 [3, 3] > print func() 4365426272 [3, 3, 3]
明白没?原来在python中,*默认参数不是每次执行时都创建的!*
这下你再想想,曾经嘲笑过的代码(至少我)为什么要 多此一举:
def func(a=None): if a is None: a = []
这里在顺带提一下==, is:
== : 值比较
is : 比较左右两边是否是同一个对象。 a is b ==> id(a) == id(b)
ok, let's move on!
我们都知道,在python中,不定参数我们可以这样定义:
def func(*args, **kv): pass
什么你不知道?看书去!
那args和kv到底是什么情况呢?到底是mutable 还是 immutable 呢?
再一次请出id()神器:
def func(*args): print id(args) > a = [1,2] > print id(a) 4364874816 > func(*a) 4364698832 > func(*a) 4364701496
看到了吧?实际上args也会产生一个新的对象。但是值是填入的传入参数。那么每一个item也会复制吗?
我们再看看:
def func(*args): print id(args[0]) > a = [1,2] > print id(a[0]) 140248135804216 > func(*a) 140248135804216
答案是,No。值会像普通list赋值一样,指向原先list(a)所引用的对象。
那么为什么会这样呢?
python的源码就是这么写的.......
最最后,还记得我说过的一句话吗?
immutable 限制的是对象本身不可变
意思就是说,对象的immtable 只是限制自身的属性能否被改变,而不会影响到其引用的对象。
看下下面的例子:
> a = [1,2] > b = (a,3) > b[1] = 100 TypeError: 'tuple' object does not support item assignment > print b ([1, 2], 3) > b[0][0] = 10 > print b ([10, 2], 3)
最最最后,我有个对象,它本身应该是 mutable 的,但是我想让他具备类似immutable的特性,可以吗?
答案是,可以模拟!
还是之前说的,immutable 限制的是其自身属性不能改变。
那么,我们的可以通过重定义(重载)属性改变函数,来模拟immutable特性。
python可以吗?O~Yee
在python的类函数中,有这样的两个函数: __setattr__ 和 __delattr__。分别会在对象属性赋值和删除时执行。
那么我们可以进行简单重载来模拟immutable:
class A: def __setattr__(self, name, val): raise TypeError("immutable object could not set attr")
以上就是为大家介绍的python黑魔法,希望对大家的学习有所帮助。
![PHP小白到大牛直播班第二十期](http://m.sbmmt.com/img/upload/aroundimg/000/000/001/629dc3d03b9a6378.png)
相关文章推荐
• 【腾讯云】年中优惠,「专享618元」优惠券!• 图文详解Python冒泡排序算法• Python 3.11中的最佳新功能和功能修复• Python接口自动化测试必备基础之http协议详解• 归纳总结Python函数进阶的使用方法• 实例详解Python面向对象的四大特征独孤九贱(3)_JavaScript视频教程
javascript是运行在浏览器上的脚本语言,连续多年,被评为全球最受欢迎的编程语言。是前端开发必备三大法器中,最具杀伤力。如果前端开发是降龙十八掌,好么javascript就是第18掌:亢龙有悔。没有它,你的前端生涯是不完整的。《php.cn独孤九贱(3)-JavaScript视频教程》课程特色:php中文网原创幽默段子系列课程,以恶搞,段子为主题风格的php视频教程!轻松的教学风格,简短的教学模式,让同学们在不知不觉中,学会了javascript知识。
JavaScript教程125914次播放
独孤九贱(6)_jQuery视频教程
jQuery是一个快速、简洁的JavaScript框架。设计的宗旨是“write Less,Do More”,即倡导写更少的代码,做更多的事情。它封装JavaScript常用的功能代码,提供一种简便的JavaScript设计模式,优化HTML文档操作、事件处理、动画设计和Ajax交互。 核心特性可以总结为:具有独特的链式语法和短小清晰的多功能接口;具有高效灵活的css选择器,并且可对CSS选择器进行扩展;拥有便捷的插件扩展机制和丰富的插件。兼容各种主流浏览器,如IE 6.0+、FF 1.5+、Safari 2.0+、Opera 9.0+等,是全球最流行的前端开发框架之一。PHP中文网根据最新版本,独家录制jQuery最新视频教程,回馈PHP中文网的新老用户。
jQuery教程104140次播放
jQuery与Ajax基础与实战
jQuery是最流行的JS函数库,封装了许多实用的功能,其中最引人入胜的就是Ajax。 jQuery中的Ajax操作,语法简单,操作方便,使Ajax从未如此轻松,前端人员从此不再为与服务器异步交互而发愁,本套课程,精选了最常用的几个方法,从基本的语法到每个参数,再到具体实例进行了全面的讲解。
AJAX教程5209次播放
Git教程(60分钟全程无废话版)
Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。 Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。 Git 与常用的版本控制工具 CVS, Subversion 等不同,它采用了分布式版本库的方式,不必服务器端软件支持
JavaScript教程4480次播放