- 对象和变量
- 类型和值
- 引用和绑定
- 标识值
- id函数
- 存储空间
- 赋值语句
- del语句
- 可变类型
- 不可变类型
- 身份运算符
- None和NoneType类型
- 引用计数
- 浮点型的特性和精度
- 算术转换
- 用于算术运算的内置函数
- 按位逻辑运算符
- 位移运算符
- 补码、原码和反码
1. 对象
对象的定义
变量是对象的引用,它只是关联到对象的一个名字,可以使用id()
函数进行验证,该函数用于返回对象的固有值,即标识值,不同对象的标识值一定不同。
a = 42 # 创建一个整数对象42,并让变量a引用它
b = a # 让变量b也引用整数对象42
# 改变a的引用
a = 'hello' # 现在让a引用一个字符串对象'hello'
# b仍然引用原来的整数对象42
print(b) # 输出: 42
# a现在引用一个不同的对象
print(a) # 输出: hello
运行结果:
以上代码首先创建了一个整数对象42,并让a引用它,b=a
表示将变量b
也引用a
当前引用的对象,即42
,之后a='hello'
将a
引用了另一个字符串对象hello
,而变量b
仍然引用对象42
,因此print(b)
语句会输出42
- 在对变量进行赋值操作时,不会复制对象的值,只会引用对象
- 在python中,一切皆为对象。对象占有一定的存储空间,并拥有标识值、类型和值等属性
- 不同的对象拥有不同的标识值,可通过标识值来区分不同的对象
可变类型和不可变类型
n = 12
print(id(n))
n += 1
print(id(n))
执行结果:
使用增量赋值运算符+=
对n的值进行递增后,n的标识值发生了变化,即n的引用由对象12
变为对象13
,这也说明数字是一种不可变类型。
数字和字符串时不可变类型,一旦赋值就不可以改变
可变类型:列表、字典、集合等
不可变类型:数字、字符串、元组等
身份运算符
在Python中,身份运算符用于比较两个对象的内存地址,即它们是否是同一个对象的引用。Python中有两个身份运算符:
is
is not
is
运算符 is
用于检查两个变量是否引用自同一个对象。如果是,则返回 True
,否则返回 False
。
示例:
a = [1, 2, 3]
b = a # b和a引用同一个列表对象
c = [1, 2, 3] # c引用另一个新建的列表对象,即使内容相同
print(a is b) # 输出: True
print(a is c) # 输出: False
is not
运算符 is not
是 is
的反义词,用于检查两个变量是否引用自不同的对象。如果是,则返回 True
,否则返回 False
。
示例:
a = [1, 2, 3]
b = a
c = [1, 2, 3]
print(a is not b) # 输出: False
print(a is not c) # 输出: True
需要注意的是,身份运算符与 ==
运算符不同。==
用于比较两个对象的值是否相等,而不考虑它们是否为同一个对象。例如:
a = [1, 2, 3]
c = [1, 2, 3]
print(a == c) # 输出: True,因为a和c的内容相同
在使用身份运算符时,要特别注意不要将其与 ==
混淆,尤其是在处理数字或不可变类型时。例如,Python在小整数池和字符串池中重用对象,这可能会导致一些意想不到的结果:
a = 256
b = 256
print(a is b) # 可能输出: True,因为小整数池重用了对象
a = 257
b = 257
print(a is b) # 输出结果取决于具体实现,可能是True,也可能是False
因此,当你想要比较两个变量是否指向同一个对象时,应使用身份运算符。当你只关心两个变量的值是否相等时,应使用相等运算符 ==
。
赋值语句
用于赋值的分隔符=
不是运算符,在使用赋值语句时:
- 等号右边的值并没有复制到等号左边
- 等号左边的变量名绑定到等号右边的对象
- 等号左边的变量名如果是第一次使用,则新生成一个变量(与作用域有关)
del语句
在Python中,del
语句用于删除对象的引用,从而允许Python的垃圾回收机制回收不再使用的内存。del
可以用来删除变量、列表中的元素、字典中的键值对等。
删除变量
当你想要删除一个变量时,可以使用 del
语句。删除后,该变量就不再存在,尝试访问它将会引发一个 NameError
。
示例:
x = 10
print(x) # 输出: 10
del x
print(x) # NameError: name 'x' is not defined
删除列表元素
del
语句也可以删除列表中的元素。可以删除单个元素,也可以删除列表的切片。
示例:
my_list = [1, 2, 3, 4, 5]
del my_list[2] # 删除索引为2的元素,即数字3
print(my_list) # 输出: [1, 2, 4, 5]
del my_list[1:3] # 删除索引从1到2的元素(不包括索引3)
print(my_list) # 输出: [1, 5]
删除字典键值对
使用 del
语句可以删除字典中的键值对。删除后,该键及其对应的值将从字典中移除。
示例:
my_dict = {'a': 1, 'b': 2, 'c': 3}
del my_dict['b'] # 删除键'b'及其对应的值2
print(my_dict) # 输出: {'a': 1, 'c': 3}
删除对象属性
如果对象拥有可删除的属性,del
语句也可以用来删除对象的属性。
示例:
class MyClass:
def __init__(self):
self.attribute = "value"
obj = MyClass()
print(obj.attribute) # 输出: value
del obj.attribute
print(obj.attribute) # AttributeError: 'MyClass' object has no attribute 'attribute'
注意事项
- 使用
del
删除变量或对象的引用并不意味着立即释放内存,这取决于Python的垃圾回收机制。 - 删除不可变数据类型(如元组中的元素)会引发错误,因为不可变数据类型不允许修改。
- 在某些情况下,即使删除了对象的引用,对象也可能因为其他引用而继续存在。
使用 del
语句时应当谨慎,确保不会在后续代码中意外地引用已删除的变量或对象。
None类型
在Python中,None
是一个特殊的常量,它表示缺失值或没有值的情况。None
是 NoneType
数据类型的唯一值,类似于其他语言中的 null
或 nil
。
使用场景
- 函数返回值:在Python中,如果一个函数没有显式地返回一个值,它会默认返回
None
。 - 可选参数:
None
可用作函数参数的默认值,表示该参数是可选的。 - 初始化:
None
可用于初始化变量,特别是当你需要在稍后的某个时间点设置它的值时。 - 标记缺失:在数据处理中,
None
可以表示数据缺失或未定义的值。
示例
# 函数返回值
def func_without_return():
pass
result = func_without_return()
print(result) # 输出: None
# 可选参数
def greet(name=None):
if name is None:
print("Hello, stranger!")
else:
print(f"Hello, {name}!")
greet() # 输出: Hello, stranger!
greet("Alice") # 输出: Hello, Alice!
# 初始化变量
a = None
if a is None:
a = "Now I'm not None!"
print(a) # 输出: Now I'm not None!
比较操作
当你需要检查一个变量是否为 None
时,推荐使用 is
操作符而不是 ==
。这是因为 is
检查两个变量是否引用内存中的同一个对象,而 None
是一个单例,意味着任何变量的值为 None
都引用同一个对象。
x = None
if x is None:
print("x is None") # 这是推荐的方式
else:
print("x is not None")
# 不推荐使用 == 来比较 None
if x == None:
print("x is None") # 这种方式虽然可行,但不推荐
else:
print("x is not None")
注意事项
None
既不是False
也不是0
,它是一个表示“无”的特殊值。None
在布尔上下文中被视为假,这意味着在条件语句中,None
会被评估为False
。- 尽管
None
在布尔上下文中被视为假,但将None
与布尔值False
直接比较仍然会返回False
,因为它们是不同的对象。
if None:
print("None is True") # 这段代码不会执行
else:
print("None is False") # 输出: None is False
print(None == False) # 输出: False
print(None is False) # 输出: False
垃圾回收机制
Python 使用了自动内存管理,这意味着开发者不需要手动管理内存分配和回收。Python 的内存回收机制主要基于以下两个方面:
- 引用计数(Reference Counting)
- 垃圾收集(Garbage Collection)
引用计数
Python 中的每个对象都有一个引用计数器,用来追踪对象被引用的次数。当一个对象的引用计数降为0时,意味着没有任何引用指向这个对象,因此它可以被立即回收。这是Python内存管理的第一道防线。
例如,当你创建一个对象时,比如 a = 42
,一个整数对象 42
被创建,并且变量 a
指向它,此时对象 42
的引用计数为1。如果你又做了一个赋值操作 b = a
,那么 42
的引用计数增加到2。如果之后你删除了一个引用 del a
,则引用计数减少到1。只有当引用计数降到0时,对象才会被真正删除。
引用计数的优点是简单、直接,对象一旦无引用即可立即回收。但它也有一个缺点:无法处理循环引用的情况。例如,两个对象相互引用,即使它们不再被其他对象引用,它们的引用计数也不会降到0。
垃圾收集
为了解决循环引用的问题,Python 使用了一个垃圾收集器,它是基于引用计数之上的。Python 的垃圾收集器主要是一个引用循环检测器,它会定期地搜索所有的对象,检测是否有对象组成了一个引用循环。如果有,且这些对象之外没有其他引用,垃圾收集器会把它们标记为可回收,并最终释放它们占用的内存。
Python 的垃圾收集器实现了几个代(generation),新创建的对象会被放入第一代。如果在一次垃圾收集之后对象仍然存活,它会被移动到下一代。较高代的对象会比较少地被检查,因为假设存活时间长的对象可能还会继续存活。这种机制可以帮助Python更高效地进行内存管理。
可以通过 gc
模块来控制Python的垃圾收集器。例如,可以使用 gc.collect()
强制进行一次垃圾收集,或者使用 gc.disable()
和 gc.enable()
来关闭和开启垃圾收集器。
总结起来,Python的内存回收机制通过引用计数来即时回收无引用的对象,并通过垃圾收集来处理更复杂的循环引用情况。这种组合方式使得Python的内存管理既自动又相对高效。
2. 类型和运算
对象和内置类型
类别 | 类型 | 描述 | 例子 |
---|---|---|---|
None 类型 | NoneType | 表示无值 | None |
数值类型 | int | 整数 | 1, 100, -5 |
float | 浮点数 | 3.14, -0.001 | |
complex | 复数 | 1 + 2j | |
bool | 布尔值 | True, False | |
序列类型 | str | 字符串 | "Hello", 'World' |
list | 列表 | [1, 2, 3], ['a', 'b'] | |
tuple | 元组 | (1, 2, 3), ('a', 'b') | |
bytes | 字节序列 | b'Hello' | |
bytearray | 可变字节序列 | bytearray(b'Hello') | |
映射类型 | dict | 字典 | {'key': 'value'} |
集合类型 | set | 集合 | {1, 2, 3} |
frozenset | 不可变集合 | frozenset([1, 2, 3]) | |
可调用类型 | function | 函数 | def func(): pass |
method | 方法 | class C: def method() | |
模块类型 | module | 模块 | import sys |
自定义类型 | user-defined | 用户自定义的类 | class MyClass: pass |
因为Python中一切皆对象,所以类型本身也是一种对象
逻辑型
Python中有一个True
对象和一个False
对象,因此程序会反复使用这些已有的对象,而不是每次生成新的对象
浮点型和实数的运算
在Python中,浮点型(float)是一种用于表示实数的数据类型,它可以包含小数部分。浮点数在Python中是基于IEEE 754标准实现的双精度(64位)浮点数。这种表示方法可以处理非常大或非常小的数,但有时会涉及精度问题,因为某些小数不能用有限的二进制位精确表示。
创建浮点数的最简单方法是直接在数值中包含小数点。例如:
a = 3.14
b = 0.5
c = -123.456
还可以使用科学记数法来创建非常大或非常小的浮点数:
d = 1.23e-4 # 相当于 0.000123
e = 1.23e+4 # 相当于 12300.0
另外,你也可以通过将整数或字符串转换为浮点数:
f = float(10) # 将整数转换为浮点数,结果为 10.0
g = float("123.456") # 将字符串转换为浮点数,结果为 123.456
浮点数的运算包括加法、减法、乘法和除法等,但要注意浮点数的精度问题。例如:
h = 0.1 + 0.2 # 结果并不会精确为 0.3,而是一个接近的数,如 0.30000000000000004
在处理涉及金钱或需要精确小数的场景时,通常建议使用decimal
模块提供的Decimal
类型,它提供了更高的精度和更完整的小数运算功能。
可以使用
sys.floatinfo
获取当前系统float类型的相关参数下面是一些
sys.float_info
中包含的属性:
max
:可表示的最大正浮点数。max_exp
:max
的指数(以2为底)。max_10_exp
:max
的指数(以10为底)。min
:可表示的最小正标准浮点数。min_exp
:min
的指数(以2为底)。min_10_exp
:min
的指数(以10为底)。dig
:浮点数的精度(十进制位数)。mant_dig
:浮点数的精度(二进制位数)。epsilon
:两个可表示浮点数之间的差异。radix
:浮点数表示的基数。rounds
:浮点加法的舍入模式。
算数转换
Python中的算术运算遵循特定的类型转换规则,以确保操作数在进行运算时具有相同的类型。以下是一些基本的类型转换规则:
整数与浮点数的运算:当整数和浮点数一起进行运算时,整数会被转换成浮点数,然后执行浮点运算。这样做是为了保持数值的精度。
result = 3 + 2.5 # 3 被转换成 3.0,结果是 5.5(浮点数)
复数与其他数值类型的运算:当复数与整数或浮点数进行运算时,整数或浮点数会被转换为复数的实部,虚部为0的复数,然后执行复数运算。
result = 1 + 2j + 3 # 3 被转换为复数 3 + 0j,结果是 4 + 2j(复数)
- 不同精度的浮点数运算:在Python中,所有浮点数都是双精度的,因此不需要在不同精度的浮点数之间进行转换。
- 不同长度的整数运算:Python的整数类型(
int
)可以处理任意大小的整数。当两个不同长度的整数进行运算时,Python会自动处理它们,不需要进行显式的类型转换。 布尔值与数值的运算:布尔值
True
和False
在数值运算中分别被视为1
和0
。这意味着布尔值可以直接与整数或浮点数进行算术运算。result = True + 1 # True 被视为 1,结果是 2
- 类型提升:在某些情况下,为了保持最大可能的精度,较“低”类型的数值会被提升(转换)到较“高”类型。类型提升的顺序通常是:
int
->float
->complex
。 除法运算:在Python 3中,普通除法运算符
/
总是返回一个浮点数,即使两个操作数都是整数。整数除法运算符//
返回的是两个整数相除的商,结果类型与操作数类型一致(如果两个操作数都是整数,则结果是整数;如果至少有一个操作数是浮点数,则结果是浮点数)。result_div = 4 / 2 # 结果是 2.0(浮点数) result_floor_div = 4 // 2 # 结果是 2(整数)
- 混合类型的序列运算:当进行序列运算(如列表拼接)时,参与运算的序列类型必须相同,否则会引发
TypeError
。
这些规则确保了Python中算术运算的一致性和可预测性。在实际编程时,了解这些规则有助于避免类型相关的错误和意外行为。
算数运算使用的常见内置函数
函数 | 描述 | 示例 | 结果 |
---|---|---|---|
abs(x) | 返回x的绝对值 | abs(-5) | 5 |
divmod(x, y) | 返回包含商和余数的元组 | divmod(5, 3) | (1, 2) |
pow(x, y) | 返回x的y次幂,等同于x**y | pow(5, 3) | 125 |
round(x[, n]) | 对x四舍五入,n是小数位数 | round(3.14159, 2) | 3.14 |
sum(iterable) | 对可迭代对象中的元素求和 | sum([1, 2, 3]) | 6 |
min(iterable) | 返回可迭代对象中的最小值 | min(1, 2, 3) | 1 |
max(iterable) | 返回可迭代对象中的最大值 | max(1, 2, 3) | 3 |
位运算
在Python中,位运算符是用来操作整数的二进制表示中的位。以下是Python中常用的位运算符:
&
(按位与):对两个数的二进制表示进行按位与操作。只有对应的两个位都为1时,结果位才为1,否则为0。
a = 12 # 1100
b = 5 # 0101
print(a & b) # 输出 4 (0100)
|
(按位或):对两个数的二进制表示进行按位或操作。只要对应的两个位中有一个为1,结果位就为1。
a = 12 # 1100
b = 5 # 0101
print(a | b) # 输出 13 (1101)
^
(按位异或):对两个数的二进制表示进行按位异或操作。只有对应的两个位不同,结果位才为1,否则为0。
a = 12 # 1100
b = 5 # 0101
print(a ^ b) # 输出 9 (1001)
~
(按位取反):对一个数的二进制表示进行按位取反操作。即将所有的0变成1,所有的1变成0。
a = 12 # 1100
print(~a) # 输出 -13 (按照二进制补码表示)
<<
(左移):将一个数的二进制表示向左移动指定的位数。左移n位相当于乘以2的n次方。
a = 12 # 1100
print(a << 2) # 输出 48 (110000)
>>
(右移):将一个数的二进制表示向右移动指定的位数。右移n位相当于除以2的n次方。
a = 12 # 1100
print(a >> 2) # 输出 3 (0011)
补码、原码和反码:
在计算机系统中,整数通常用补码(complement)表示。了解补码、原码(true form)和反码(ones' complement)对于深入理解计算机如何处理负数非常重要。以下是这三种表示法的基本概念:
原码 (True Form):
原码是最直观的表示数字的方式。对于正数,原码的最高位(符号位)是0,其余位表示数字本身。对于负数,符号位是1,其余位同样直接表示数字本身(不考虑符号)。例如,假设我们用8位表示一个整数:
+5 的原码表示为:0000 0101
-5 的原码表示为:1000 0101反码 (Ones' Complement):
反码是对原码的一种变形,用来表示负数。对于正数,反码与原码相同。对于负数,反码是将原码中除符号位外的所有位取反(0变1,1变0)。使用上面的例子:
+5 的反码表示为:0000 0101 (与原码相同)
-5 的反码表示为:1111 1010 (除符号位外,其余位取反)补码 (Two's Complement):
补码是在现代计算机系统中用来表示有符号整数的标准方式。对于正数,补码与原码相同。对于负数,补码是在该数的反码基础上加1。使用上面的例子:
+5 的补码表示为:0000 0101 (与原码相同)
-5 的补码表示为:1111 1011 (反码1111 1010加1)补码的优势在于它简化了包括正数和负数在内的整数的加法和减法运算。在补码表示法中,不需要考虑操作数的符号就可以直接进行加法运算,而且减法可以通过加上一个数的补码来实现。此外,补码表示法也解决了原码和反码表示法中出现的“+0”和“-0”两种零的问题,补码表示法中只有一个零。
在Python中,整数是以补码形式存储的。当你对负整数进行位运算时,你实际上是在操作这些数的补码表示。例如:
a = -5 print(bin(a & 0xff)) # 输出 '0b11111011',这是-5的补码表示,0xff用来显示所有8位
请注意,
bin
函数会返回一个字符串,该字符串表示参数的二进制形式。在处理负数时,Python会显示负数的补码形式,但会在前面加上一个负号,这可能会导致一些混淆。在上面的例子中,我们使用& 0xff
来获取负数的补码的最后8位,以避免这种混淆。