[Python] 类型与对象
程序中所存储的所有数据都是对象。每个对象都有一个身份、一个类型和一个值。对象的身份可以看作是指向它在内存中所处位置的指针,变量名就是引用这个具体位置的名称。
对象的类型也称作类别,用于描述对象的内部表示及它支持的方法与操作。创建特定类型的对象时,有时也将该对象称为该类型的实例。实例被创建之后,它的身份和类型就不可改变。如果对象的值是可以修改的,称为可变对象,反之称为不变对象。如果某个对象包含对其他对象的引用,则将其称为容器或集合。
大多数对象拥有大量特有的数据属性和方法。属性就是与对象相关的值。方法就是被调用时将在对象上执行某些操作的函数。使用点"."运算符可以访问属性和方法。
内置函数id()可返回一个对象的身份,返回值为整数。is运算符用于比较两个对象的身份。内置函数type()则返回一个对象的类型。例如:
def compare(a, b): if a is b: # 同一个对象 if a == b: # 具有相同的值 if type(a) is type(b): # 具有相同类型
对象的类型本身也是一个对象,称为对象的类。所有类型对象都有一个指定的名称,可用于执行类型检查,例如:
if type(s) is list: s.append(item)if type(d) is dict: d.update(t)
检查类型的更佳方式是用内置函数isinstance(object, type),例如:
if isinstance(s, list): s.append(item)if isinstance(d, dict): d.update(t)
因为isinstance()函数能够实现继承,因此是检查所有Python对象类型的首选方式。
所有对象都有引用计数。无论是给对象分配一个新名称,还是将其放入一个容器,该对象的引用计数就会增加,例如:
a = 37 # 创建值为37的对象b = a # 增加37的引用计数c = []c.append(b) #增加37的引用计数
这个例子创建了一个包含值37的对象,a只是引用这个新创建对象的一个名称,将a赋值给b时,b就成了同一对象的新名称,而且该对象的引用计数会增加。类似地,将b放到一个列表中时,该对象的引用计数将再次增加。
使用del语句或者引用超出作用域时(或者被重新赋值),对象的引用计数就会减少,例如:
del a # 减少37的引用计数b = 42 #减少37的引用计数c[0] = 2.0 #减少37的引用计数
使用sys.getrefcount()函数可以获得对象的当前引用计数,例如:
a = 37import sysprint(sys.getrefcount(a))
多数情况下,引用计数比猜测的要大得多,对于不可变数据(如数字和字符串),解释器会主动在程序的不同部分共享对象,以便节约内存。
当一个对象的引用计数归零时,它将被垃圾收集机制处理掉。在某些情况下,很多已不再使用的对象间可能存在循环依赖关系,例如:
a = {}b = {}a['b'] = bb['a'] = adel adel b
在以上例子中,del语句将会减少a和b的引用计数,并销毁用于引用底层对象的名称。然而因为每个对象都包含一个对其他对象的引用,所以引用计数不会归零,对象也不会被销毁,从而导致内存泄露。为了解决这个问题,解释器会定期执行一个循环检测器,搜索不可访问对象的循环并删除它们。
在程序进行像a = b这样的赋值时,就会创建一个对b的引用。对于像数字和字符串这样的不可变对象,这种赋值实际上创建了b的一个副本。然而,对于可变对象(如列表和字典)引用行为会完全不同,例如:
a = [1, 2, 3, 4]b = aprint(b is a) # Trueb[2] = -100print(a[2]) #-100
因为a和b引用的同一个对象,修改其中任意一个变量都会影响到另一个。所以必须创建对象的副本而不是新的引用。对于像列表和字典这样的容器对象,可以使用两种复制操作: 浅复制和深复制。浅复制将创建一个新对象,但它包含的是对原始对象中包含的项的引用,例如:
a = [1, 2, [3, 4]]b = list(a)print(b is a) #Falseb.append(100)print(b) # [1, 2, [3, 4], 100]print(a) # [1, 2, [3, 4]]b[2][0] = -100print(b) # [1, 2, [-100, 4], 100]print(a) # [1, 2, [-100, 4]]
深复制将创建一个新对象,并且递归地复制它包含的所有对象。可以使用标准库中的copy.deepcopy()函数完成该工作,例如:
import copya = [1, 2, [3, 4]]b = copy.deepcopy(a)b[2][0] = -100print(b) # [1, 2, [-100, 4]]print(a) # [1, 2, [3, 4]]
5. 表示数据的内置类型
大约有12种数据类型可用于表示程序中用到的大多数数据。如下表所示:
None类型表示一个没有值的对象,在程序中表示为None。如果一个函数没显示返回值,则返回该对象。None常用于可选参数的默认值,以便让函数检测调用者是否为该参数实际传递了值。
Python使用4种数字类型:布尔型、整数、浮点数以及复数。除了布尔值,所有数字对象都是有符号的。所有数字类型都不可变。数字类型拥有大量的属性和方法,可以简化涉及混合算术的运算。为了与有理数兼容,整数使用了属性x.numerator和x.denominator。为了兼容复数,整数或浮点数y拥有属性y.real和y.imag,以及方法y.conjugate()。使用y.as_interger_ratio()可将浮点数y转换为分数形式的一对整数。方法y.is_interger()用于测试浮点数y是否表示整数值。通过方法y.hex()和y.fromhex()可用低级二进制形式使用浮点数。
序列表示索引为非负整数的有序对象集合,包括字符串、列表和元组。所有序列支持的方法如下表:
适用于可变序列的方法如下表:
列表支持的方法如下表:
list(s)可将任意可迭代类型转换为列表。如果s已经是列表,则该函数构造的新列表是s的一个浅复制。
字符串支持的方法如下表:
很多字符串方法都接受可选的start和end参数,其值为整数,用于指定s中起始和结束位置的索引。大多数情况下,这些值可以为负值,表示索引是从字符串结尾处开始计算的。
映射类型表示一个任意对象的集合,而且可以通过另一个几乎是任意键值的集合进行索引。和序列不同,映射对象是无序的,可以通过数字、字符串和其他对象进行索引。映射是可变的。
字典是唯一内置的映射类型,任何不可变对象都可以用作字典键值,如字符串、数字、元组等。字典的方法如下表:
集合是唯一的无序集。与序列不同,集合不提供索引或切片操作。它们和字典也有所区别,即对象不存在相关的键值。放入集合的项目必须是不可变的。集合分为两种类型,set是可变的集合,而frozenset是不可变的集合,这两类集合都是用一对内置函数创建的,例如:
s = set([1, 5, 10, 15])f = frozenset(['a', 37, 'hello'])
所有集合支持的方法如下表:
可变集合还另外提供了一些方法,如下表:
所有的这些操作都可以直接修改集合s。
在Python中,函数、类和模块都可以当做数据操作的对象,如下表:
可调用类型表示支持函数调用操作的对象。具有这种属性的对象有:用户定义的函数,方法、内置函数与方法,可调用的类与实例。
用户定义的函数是指用def语句或lambda运算符在模块级别上创建的可调用对象,它具有以下属性:
方法是在类定义中定义的函数。有3种常见的方法:实例方法、类方法和静态方法。实例方法是操作指定类的实例的方法,实例作为第一个参数传递给方法,根据约定该参数一般称为self。类方法把类本身当作一个对象进行操作,在第一个参数中将类对象传递给类。静态方法就是打包在类中的函数,它不能使用一个实例或类对象作为第一个参数。例如:
f = Foo()meth = f.instance_methodmeth(30)
在以上例子中,meth称为绑定方法。绑定方法是可调用对象,它封装了函数和一个相关实例。调用绑定方法时,实例就会作为第一个参数(self)传递给方法。方法查找也可以出现类本身上,例如:
umeth = Foo.instance_methodumeth(f, 30)
在以下例子中,umeth称为非绑定方法。非绑定方法是封装了方法函数的可调用对象,但需要传递一个正确类型的实例作为第一个参数。如果传递的对象类型错误,就会引发TypeError异常。
为方法对象定义的属性如下表:
类对象和实例也可以当作可调用对象进行操作。类对象使用class语句创建,并作为函数调用,以创建新实例。在这种情况下,将函数的参数传递给类的__init__()方法,以便初始化新创建的实例。如果实例定义了一个特殊方法__call__(),它就能够模拟函数的行为。如果该方法是为某个实例x而定义,使用x(args)语句等同于调用方法x.__call__(args)。
定义类时,类定义通常会生成一个type类型的对象,一个类型对象t的常用属性如下表:
创建一个对象实例时,实例的类型就是定义它的类,例如:
f = Foo()print(type(f)) # <class '__main__.Foo'>
下表显示实例拥有的特殊属性:
模块类型是一个容器,可保存使用import语句加载的对象。模块定义了一个使用字典实现的命名空间,比如,m.x=y等价于m.__dic__["x"]=y。模块的可用属性如下:
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。