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]
?多排除了一個?|
,但不影響這道題的結果(因為字符串里沒有?|
)。(看來是起到迷惑作用的)
理解字符集的 “取反” 邏輯是關鍵,多練習幾個例子就會熟悉啦~