1. 术语
程序中所存储的所有数据都是对象. 每个对象都有一个身份, 一个类型和一个值. 对象的身份可以看作是指向它在内存中所处位置的指针, 变量名就是引用这个具体位置的名称.
对象的类型也称作类别, 用于描述对象的内部表示及它支持的方法与操作. 创建特定类型的对象时, 有时也将该对象称为该类型的实例. 实例被创建之后, 它的身份和类型就不可改变. 如果对象的值是可以修改的, 称为可变对象, 反之称为不变对象. 如果某个对象包含对其他对象的引用, 则将其称为容器或集合.
大多数对象拥有大量特有的数据属性和方法. 属性就是与对象相关的值. 方法就是被调用时将在对象上执行某些操作的函数. 使用点 "." 运算符可以访问属性和方法.
2. 对象的身份与类型
内置函数 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 对象类型的首选方式.
3. 引用计数与垃圾收集
所有对象都有引用计数. 无论是给对象分配一个新名称, 还是将其放入一个容器, 该对象的引用计数就会增加, 例如:
- 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 = 37
- import sys
- print(sys.getrefcount(a))
多数情况下, 引用计数比猜测的要大得多, 对于不可变数据 (如数字和字符串), 解释器会主动在程序的不同部分共享对象, 以便节约内存.
当一个对象的引用计数归零时, 它将被垃圾收集机制处理掉. 在某些情况下, 很多已不再使用的对象间可能存在循环依赖关系, 例如:
- a = {}
- b = {}
- a['b'] = b
- b['a'] = a
- del a
- del b
在以上例子中, del 语句将会减少 a 和 b 的引用计数, 并销毁用于引用底层对象的名称. 然而因为每个对象都包含一个对其他对象的引用, 所以引用计数不会归零, 对象也不会被销毁, 从而导致内存泄露. 为了解决这个问题, 解释器会定期执行一个循环检测器, 搜索不可访问对象的循环并删除它们.
4. 引用与复制
在程序进行像 a = b 这样的赋值时, 就会创建一个对 b 的引用. 对于像数字和字符串这样的不可变对象, 这种赋值实际上创建了 b 的一个副本. 然而, 对于可变对象 (如列表和字典) 引用行为会完全不同, 例如:
- a = [1, 2, 3, 4]
- b = a
- print(b is a) # True
- b[2] = -100
- print(a[2]) #-100
因为 a 和 b 引用的同一个对象, 修改其中任意一个变量都会影响到另一个. 所以必须创建对象的副本而不是新的引用. 对于像列表和字典这样的容器对象, 可以使用两种复制操作: 浅复制和深复制. 浅复制将创建一个新对象, 但它包含的是对原始对象中包含的项的引用, 例如:
- a = [1, 2, [3, 4]]
- b = list(a)
- print(b is a) #False
- b.append(100)
- print(b) # [1, 2, [3, 4], 100]
- print(a) # [1, 2, [3, 4]]
- b[2][0] = -100
- print(b) # [1, 2, [-100, 4], 100]
- print(a) # [1, 2, [-100, 4]]
深复制将创建一个新对象, 并且递归地复制它包含的所有对象. 可以使用标准库中的 copy.deepcopy() 函数完成该工作, 例如:
- import copy
- a = [1, 2, [3, 4]]
- b = copy.deepcopy(a)
- b[2][0] = -100
- print(b) # [1, 2, [-100, 4]]
- print(a) # [1, 2, [3, 4]]
5. 表示数据的内置类型
大约有 12 种数据类型可用于表示程序中用到的大多数数据. 如下表所示:
类型分类 | 类型名称 | 描述 |
---|---|---|
None | Type(None) | null 对象 None |
数字 | int | 整数 |
数字 | float | 浮点数 |
数字 | complex | 复数 |
数字 | bool | 布尔值 |
序列 | str | 字符串 |
序列 | list | 列表 |
序列 | tuple | 元组 |
序列 | range | 创建的整数范围 |
映射 | range | 创建的整数范围 |
集合 | set | 可变集合 |
集合 | frozenset | 不可变集合 |
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() 可用低级二进制形式使用浮点数.
序列表示索引为非负整数的有序对象集合, 包括字符串, 列表和元组. 所有序列支持的方法如下表:
项目 | 描述 |
---|---|
s[i] | 返回一个序列的元素 i |
s[i:j] | 返回一个切片 |
s[i:j:stride] | 返回一个扩展切片 |
lens(s) | s 中的元素数 |
min(s) | s 中的最小值 |
max(s) | s 中的最大值 |
sum(s [, initial]) | s 中各项的和 |
all(s) | 检查 s 中的所有项是否为 True |
any(s) | 检查 s 中的任意项是否为 True |
适用于可变序列的方法如下表:
项目 | 描述 |
---|---|
s[i] = v | 项目赋值 |
s[i:j] = t | 切片赋值 |
s[i:j:stride] = t | 扩展切片赋值 |
del s[i] | 项目删除 |
del s[i:j] | 切片删除 |
del s[i:j:stride] | 扩展切片删除 |
列表支持的方法如下表:
方法 | 描述 |
---|---|
list(s) | 将 s 转换为一个列表 |
s.append(x) | 将一个新元素 x 追加到 s 末尾 |
s.extend(x) | 将一个新列表追加到 s 末尾 |
s.count(x) | 计算 s 中 x 的出现次数 |
s.index(x [, start [, stop]]) | 找到 x 首次出现的位置 |
s.insert(i, x) | 在索引 i 处插入 x |
s.pop([i]) | 返回元素 i 并从列表中移除它,省略 i 则返回列表中最后一个元素 |
s.remove(x) | 搜索 x 并从 s 中移除它 |
s.reverse() | 颠倒 s 中的所有元素的顺序 |
s.sort([key [, reverse]]) | 对 s 中的所有元素进行排序。key 是一个键函数。reverse 表明以倒序对列表进行排序 |
list(s) 可将任意可迭代类型转换为列表. 如果 s 已经是列表, 则该函数构造的新列表是 s 的一个浅复制.
字符串支持的方法如下表:
方法 | 描述 |
---|---|
s.captitalize() | 首字符变大写 |
s.center(width [, pad]) | 在长度为 width 的字段内将字符串居中。pad 是填充字符 |
s.count(sub [, start [, end]]) | 计算指定子字符串 sub 的出现次数 |
s.decode([encoding [, errors]]) | 解码一个字符串并返回一个 Unicode 字符串 |
s.encdoe([encoding [, errors]]) | 返回字符串的编码版本 |
s.endswith(suffix [, start [, end]]) | 检查字符串是否以 suffix 结尾 |
s.expandtabs([tabsize]) | 使用空格替换制表符 |
s.find(sub [, start [, end]]) | 找到指定子字符串 sub 首次出现的位置,否则返回 - 1 |
s.format( args, * kwargs) | 格式化 s |
s.index(sub [, start [, end]]) | 指到指定子字符串 sub 首次出现的位置,否则报错 |
s.isalnum() | 检查所有字符是否都为字母或数字 |
s.isalpha() | 检查所有字符是否都为字母 |
s.isdigit() | 检查所有字符是否都为数字 |
s.islower() | 检查所有字符是否都为小写 |
s.isspace() | 检查所有字符是否都为空白 |
s.istitle() | 检查字符串是否为标题字符串(每个单词首字母大写) |
s.isupper() | 检查所有字符是否都为大写 |
s.join(t) | 使用 s 作为分隔符连接序列 t 中的字符串 |
s.ljust(width [, fill]) | 在长度为 width 的字符串内左对齐 s |
s.lower() | 转换为小写形式 |
s.lstrip([chrs]) | 删掉 chrs 前面的空白或字符 |
s.partition(sep) | 使用分隔符字符串 sep 划分一个字符串。返回一个元组 (head, sep, tail) |
s.replace(old, new [, maxreplace]) | 替换一个子字符串 |
s.rfind(sub [, start [, end]]) | 找到一个子字符串最后一次出现的位置 |
s.rindex(sub [, start [, end]]) | 找到一个子字符串最后一次出现的位置,否则报错 |
s.rjust(width [, fill]) | 在长度为 width 的字符串内右对齐 s |
s.rpartition(sep) | 使用分隔符 sep 划分字符串,但是从字符串的结尾处开始搜索 |
s.rsplit([sep [, maxsplit]]) | 使用 sep 作为分隔符对一个字符串从后往前进行划分。maxsplit 是最大划分次数 |
s.rstrip([chrs]) | 删掉 chrs 尾部的空白或字符 |
s.split([sep [, maxsplit]]) | 使用 sep 作为分隔符对一个字符串进行划分。maxsplit 是划分的最大次数 |
s.splitlines([keepends]) | 将字符串分为一个行列表。如果 keepends 为 1,则保留各行最后的换行符 |
s.startswith(prefix [, start [, end]]) | 检查一个字符串是否以 prefix 开头 |
s.strip([chrs]) | 删掉 chrs 开头和结尾的空白或字符 |
s.swapcase() | 将大写转换为小写,或者相反 |
s.title() | 将字符串转换为标题格式 |
s.translate(table [, deletechars]) | 使用一个字符转换表 table 转换字符串,删除 deletechars 中的字符 |
s.upper() | 将一个字符串转换为大写形式 |
s.zfill(width) | 在字符串的左边填充 0,直至其宽度为 width |
很多字符串方法都接受可选的 start 和 end 参数, 其值为整数, 用于指定 s 中起始和结束位置的索引. 大多数情况下, 这些值可以为负值, 表示索引是从字符串结尾处开始计算的.
映射类型表示一个任意对象的集合, 而且可以通过另一个几乎是任意键值的集合进行索引. 和序列不同, 映射对象是无序的, 可以通过数字, 字符串和其他对象进行索引. 映射是可变的.
字典是唯一内置的映射类型, 任何不可变对象都可以用作字典键值, 如字符串, 数字, 元组等. 字典的方法如下表:
项目 | 描述 |
---|---|
len(m) | 返回 m 中的项目数 |
m[k] | 返回 m 中键 k 的项 |
m[k] = x | 将 m[k] 的值设为 x |
del m[k] | 从 m 中删除 m[k] |
k in m | 如果 k 是 m 中的键,则返回 True |
m.clear() | 删除 m 中的所有项目 |
m.copy() | 返回 m 的一个副本 |
m.fromkeys(s [, value]) | 创建一个新字典并将序列 s 中的所有元素作为新字典的键,这些键的值均为 value |
m.get(k [, v]) | 返回 m[k],如果找不到 m[k],则返回 v |
m.items() | 返回由 (key, value) 对组成的一个序列 |
m.keys() | 返回键值组成的一个序列 |
m.pop(k [, default]) | 如果找到 m[k],则返回 m[k] 并从 m 中删除,否则返回 default 的值 |
m.popitem() | 从 m 中删除一个随机的 (key, value) 对,并把它返回为一个元组 |
m.setdefault(k [, v]) | 如果找到 m[k],则返回 m[k],不则返回 v,并将 m[k] 的值设为 v |
m.update(b) | 将 b 中的所有对象添加到 m 中 |
m.values() | 返回 m 中所有值的一个序列 |
集合是唯一的无序集. 与序列不同, 集合不提供索引或切片操作. 它们和字典也有所区别, 即对象不存在相关的键值. 放入集合的项目必须是不可变的. 集合分为两种类型, set 是可变的集合, 而 frozenset 是不可变的集合, 这两类集合都是用一对内置函数创建的, 例如:
- s = set([1, 5, 10, 15])
- f = frozenset(['a', 37, 'hello'])
所有集合支持的方法如下表:
项目 | 描述 |
---|---|
len(s) | 返回 s 中项目数 |
s.copy() | 制作 s 的一份副本 |
s.difference(t) | 求差集。返回所有要 s 中,但不在 t 中的项目 |
s.intersection(t) | 求交集。返回所有同时在 s 和 t 中的项目 |
s.isdisjoint(t) | 如果 s 和 t 没有相同项,则返回 True |
s.issubset(t) | 如果 s 是 t 的一个子集,则返回 True |
s.issuperset(t) | 如果 s 是 t 的一个超集,则返回 True |
s.symmetric_difference(t) | 求对称差集。返回所有在 s 或 t 中,但又不同时在这两个集合中的项 |
s.union(t) | 求并集。返回所有在 s 或 t 中的项 |
可变集合还另外提供了一些方法, 如下表:
项目 | 描述 |
---|---|
s.add(item) | 将 item 添加到 s 中。如果 item 已经在 s 中,则无任何效果 |
s.clear() | 删除 s 中的所有项 |
s.difference_update(t) | 从 s 中删除同时也在 t 中的所有项 |
s.discard(item) | 从 s 中删除 item,如果 item 不要 s 的成员,则无任何效果 |
s.intersection_update(t) | 计算 s 与 t 的交集,并将结果放入 s |
s.pop() | 返回一个任意的集合元素,并将其从 s 中删除 |
s.remove(item) | 从 s 中删除 item,如果 item 不是 s 的成员,引发异常 |
s.symmetric_difference_update(t) | 计算 s 与 t 的对称差集,并将结果放入 s |
s.update(t) | 将 t 中的所有项添加到 s 中 |
所有的这些操作都可以直接修改集合 s.
6. 表示程序结构的内置类型
- f = Foo()
- meth = f.instance_method
- meth(30)
- umeth = Foo.instance_method
- umeth(f, 30)
- f = Foo()
- print(type(f)) #
来源: http://www.bubuko.com/infodetail-2721293.html