1 Python 和 Java 在类属性(静态属性)和实例属性的处理
题目
执行以下程序,输出结果为()
class Base(object):count = 0def __init__(self):pass
b1 = Base()
b2 = Base()
b1.count = b1.count + 1
print(b1.count,end=" ")
print(Base.count,end=" ")
print(b2.count)A 1 1 1
B 1 0 0
C 1 0 1
D 抛出异常正确答案:B
解析
在这个程序中,我们需要理解类属性和实例属性的区别:
count = 0
是Base
类的类属性,所有实例共享这个属性- 当我们创建
b1 = Base()
和b2 = Base()
时,它们最初都没有自己的count
实例属性,会共享类的count
属性
关键操作是 b1.count = b1.count + 1
:
- 这行代码首先读取
b1.count
,由于b1
没有自己的count
属性,会读取类属性Base.count
(值为 0) - 然后创建一个
b1
的实例属性count
,并赋值为 0 + 1 = 1
此时:
b1.count
访问的是b1
自己的实例属性,值为 1Base.count
访问的是类属性,仍然保持初始值 0b2.count
访问的是类属性(因为b2
没有自己的实例属性),值为 0
所以输出结果是 1 0 0
,正确答案是 B。
【base不用先创造实例吗?】
在 Python 中,类属性(如 Base.count
)属于类本身,不需要先创建实例就可以直接访问。
类属性的特点:
- 它是类级别的属性,不属于任何特定实例
- 可以通过
类名.属性名
直接访问(如Base.count
) - 所有实例在没有自己的同名实例属性时,都会共享这个类属性
在你提供的代码中:
count = 0
是Base
类的类属性,定义后就存在了- 即使不创建
b1
和b2
实例,也可以直接访问Base.count
(值为 0)
当创建实例后:
b1
和b2
初始时没有自己的count
属性,所以b1.count
和b2.count
会指向类属性Base.count
- 直到执行
b1.count = ...
时,才会给b1
创建一个独立的实例属性count
,此时b1.count
不再指向类属性
这就是为什么代码中可以直接访问 Base.count
而不需要先创建实例的原因。
【这个py和java的语法规则一样吗?】
不仅学混了还学忘了。
Python 和 Java 在类属性(静态属性)和实例属性的处理上有相似之处,但语法和细节实现细节存在差异,主要区别如下:
1. 类属性的定义与访问
Python:
直接在类中定义的变量就是类属性,无需特殊关键字,可通过类名.属性名
或实例.属性名
(无同名实例属性时)访问。
例:python
运行
class Base:count = 0 # 类属性print(Base.count) # 直接通过类访问,无需实例化
Java:
类属性需要用static
关键字声明,称为 “静态属性”,必须通过类名.属性名
或实例.属性名
访问(推荐用类名)。
例:java
运行
class Base {static int count = 0; // 静态属性(类属性) }public class Main {public static void main(String[] args) {System.out.println(Base.count); // 直接通过类访问} }
2. 实例属性对类属性的遮蔽(Shadowing)
Python:
当实例定义与类属性同名的实例属性时,会自动 “遮蔽” 类属性(实例访问时优先用自己的属性,类访问仍用类属性)。
例:python
运行
b1 = Base() b1.count = 1 # 给b1创建实例属性,遮蔽类属性 print(b1.count) # 1(实例属性) print(Base.count) # 0(类属性不受影响)
Java:
实例不能定义与静态属性同名的实例属性(编译报错),避免混淆。
若尝试定义:java
运行
class Base {static int count = 0;int count; // 编译错误:与静态属性重名 }
3. 核心区别总结
场景 | Python | Java |
---|---|---|
类属性声明 | 直接定义(无关键字) | 必须用 static 关键字 |
类属性访问 | 类名。属性 或 实例。属性(无同名实例属性) | 类名。属性 或 实例。属性(推荐类名访问) |
实例与类属性重名 | 允许(实例属性遮蔽类属性) | 不允许(编译报错) |
类属性是否依赖实例化 | 不依赖(类加载时就存在) | 不依赖(类加载时就存在) |
回到原题
如果用 Java 实现类似逻辑,由于不允许实例属性与静态属性重名,b1.count = ...
这样的代码会直接编译报错。
但 Python 允许这种操作,因此两者行为不同。
简单说:语法规则不完全相同,核心差异体现在类属性的声明方式和实例属性与类属性的重名处理上。
2 Python中or运算符的一个重要特性:它返回的是操作数本身,而不是布尔值。
Python 中的 or
运算符有一个重要特性:它返回的是第一个为 “真” 的操作数,而不是单纯的布尔值 True
或 False
。如果所有操作数都为 “假”,则返回最后一个操作数。
这与许多其他编程语言中 or
仅返回布尔值的行为不同,在 Python 中,or
更像是 "返回第一个真值" 的运算符。
示例:
python
运行
# 返回第一个真值(非空字符串是真值)
print("hello" or 0) # 输出: hello# 0是假值,返回第二个操作数
print(0 or "world") # 输出: world# 所有都是假值,返回最后一个
print(0 or "" or None) # 输出: None# 数字0是假值,非0数字是真值
print(0 or 42) # 输出: 42# 空列表是假值,非空列表是真值
print([] or [1, 2, 3]) # 输出: [1, 2, 3]
这个特性在 Python 中非常实用,可以用来简洁地设置默认值,例如:
python
运行
def greet(name=None):# 如果name为None(假值),则使用"Guest"作为默认值name = name or "Guest"print(f"Hello, {name}!")greet() # 输出: Hello, Guest!
greet("Alice") # 输出: Hello, Alice!
理解这个特性有助于写出更简洁、更 Pythonic 的代码。
【和java一样吗?不一样!】
在 Java 中,||
运算符(逻辑或)的行为与 Python 的 or
有本质区别:
Java 的 ||
严格返回布尔值(true
或 false
),不会返回操作数本身。它仅判断操作数的真假性,最终结果只能是布尔类型。
示例对比:
java
运行
// Java 中 || 返回布尔值
System.out.println("hello" || 0); // 编译错误(操作数类型不兼容)
System.out.println(0 || 42); // 编译错误(操作数必须是布尔类型)// 正确用法:操作数为布尔值,结果为布尔值
System.out.println(1 > 0 || 2 > 5); // 输出 true
System.out.println(1 < 0 || 2 > 5); // 输出 false
而 Python 的 or
如前所述,返回的是第一个为 "真" 的操作数本身(非布尔值),这是两种语言在设计上的明显差异。
简单说:
- Java 的
||
是 "逻辑或判断",结果只能是true
/false
- Python 的
or
是 "取第一个真值",结果是操作数本身
题目
下列Python3代码运行结果是( )
a = 'a'
print(a > 'b' or 'c')
A a
B b
C c
D True
E False正确答案:C
这道题考查的是 Python 中 or
运算符的特性 —— 它返回的是操作数本身,而不是布尔值,这与许多其他语言不同。
分析代码 print(a > 'b' or 'c')
:
- 首先计算
a > 'b'
,其中a = 'a'
。在 Python 中,字符串比较是基于字符的 Unicode 编码值,'a' 的编码值小于 'b',所以a > 'b'
的结果为False
(假值)。 - 然后执行
or
运算。or
运算符的逻辑是:如果第一个操作数是真值,则返回第一个操作数;如果第一个操作数是假值,则返回第二个操作数。 - 由于
a > 'b'
的结果是False
(假值),所以or
运算返回第二个操作数'c'
。
因此,代码的输出结果是 c
,正确答案是 C。
3 Python 中可以直接修改原字典的方法
直接赋值
通过dict[key] = value
为已有键设置新值,或添加新的键值对:python
运行
d = {"a": 1} d["a"] = 100 # 修改已有键 d["b"] = 2 # 添加新键值对
update()
合并其他字典或键值对到原字典,重复键会被覆盖:python
运行
d = {"a": 1} d.update({"b": 2, "a": 10}) # 原字典变为 {"a": 10, "b": 2}
pop(key[, default])
删除指定键并返回其值,原字典尺寸减小:python
运行
d = {"a": 1, "b": 2} d.pop("a") # 返回 1,原字典变为 {"b": 2}
popitem()
删除并返回字典中最后插入的键值对(Python 3.7+ 有序):python
运行
d = {"a": 1, "b": 2} d.popitem() # 返回 ("b", 2),原字典变为 {"a": 1}
clear()
清空字典中所有键值对,原字典变为空:python
运行
d = {"a": 1} d.clear() # 原字典变为 {}
setdefault(key[, default])
若键不存在,则添加键值对(key: default
),原字典被修改:python
运行
d = {"a": 1} d.setdefault("b", 2) # 原字典变为 {"a": 1, "b": 2}
del
语句
通过del dict[key]
直接删除指定键:python
运行
d = {"a": 1} del d["a"] # 原字典变为 {}
这些操作都会直接修改原字典本身,而非返回新字典。如果需要保持原字典不变,可先使用 copy()
方法创建副本后再操作。
4 正则表达式中字符集([]
)的用法
下面字符串中匹配正则的有?mstr = r"red hed ced ped"
regex = r"[^c|p]ed"
pattern = re.compile(regex)
print pattern.findall(mstr)A red
B hed
C ced
D ped正确答案:AB
首先看正则表达式:r"[^c|p]ed"
这里的关键是 [^c|p]
这个部分,我们拆开来理解:
[]
表示 “字符集”,匹配其中任意一个字符^
放在字符集开头,表示 “取反”,即 “不匹配后面的字符”- 所以
[^c|p]
的意思是:匹配一个 “既不是 c、也不是 |、也不是 p” 的字符
(注意:|
在这里只是一个普通字符,不是 “或” 的意思,因为在字符集里|
没有特殊含义)
再看被匹配的字符串:"red hed ced ped"
里面有 4 个以 ed
结尾的子串,我们逐个检查:
red
:前面的字符是r
r
既不是c
、也不是|
、也不是p
,符合[^c|p]
,所以会被匹配。hed
:前面的字符是h
h
既不是c
、也不是|
、也不是p
,符合[^c|p]
,所以会被匹配。ced
:前面的字符是c
c
是[^c|p]
中明确排除的字符,不符合,所以不会被匹配。ped
:前面的字符是p
p
也是[^c|p]
中明确排除的字符,不符合,所以不会被匹配。
结论
符合正则表达式 r"[^c|p]ed"
的只有 red
和 hed
,对应选项 A、B。
补充说明
如果想表达 “不是 c 也不是 p”,正确的写法应该是 [^cp]
(去掉 |
),因为 |
在字符集里是普通字符,会被当成 “需要排除的字符” 之一。原正则 [^c|p]
多排除了一个 |
,但不影响这道题的结果(因为字符串里没有 |
)。(看来是起到迷惑作用的)
理解字符集的 “取反” 逻辑是关键,多练习几个例子就会熟悉啦~