1 Python中的类型与对象

类型与对象

转载请标明出处(http://blog.csdn.net/lis_12/article/details/52693637)

1 术语

程序中所有数据都是对象(实例),每个对象都有一个身份,一个类型和一个值.如a = 10,使用值10创建一个整数对象,对象的身份可以看做它在内存中所处位置的指针(id(a)获取),a就是引用这个位置的名称.

实例的类型决定了实例内部的表示以及它支持的方法.

实例被创建后,它的身份和类型就不可改变,如果对象的值是可以修改的称为可变对象(list,dict);如果对象的值不可修改称为不可变对象(int,str,tuple).

2 身份与类型

a = 10,b = 10

  1. 身份的获取,id(a);

  2. 类型的获取,type(a)

  3. is用来比较地址是否相同,即两个对象身份是否相同,==用来比较两个对象的值是否相同;

  4. type(a) is type(b) # true

    对象的类型本身也是一个对象,称为对象的类,该对象的定义是唯一的,对于某个类型的所有实例都是相同的.

  5. 检查某个实例的类型最佳方式是用,isinstance(object,(int,str,list…..))

3 引用计数与垃圾收集

所有对象都有引用计数,无论是给一个对象分配一个新名称,还是放入容器当中,都会让该对象的引用计数增加.

a = 10      #创建一个值为10的对象
b = a       #增加10的引用计数
c = []     
c.append[a] #增加10的引用计数
#上面只有一个包含10的对象,其他操作都只是创建了该对象的引用.
del a       #减少10的引用计数,此时a也就无法访问了
b = 10      #减少10的引用计数
c[0] = 5    #减少10的引用计数
'''
使用del或者重新赋值时,对象的引用计数会减少
'''
print sys.getrefcount(b) #查看b的引用次数,一般情况下会比预想情况多- -
a = {}
b = {}
a['a'] = b
b['b'] = a
del a
del b
'''
虽然del语句销毁了a与b,但是他们相互引用,所以引用计数不会归零,对象也不会销毁(从而导致内存泄漏),为了解决这个问题,解释器会有一个循环检测器,搜索不可访问对象并删除.
'''

4 引用和复制

a = 1
b = 1
print id(a)       #0
print id(b)       #0
#a与b的地址相同,因为1是不可变对象,不可改变,a和b都是对1的引用.

a = range(10)
b = range(10)
print id(a)       #0
print id(b)       #1
#a与b的地址不同,因为list是可变对象,可变对象是可修改的
for i,j in zip(a,b):
    print id(i) == id(j)
#result:10个True,因为list里面的对象都是不可变对象,a和b里面的元素分别引用了0,1,...,8,9
#可变对象
a = range(4)
b = a
b[1] = 100
print a    #[0,100,2,4]

#不可变对象
c = 1
d = c
d = 2
print c    #1

a,b引用的是同一个对象,修改其中任何一个都会影响另一个,为了避免这种情况,必须创建对象的副本而不是新的引用.

可变对象的复制分为浅,深复制.

  • 浅复制将创建一个新对象,但它包含的是对原始对象中包含的项的引用.
a = [0,[1,2]]
b = a      #此时b就相当于a的别名
c = list(a)#创建a的一个浅复制
a is b     #True
a is c     #False
b.append(2)
c.append(3)
print a    #[0, [1, 2], 2]
print b    #[0, [1, 2], 2]
print c    #[0, [1, 2], 3]
c[1][0] = 10
print a    #[0, [10, 2], 2]
print b    #[0, [10, 2], 2]
print c    #[0, [10, 2], 3]

a[1][0]=20 #可变对象
print a    #[0, [20, 2], 2]
print b    #[0, [20, 2], 2]
print c    #[0, [20, 2], 3]

a[0] = 3   #不可变对象
print a    #[3, [20, 2], 2]
print b    #[3, [20, 2], 2]
print c    #[0, [20, 2], 2]

a和c是单独的列表对象,但是他们包含的公共元素是共享的,修改a的元素就会修改c(可变类型才会变哦,如果是不可变类型会新建对象的).

  • 深复制将创建一个新对象,并且递归地复制它包含的所有对象.可以使用copy.deepcopy()完成该工作.
import copy
a = [0,[1,2]]
b = copy.deepcopy(a)
b.append(2)
print a    #[0, [1, 2]]
print b    #[0, [1, 2], 2]
b[1][0] = 10
print a    #[0, [1, 2]]
print b    #[0, [10, 2], 2]

b[0] = 3
print a    #[0, [1, 2]]
print b    #[3, [10, 2], 2]

5 数据类型

NoneType(None无任何属性,值为false)
数字int,Long,float,cmplex,bool
序列str,unicode,list,tuple,xrange
映射dict
集合set(可变集合),frozenset(不可变集合)

5.1 None

None类型表示一个null对象,如果一个函数没有显式地返回值,则返回None,如果None在布尔表达式求值时为False。

5.2 序列

序列表示索引为非负值整数的有序对象集合(list,tuple,str).字符串是字符的序列,而列表和元组是任意Python对象的序列,字符串和元组是不可变的,而列表支持插入,删除,替换元素.索引序列都支持迭代.

1) 序列通用操作
  1. s[i]
  2. s[i:j] 切片,返回s[i]至s[j-1]的元素
  3. s[i:j:step],返回s[i],s[i+step],s[i+2*step],…..直至i + n * step >= j,如a = range(10) a[0:5:2] = [0,2,4],
    如果i < j,step < 0,则返回[],(step不能为0)
  4. len(s)
  5. min(s),max(s)
  6. sum(s)
  7. all(s),检查s中所有项是否为True
  8. any(s),检查s中任意项为True,有一个元素为真则返回True
  9. s[i] = value
  10. s[i:j] = t,切片赋值,
  11. s[i:j:step],扩展切片赋值
