飞翔飞翔
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
  • 学习路径
  • 第1章 认识Python

    • Python 历史与特点
    • Python 2 与 Python 3 的核心差异
    • 安装与运行 Python 2.7.18
    • 编码规范 PEP 8
  • 第2章 基础语法

    • 变量与对象
    • 数字类型
    • 字符串 str
    • Unicode 字符串
    • 运算符
    • 空值 None
  • 第3章 流程控制

    • if 条件语句
    • if-else 条件语句
    • if-elif-else 多分支
    • 条件表达式(三元运算符)
    • while 循环
    • for 循环
    • range 与 xrange
    • 循环控制:break、continue、pass
    • 循环 else 子句
  • 第4章 数据结构

    • 列表基础
    • 列表方法
    • 列表推导式
    • 元组
    • 字典基础
    • 字典方法
    • 字典循环技巧
    • 集合
    • 序列解包
    • 序列比较
  • 第5章 函数

    • 定义函数
    • 参数传递机制
    • 默认参数
    • 关键字参数
    • 可变参数
    • Lambda 表达式
    • 文档字符串
    • 函数对象
  • 第6章 模块与包

    • import 导入
    • 模块搜索路径
    • name 与主程序
    • 编译文件 .pyc 与 .pyo
    • 包结构
    • dir() 函数
  • 第7章 文件与IO

    • 打开与关闭文件
    • 文件读写方法
    • with 上下文管理器
    • 格式化输出:% 操作符
    • 格式化输出:str.format()
    • JSON 序列化
  • 第8章 面向对象

    • 类定义与实例化
    • init 构造方法
    • 类变量与实例变量
    • 方法调用与 self
    • 继承基础
    • 多重继承
    • 新式类与旧式类
    • 私有变量与名称改写
    • 属性装饰器 property
    • 类方法与静态方法
    • 魔术方法
    • 空类与数据记录
  • 第9章 异常处理

    • 异常类型
    • try-except
    • try-except-else-finally
    • 抛出异常 raise
    • 自定义异常
    • with 语句与上下文管理器
  • 第10章 迭代器与生成器

    • 迭代器协议
    • 生成器函数
    • 生成器表达式
    • itertools模块
  • 第11章 标准库精要

    • os模块
    • sys模块
    • datetime模块
    • re模块
    • json模块
    • collections模块
    • math与random模块
    • urllib2与网络请求
    • subprocess与命令执行
    • threading与并发
    • unittest与测试
    • 虚拟环境与包管理
  • 第12章 工程实践

    • 调试技巧
    • 性能分析
    • 文档与注释
    • 下一步学习

变量与对象

在 Python 中,变量不是存储数据的盒子,而是绑定到对象的标签。理解这一点,是掌握 Python 动态类型系统的关键。

变量是名字,不是盒子

许多语言(如 C)把变量看作内存中的盒子:声明 int x = 10 后,盒子里就装着整数 10。Python 完全不同:

a = 1

这行代码做了两件事:创建一个整数对象 1,然后把名字 a 绑定到这个对象上。名字和对象之间是"贴标签"的关系,而非"装东西"的关系。

a = 1
b = a      # b 也绑定到同一个对象 1
a = 2      # a 改绑定到对象 2,b 仍然绑定到 1
print a    # 2
print b    # 1

b = a 不是"把 a 的值复制给 b",而是"让 b 也指向 a 所指的对象"。当 a = 2 时,a 撕下旧标签贴到新对象上,b 不受影响。

对象的三要素

每个 Python 对象都有三个固有属性:

身份(Identity):对象创建后永不改变,可以理解为内存地址。用 id() 查看:

a = [1, 2, 3]
print id(a)       # 如 140234567890

类型(Type):决定对象能做什么操作。用 type() 查看:

print type(42)        # <type 'int'>
print type("hello")   # <type 'str'>
print type([1, 2])    # <type 'list'>

类型在对象创建时确定,之后不可变。你不能把一个整数对象"变成"字符串对象,只能让变量重新绑定到另一个对象。

值(Value):可变对象的值可以改变,不可变对象的值永远固定。

# 可变对象:列表
a = [1, 2, 3]
print id(a)       # 140234567890
a.append(4)       # 修改值,但身份不变
print id(a)       # 仍然是 140234567890

# 不可变对象:整数
b = 10
print id(b)       # 如 140230000000
c = b + 1         # 不是修改 10,而是创建新对象 11
print id(c)       # 不同的地址

可变与不可变对象

类型可变?示例
int, long, float, complex否42, 3.14
bool否True, False
str, unicode否"hello", u"你好"
tuple否(1, 2, 3)
list是[1, 2, 3]
dict是{"a": 1}
set是{1, 2, 3}

可变对象的"别名陷阱"是 Python 初学者最常踩的坑:

a = [1, 2, 3]
b = a          # b 和 a 指向同一个列表对象
b.append(4)
print a        # [1, 2, 3, 4] —— a 也被改了!

因为 a 和 b 绑定到同一个列表对象,通过 b 修改列表,通过 a 也能看到变化。如果需要独立的副本,必须显式复制:

a = [1, 2, 3]
b = a[:]       # 切片复制,创建新列表
b.append(4)
print a        # [1, 2, 3] —— a 不受影响

动态类型的本质

Python 是动态类型语言:变量没有类型,对象才有类型。同一个变量可以在不同时刻绑定到不同类型的对象:

x = 10
print type(x)    # <type 'int'>

x = "hello"
print type(x)    # <type 'str'>

x = [1, 2]
print type(x)    # <type 'list'>

这与静态类型语言(如 C、Java)形成鲜明对比。在 C 中,int x 声明后,x 永远只能存储整数。在 Python 中,x 只是一个名字,它可以指向任何对象。

动态类型带来了灵活性,但也增加了运行时出错的风险。Python 2 不会在编译时检查类型,以下代码完全合法,直到运行时才报错:

def add(a, b):
    return a + b

print add(1, 2)       # 3
print add("1", "2")   # "12"
print add(1, "2")     # TypeError: unsupported operand type(s)

垃圾回收

当一个对象没有任何名字绑定时,它就变成了"垃圾"。Python 主要使用引用计数来回收垃圾:每个对象内部维护一个计数器,记录有多少个名字指向它。计数器归零时,对象立即被销毁,内存被释放。

a = [1, 2, 3]     # 列表对象的引用计数 = 1
b = a             # 引用计数 = 2
del a             # 引用计数 = 1
del b             # 引用计数 = 0,对象被销毁

引用计数简单高效,但无法处理循环引用:

a = []
b = []
a.append(b)       # a 引用 b
b.append(a)       # b 引用 a
del a
del b             # 两个对象的引用计数都是 1(互相引用),无法释放

Python 2 的 gc 模块会定期检测并打破循环引用,但引用计数仍是主要的垃圾回收机制。这意味着大多数情况下,对象会在失去引用的瞬间被清理,不需要等待垃圾回收器运行。

下一页
数字类型