学习日志32 python
1 python浅拷贝只复制地址
在 Python 中,浅拷贝(shallow copy)的行为可以理解为 "只复制容器本身,而不复制容器中的元素",这意味着容器内元素的引用(地址)会被保留。
具体来说:
- 对于列表、字典等容器类型,浅拷贝会创建一个新的容器对象
- 新容器中存储的是原容器中元素的引用(内存地址),而不是元素本身的副本
- 因此,当修改原容器或新容器中的可变元素时(如列表中的子列表),另一个容器会受到影响
示例代码:
python
运行
original = [1, [2, 3], 4]
shallow_copied = original.copy() # 浅拷贝列表# 修改原列表中的可变元素
original[1].append(5)print(original) # 输出: [1, [2, 3, 5], 4]
print(shallow_copied) # 输出: [1, [2, 3, 5], 4] (子列表被修改)# 修改原列表中的不可变元素
original[0] = 100print(original) # 输出: [100, [2, 3, 5], 4]
print(shallow_copied) # 输出: [1, [2, 3, 5], 4] (不可变元素不受影响)
这就是为什么说浅拷贝 "只复制地址"—— 它复制了容器的结构,但容器内元素的引用关系被保留了下来。如果需要完全独立的副本,需要使用深拷贝(deep copy)。
2 a is b和id(a) == id(b)的功能类似
是的,在 Python 中,a is b
和 id(a) == id(b)
的功能非常相似,它们本质上都是判断两个变量是否指向内存中的同一个对象。
id(object)
函数返回对象的唯一标识符(可以理解为对象在内存中的地址)a is b
检查的是a
和b
是否引用同一个对象,也就是判断它们的内存地址是否相同- 因此
a is b
等价于id(a) == id(b)
示例代码:
python
运行
x = [1, 2, 3]
y = x
z = [1, 2, 3]print(x is y) # True - x和y指向同一个对象
print(id(x) == id(y)) # True - 内存地址相同print(x is z) # False - x和z是不同对象
print(id(x) == id(z)) # False - 内存地址不同
需要注意的是,对于小整数、短字符串等,Python 会进行缓存(字符串驻留机制),可能会出现不同变量引用同一对象的情况:
python
运行
a = 10
b = 10
print(a is b) # True
print(id(a) == id(b)) # True
这种情况下,即使没有显式赋值,a
和 b
也可能指向同一个对象,所以两种判断方式仍然会得到相同的结果。
3 我就这么理解把foo
成编程里的 “张三”
对于Python类中单下划线_foo、双下划线__foo与__foo__的成员,下列说法正确的是?
A _foo 不能直接用于’from module import *’
B __foo解析器用_classname__foo来代替这个名字,以区别和其他类相同的命名
C __foo__代表python里特殊方法专用的标识
D __foo 可以直接用于’from module import *’正确答案:ABC
你的答案:BC
在 Python 类中,foo
只是一个占位符,代表类中的成员名称(可以是变量名或方法名)。单下划线 _foo
、双下划线 __foo
和 __foo__
是通过不同的命名方式,来表示成员的访问权限或特殊含义,具体解释如下:
1. 单下划线 _foo
- 这是一种约定俗成的命名规范(非强制语法),表示该成员是 “私有” 的,建议外部不要直接访问(但语法上允许访问)。
- 当使用
from module import *
导入模块时,默认不会导入以单下划线开头的成员(对应选项 A 正确)。 - 例:
_name
通常表示这个变量是模块或类的内部变量,不希望被外部直接使用。
2. 双下划线 __foo
- 这是 Python 的名称修饰(name mangling)机制,会触发语法级别的访问限制。
- 解析器会自动将
__foo
改名为_类名__foo
(如类Person
中的__age
会被改为_Person__age
),目的是避免子类和父类中的同名成员冲突(对应选项 B 正确)。 - 这种成员无法通过
from module import *
直接导入(选项 D 错误),且外部不能直接通过__foo
访问,必须用修饰后的名称访问(但不建议这么做,破坏了封装性)。
3. 双下划线包围 __foo__
- 这种命名方式用于 Python 的特殊方法或内置属性,是语法规定的 “魔术方法” 标识。
- 例如
__init__
(初始化方法)、__len__
(长度方法)、__str__
(字符串转换方法)等,这些方法有特殊的功能,由 Python 解释器自动调用(对应选项 C 正确)。 - 开发者应避免自定义这种格式的名称,以免与 Python 内置功能冲突。
简单说,foo
只是一个示例名称,核心是通过前缀的下划线数量,来区分成员的访问规则和特殊含义。
可以把 foo
理解成编程里的 “张三”—— 一个通用的占位符,用来代表任意的名字。
就像我们说 “张三做了某件事” 时,“张三” 只是一个举例用的人名,换成 “李四”“王五” 意思不变;在代码里说 _foo
__foo
时,foo
也只是一个举例用的成员名,换成 name
age
等具体名称,这些命名规则的含义和作用是完全一样的。
比如:
_name
和_foo
一样,都是 “约定私有” 的成员;__age
和__foo
一样,会触发名称修饰机制;__init__
和__foo__
一样,属于 Python 特殊方法的命名格式。
所以 foo
本身没有实际意义,只是为了方便举例而用的 “通用名称”,类似日常生活中用 “张三李四” 代指不确定的人~
4 如果直接字典调用 pop()
而不传入任何参数
Python字典的pop()方法需要指定一个键(key)作为参数来删除并返回对应的值。
直接调用pop()方法而不传入任何参数会引发TypeError异常。
在 Python 中,字典的 pop()
方法必须指定一个键(key)作为参数,用于删除并返回该键对应的值。
如果直接调用 pop()
而不传入任何参数,确实会引发 TypeError
异常,提示缺少必要的参数。
示例如下:
python
运行
my_dict = {"name": "Alice", "age": 30}# 正确使用:指定键作为参数
removed_value = my_dict.pop("age")
print(removed_value) # 输出: 30
print(my_dict) # 输出: {'name': 'Alice'}# 错误使用:不传入任何参数
my_dict.pop() # 会引发 TypeError: pop() takes at least 1 argument (0 given)
此外,pop()
方法还可以接受第二个可选参数(默认值),当指定的键不存在时,会返回这个默认值而不是引发 KeyError
异常。例如:
python
运行
my_dict = {"name": "Alice"}
value = my_dict.pop("age", "Unknown")
print(value) # 输出: Unknown(因为"age"键不存在)