a = range(5)
print a[0:5:2]         #[0,2,4]
print a[0:5:-1]        #[]
print a[4:0:-1]        #[4,3,2,1]
a = range(5)
a[0:2]  = [10]
print a                #[10,2,3,4]
a[0:3]  =  range(10)   #[1,2,3,4,5,6,7,8,9,4]

a = range(5)
a[0:5:2] = [10,20,30]  #个数必须相同
print a                #[10, 1, 20, 3, 30]
2) 列表,特有的函数

内置函数list(s)可以将任意可迭代对象转换为列表,如果s已经是一个列表,则该函数构造的新列表是s的一个浅复制.

list(s)将s转换为一个列表,浅复制
s.append(x)x添加到s尾部
s.extend(x)将列表x添加到x尾部
s.count(x)计算s中x的出现次数
s.index(x)搜索s中首次出现的x,并且返回索引,如果没有则会触发异常
s.insert(i,x)在索引i处插入x
s.pop([i])返回索引i处的元素并删除,如果省略i则返回最后一个元素并且删除
s.remove(x)从s中搜索x并删除
s.reverse()翻转s
s.sort(cmp=None, key=None, reverse=False)cmp比较规则,key在进行比较之前应用于每个元素的函数,reverse是否反转
a = [0,5,3,6,9,4]
f = lambda a:a%8
a.sort(key = f)
#[0, 9, 3, 4, 5, 6]
3) 字符串,特有的函数

通用操作虽然都是字符串实例,但是他们并不会修改原始的字符串数据(因为是不可变类型啊).

可利用help(str)查看所有方法

操作解释return
s.capitalize(),capitalize = 以大写字母写首字母大写string
s.center(width[, fillchar])在长度为width的字段内将字符串s居中,fillchar是填充字符(默认为空格),如果width还没有字符串本身长的话直接返回字符串s本身string
s.count(sub[, start[, end]])返回sub在s中出现的次数,start,end为s的范围int
s.decode([encoding[,errors]])编码,errors为出现错误时候的处理情况(默认为’strict’,还有’ignore’, ‘replace’ and’xmlcharrefreplace’)object
s.encode([encoding[,errors]])解码object
s.endswith(suffix[, start[, end]])检查字符串s是否以suffix结尾bool
s.expandtabs([tabsize])使用空格替换制表符string
s.find(sub [,start [,end]])找到指定的子串首次出现的位置,未找到返回-1int
s.format(*args, **kwargs)格式化sstring
s.index(sub [,start [,end]])找到指定的子串首次出现的位置,否则报异常int
s.isalnum()检查所有字符是否都为字母或者数字bool
s.isalpha()检查所有字符是否都为字母bool
s.isdigit()检查所有字符是否都为数字bool
s.islower()检查所有字符是否都为小写bool
s.isspace()检查所有字符是否都为空白bool
s.istitle()检查字符串是否为标题字符串(每个单词的首字母大写)bool
s.isupper()检查所有字符是否都为大写bool
s.join(iterable)使用s作为分隔符连接迭代器中的字符串(必须为字符串)string
s.ljust(width[, fillchar])在长度为width的字符串内左对齐s,右侧用fillchar来填充string
s.lower()转化为小写string
s.lstrip([chars]),strip = 剥夺,去掉从索引0处开始,去掉字符串s中出现在chars中出现的字符,直至s中的字符不在chars中,chars默认为空格(从左侧开始,去掉s中出现在chars中的字符,当s中出现了不在chars中的字符,则停止,详细使用见代码)string
s.partition(sep),partition = 划分,分割,区分使用seq将字符串s划分,返回(head,seq,tail),找到后停止查找,如果未找到seq,则返回(s,”,”),详见代码(head, sep, tail)
s.replace(old, new[, count])将出现在s中的old子串用new替换,count为次数,默认为全部替换string
s.rfind(sub [,start [,end]])找到一个子串最后出现的位置,未找到返回-1int
s.rindex(sub [,start [,end]])找到一个子串最后出现的位置,否则报错int
s.rjust(width[, fillchar])在长度为width的字符串内右对齐s,左侧用fillchar来填充string
s.rpartition(sep)s.partition(sep)相反string
s.rsplit([sep [,maxsplit]])从右侧,将s用sep拆分,如果省略了maxsplit则结果与split一样,(划分方向不一样)list
s.rstrip([chars])s.lstrip([chars])相反string
s.split([sep [,maxsplit]])从左侧,将s用sep拆分,maxsplit为划分次数list
s.splitlines(keepends=False)将字符串分为一个行列表,如果keepends为1,则保留最后的换行符(以’\n’分割)list
s.startswith(prefix[, start[, end]])检查s是否以prefix开头bool
s.strip([chars])删除s的开头和结尾出现在chrs中的字符,如果出现了一个不匹配的则停止string
s.swapcase()将大写转换为小写,小写转换为大写string
s.title()将s转换为标题格式,即每个单词的首字母大写string
s.translate(table [,deletechars])根据table给出的表(包含256个字符)转换string的字符,要过滤掉的字符放在deletechars中(感觉有点像加密- -)string
s.upper()转化为大写string
s.zfill(width)在s左边填充0,直至宽度为width,和rjust(width,’0’)功能一样a,如果width没有s长的话直接返回sstring
a = '123123'          
b = '1234123'
c = ' 123123'
d = '123444451\n222\n2'
#测试lstrip([chars])
print a.lstrip()      #123123
print c.lstrip()      #123123,去掉了空格
print a.lstrip('123') #空,去掉了123123
print b.lstrip('123') #4123,去掉了123
print c.lstrip('123') # 123123,啥都没去掉

#测试partition
print a.partition('23') #('1', '23', '123')
print a.partition('3')  #('12', '3', '123')

#rsplit
print b.rsplit('3')     #['12', '12', '']
print b.rsplit('3')     #['12', '412', '']

#splitlines
print d.splitlines()    #['123444451', '222', '2']

#format
a = "{0}-{1}-{1}-{2}"
a.format(4,5,6)         #'4-5-5-6'
4) xrange()

xrange([i,]j[,step])创建的对象表示一个整数范围k,如果i<= k < j.

i,step是可选的默认值为0和1.

很像list,但是并不是list,不支持切片等操作

a = list(xrange(0,10,2))  #[0,2,4,6,8]
print type(a)             #<type 'xrange'>
5) 映射类型dict

dict,无序,任何不可变对象都可以用作字典键值,包含可变对象的列表,字典,元组不能作为键值,因为字典类型要求键值保持不变.

a = ([1,2],3)     #a是元组不可变
print id(a[0])    #48835016
print id(a[0][0]) #31359704
a[0][0] = 100
print id(a[0])    #48835016 与上述一样
print id(a[0][0]) #31361312,改变了哦
print a           
'''([100,2],3),我擦改变了!因为tuple是不可变类型,a[0]未改变,符合tuple要求,
但是list是可变类型啊,只要不改变a[0]的引用即可(a[0] = range(5)这样是不可以的哦),对list内部的操作是可以的(如a[0].append(3).
这就是为什么键值不能为包含可变类型的不可变类型的对象
'''
a[0].append(3)
print a            #([100, 2, 3], 3)

dict内置方法

内置方法含义
len(D)键-值个数
D[k]键k对应的值
D[k] = x键k的值改为x
del D[k]删除键k
k in D如果k在D的键中返回True
D.clear()删除所有元素
D.copy()浅复制字典的副本
dict.fromkeys(seq,val = None)创建一个新字典,seq(list,tuple)中的元素作为键,值为val
D.get(k,default = None)如果键k在D中则返回对应的值,否则返回default
D.has_key(k)查看键k是否在D中
D.items()返回由(key,value)组成的list
D.iteritems()返回由(key,value)组成的迭代器
D.iterkeys()返回由key组成的迭代器
D.itervalues()返回由value组成的迭代器
D.keys()返回由key组成的list
D.pop(k,[,default])如果找到了D[k],则返回D[k]并删除,否则,如果给了default的值则返回default,如果没有提供,则引发KeyError异常
D.popitem()随机删除一个key-value,并返回成一个元组
D.setdefault(k[,d])如果找到了D[k],返回,否则返回d,并将D[k] = d
D.update(D2)将D2中的所有对象添加到D中
D.values()返回由value组成的list
D.viewitems()查看所有键-值的方法
D.viewkeys()查看所有键的方法
D.viewvalues()查看所有值的方法
6) 集合

集合是唯一项的无序集,无切片操作,放入集合的项目必须是不可变的.

set,可变集合

frozenset,不可变集合

函数含义
s.copy()制作s的一份副本,浅复制
s.different(t)差集,在s不在t
s.difference_update(t)删除s中所有在t中出现的元素
s.discard(mem)删除s中的mem,如果mem不在s中,不作任何操作
s.intersection(t)交集
s.intersection_update(t)将s更新为s与t的交集
s.isdisjoint(t)如果s和t没有相同项,则返回True
s.issubset(t)如果s是t的子集,返回True
s.issuperset(t)如果s是t的一个超集,返回True
s.pop()返回集合中的任意一个元素,并删除
s.remove(m)删除m,如果m不在s,则报出异常
s.symmetric_difference(t)s与t的对称差集
s.symmetric_difference_update()将s更新为s与t的对称差集
s.union()求并集
s.update(t)将t中所有的元素添加到s中,t可以是一个集合,一个序列,一个可迭代的任意对象

5.3 表示程序结构的内置类型

在Python中,函数,类,模块都可以当做数据操作的对象.

类型分类类型名称描述
可调用types.BuiltinFunctionType内置方法或函数
可调用type内置类型和类的类型
可调用object所有类型和类的祖先
可调用types.FunctionType用户定义的函数
可调用types.MethodType类方法
模块types.ModuleType模块
object所有类型和类的祖先
类型type内置类型和类的类型
1) 可调用类型
  1. 用户自定义函数,具有如下属性

    def f():

    ​ pass

属性描述结果
f.__name__函数名称‘f’
f.__dict__包含函数属性的字典{}
f.__code__字节编译的代码
2) 类,类型与实例

定义类的时候,通常会生成一个type类型的对象

class Foo(object):
    pass
type(Foo)  # <type 'type'>

下表表示一个类型对象Foo = t的常用属性

属性描述
t.__doc__文档字符串
t.__name__类名称
t.__bases__由基类组成的元组
t.__dict__保存类方法和变量的字典
t.__module__定义类的模块名称,也就是文件名字,如果是在本本文件调用的话就是__main__
t.__abstractmethods__抽象方法名称的集合,如果不存在抽象方法,就是未定义

下面为实例 i = Foo()的特殊属性

i.__class__实例所属的类
i.__dict__保存实例数据的字典

__dict__属性通常用于存储与一个实例相关的所有数据.就像i.var = value,这样的值就会保存在dict里面.但是如果用户使用的类使用了__slots__,就会使用一种更加有效的内部表示,实例也不会有__dict__属性.

__slots__是一个类变量,由一序列型对象组成,由所有合法标识构成的实例属性来表示.任何试图创建其名不在__slots__中的名字的实例属性都将导致AttributeError异常.

3) 模块

模块类型就是一个容易,可保存使用import语句加载的对象.例如,import foo,就会把名称foo模块的属性赋给调用的模块.可用属性如下:

属性描述
m.__doc__模块文档字符串
m.__dict__模块相关的字典
m.__name__模块名称
m.__file__用于加载模块的文件
m.__path__完全限定包名,只在模块对象引用包时定义

5.4 对象行为和特殊方法

Python中的对象通常根据它们的行为和实现的功能进行分类.例如,所有序列类型放在一组,如list,tuple,因为他们都支持一系列相同操作,如s[n],len(s)等.所有基本的解释器操作都通过特殊的对象方法来实现.

特殊的方法名称前后始终带有双下划线__.当程序执行时,这些方法都由解释器自动触发.如x + y被映射为内部方法 x.__add__(y),x[k]映射为x.__getitem__(k).

每种数据类型的行为完全取决于它实现的一组特殊方法.

《对象行为完全取决于特殊方法啊= =》

1) 对象的创建与销毁
方法描述
__new__(cls[,*args[,**kwargs])创建新实例时调用的类方法
__init__(self[,*args[,**kwargs])初始化新实例时调用
__del__(self)销毁实例时候调用

调用A(args)创建对象时候,会进行以下步骤:

  1. x = A.__new__(A,args)
  2. is isinstance(x,A): x.__init__(args)

自定义的对象中,很少定义__new__()或者__del__()方法.

  • new通常只定义在元类或继承不可变类型之一的用户自定义对象中.
  • del方法只在有某种关键资源管理问题的情况才会定义,如释放锁定或关闭连接.
2) 对象的字符串表示
方法描述
__format__(self,format_spec)创建格式化后的表示
__repr__(self)创建对象的字符串表示
__str__(self)创建简单的字符串表示

__repr__(),通常返回一个表达式字符串,可对该字符串重新求值以重新创建对象.

如果无法创建字符串表达式,repr()返回一个\<_message>形式的字符串

a = range(3)  #[0,1,2]
s = repr(a)   #'[0,1,2]'
b = eval(s)   #[0,1,2]
class a(object):
    pass
b = a()
print a.__repr__()  # error
print b.__repr__()  #'<__main__.a object at 0x0000000002EF7EF0>'

__str__()与__repr__()的区别是它返回的字符串更加简明易懂.如果该方法未定义就会调用__repr__()方法

__format__ , “{0}a{0}b{1}”.format(123,456,789) 等价于

“{0}a{0}b{1}”.__format__(123,456,789)

3) 对象比较与排序

对象测试与散列的特殊方法.

方法属性
__bool__(self)用于真值测试,返回值为True或False.如果该方法未定义,Python将调用__len__()方法来确定其对象的真值.
__hash__(self)定义在要作字典中键的对象上.如果两个对象比较厚相等,作为返回值得整数应该完全相同.可变对象不应该定义该方法,因为对一个对象所做的任何改动都将改变散列值,从而在后续的字典查找中无法定位对象.

用于比较的方法见下表:

方法结果
__lt__(self,other)self < other
__le__(self,other)self <= other
__gt__(self,other)self > other
__ge__(self,other)self >= other
__eq__(self,other)self == other
__ne__(self,other)self < other
4) 类型检查
方法结果
__instancecheck__(cls,object)isinstance(object,cls)
__subclasscheck__(self,other)issubclass(sub,cls)
5) 属性访问
方法描述
__getattribute__(self,name)返回self.name
__getattr__(self,name)返回属性self.name,如果通过常规属性查找未找到属性,则引发AttributeError异常
__setattr__(self,name,name)设置self.name = value
__delattr__(self,name)删除属性self.name

访问属性时始终会调用__getattribute__()方法,如果找到属性则返回之,否则调用__getattr__()方法来触发异常.设置属性会调用__setattr__()方法,删除属性时候则会调用__delattr__()方法.

6) 属性包装与描述符

描述符的__get__(),__set__()和__delete__()方法用于同类型的__getattribute__(),__setattr__(),__delattr__()方法进行交互.如果在用户自定义类的主体中放入一个描述符对象的实体,这种交互就会发生.

7) 序列与映射方法

如果对象要模拟序列和映射对象的行为,用下表的方法.

方法描述
__len__(self)返回self的长度
__getitem__(self,key)返回self[key]
__setitem__(self,key,value)设置self[key] =value
__delitem__(self,key)删除self[key]
__contains__(self,obj)如果在obj在self中,则返回True,否则False
a = [0,1,2,3,4,5,6]
len(a)         #a.__len__()
a[2]           #a.__getitem__(2)
a[0:4]         #a.__getitem__(slice(0,4,None)),slice对象描述切片范围的属性
a[1] = 7       #a.__setitem__(1,7)
del a[1]       #a.__delitem__(1)
5 in a         #a.__contains__(5)
8) 迭代器

如果对象obj支持迭代,它必须提供方法obj.__iter__(),该方法返回一个迭代器对象.迭代器对象iter必须返回一个方法iter.next(),该方法返回下一个对象,迭代结束引发异常.

class A(object):
    def __init__(self):
        self.num = 10

    def __iter__(self):
        return self

    def next(self):
        if self.num > 0:
            self.num -= 1
            return self.num
        else:
            raise StopIteration


a = A()
b = iter(a)
print b.next()  # 9
for i in b:
    print i     #8,7,6,5,4,3,2,1,0
#for遇到StopIteration会停止循环
9) 可调用接口

对象可提供__call__(self[,*arg[,**kwargs])方法可模拟函数的行为.如果一个对象x提供了该方法,就可以像函数一样调用它.就是说x(arg1,arg2….)等同于调用了x.__call__(self,arg1,arg2…).模拟函数的对象可以用于创建仿函数或者代理.

10) 上下文管理协议

with operate as var:

​ statements

执行with语句时,就会调用__enter__()方法,该方法的返回值将被放入var中,离开with语句的时候回调用__exit__()方法.__exit__()方法接收当前异常的类型、值和跟踪作为参数.如果没有要处理的错误,所有三个值都将被置为None.

11) 对象检查dir

__dir__(self)获得对象可用函数名称列表.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值