Python - 存取修飾符

你好,有抱負的 Python 程式設計師!今天,我們將進入 Python 裡的存取修飾符的世界,進行一場激動人心的旅程。如果你是編程新手,不必擔心;我會逐步引導你了解這個概念,並提供許多例子和解釋。那麼,我們開始吧!

Python - Access Modifiers

Python 中的存取修飾符

在面向對象編程中,存取修飾符用於控制類成員(屬性和方法)的可見性和可訪問性。許多編程語言都有嚴格的存取修飾符,如 public、private 和 protected,而 Python 採取了一種較為放鬆的方法。它遵循一種常被稱為「我們這裡都是成年人」的哲學。

在 Python 裡,我們有三種存取修飾符:

  1. 公開
  2. 保護
  3. 私有

讓我們通過例子來探索這些。

公開成員

在 Python 中,所有成員默認為公開。這意味著它們可以從類的外部訪問。以下是一個例子:

class Student:
def __init__(self, name, age):
self.name = name  # 公開屬性
self.age = age    # 公開屬性

def display_info(self):  # 公開方法
print(f"Name: {self.name}, Age: {self.age}")

# 創建 Student 的實例
student1 = Student("Alice", 20)

# 訪問公開成員
print(student1.name)  # 輸出:Alice
student1.display_info()  # 輸出:Name: Alice, Age: 20

在這個例子中,nameagedisplay_info() 都是公開成員。我們可以直接從類的外部訪問它們。

保護成員

保護成員用單個下劃線(_)作為成員名的前綴來表示。它們並非真正的私有,仍然可以從類的外部訪問,但按照慣例,它們應該只用於內部使用。

class Employee:
def __init__(self, name, salary):
self._name = name      # 保護屬性
self._salary = salary  # 保護屬性

def _display_salary(self):  # 保護方法
print(f"{self._name}'s salary is ${self._salary}")

# 創建 Employee 的實例
emp1 = Employee("Bob", 50000)

# 訪問保護成員(注意:這是可能的,但不推薦)
print(emp1._name)  # 輸出:Bob
emp1._display_salary()  # 輸出:Bob's salary is $50000

雖然我們可以訪問 _name_salary_display_salary(),但一般來說,不建議在類或其子類的外部進行訪問。

私有成員

私有成員用雙個下劃線(__)作為名稱的前綴。Python 會對這些成員進行名稱重整,使它們難以(但不是不可能)從類的外部訪問。

class BankAccount:
def __init__(self, account_number, balance):
self.__account_number = account_number  # 私有屬性
self.__balance = balance                # 私有屬性

def __display_balance(self):  # 私有方法
print(f"Balance: ${self.__balance}")

def public_display(self):
self.__display_balance()

# 創建 BankAccount 的實例
account1 = BankAccount("123456", 1000)

# 試圖訪問私有成員
# print(account1.__account_number)  # 這會引發 AttributeError
# account1.__display_balance()      # 這也會引發 AttributeError

# 通過公開方法訪問私有方法
account1.public_display()  # 輸出:Balance: $1000

在這個例子中,__account_number__balance__display_balance() 是私有成員。嘗試直接從類的外部訪問它們將引發 AttributeError。

名稱重整

當我提到 Python 中的私有成員並非真正的私有時,這是因為一個稱為名稱重整的機制。當你使用雙個下劃線創建私有成員時,Python 會在內部更改其名稱,使它們難以意外訪問。

以下是其工作原理:

class NameManglingDemo:
def __init__(self):
self.__private_var = "I'm private!"

demo = NameManglingDemo()
print(dir(demo))
# 輸出:[..., '_NameManglingDemo__private_var', ...]

# 使用重整後的名稱訪問私有變量
print(demo._NameManglingDemo__private_var)  # 輸出:I'm private!

正如你所見,Python 將 __private_var 重命名為 _NameManglingDemo__private_var。這就是名稱重整的實現!

Python 屬性對象

Python 的 property() 函數是一個內置函數,用於創建並返回一個屬性對象。這是一種為類屬性添加 getter、setter 和 deleter 方法的方法。

以下是一個例子:

class Temperature:
def __init__(self, celsius):
self._celsius = celsius

def get_fahrenheit(self):
return (self._celsius * 9/5) + 32

def set_fahrenheit(self, fahrenheit):
self._celsius = (fahrenheit - 32) * 5/9

fahrenheit = property(get_fahrenheit, set_fahrenheit)

# 使用屬性
temp = Temperature(25)
print(temp.fahrenheit)  # 輸出:77.0

temp.fahrenheit = 86
print(temp._celsius)  # 輸出:30.0

在這個例子中,fahrenheit 是一個屬性,允許我們在以攝氏度存儲的時候獲取和設置華氏度的溫度。

Getter 和 Setter 方法

Getter 和 Setter 方法是用於獲取和設置類屬性值的方 法。它們提供了一種在保持封裝的同時訪問和修改私有屬性的方法。

以下是一個使用 @property 装飾器的例子,這是一種更具 Python 風格的實現 Getter 和 Setter 的方法:

class Person:
def __init__(self, name, age):
self._name = name
self._age = age

@property
def name(self):
return self._name

@name.setter
def name(self, value):
if not isinstance(value, str):
raise ValueError("Name must be a string")
self._name = value

@property
def age(self):
return self._age

@age.setter
def age(self, value):
if not isinstance(value, int) or value < 0:
raise ValueError("Age must be a positive integer")
self._age = value

# 使用 Getter 和 Setter
person = Person("Charlie", 30)
print(person.name)  # 輸出:Charlie

person.name = "David"
print(person.name)  # 輸出:David

try:
person.age = -5
except ValueError as e:
print(e)  # 輸出:Age must be a positive integer

在這個例子中,我們為 nameage 創建了 Getter 和 Setter 方法。Setter 方法包括驗證,以確保設置的值符合特定的條件。

總結我們討論過的方法,以下是 Markdown 格式的表格:

方法 描述 示例
公開 任何地方都可訪問 self.name
保護 類及其子類內可訪問(按慣例) self._name
私有 名稱重整以限制訪問 self.__name
屬性 創建一個屬性對象 property(get_method, set_method)
Getter 獲取屬性值的方法 @property
Setter 設置屬性值的方法 @attribute.setter

就是這樣!我們已經涵蓋了 Python 的存取修飾符、名稱重整、屬性對象以及 Getter 和 Setter 方法。請記住,Python 對存取控制的處理更多是關於慣例和信任,而不是嚴格的規則。在你繼續 Python 的旅程中,你會發現這種靈活性使得代碼保持清晰可讀,同時仍提供實現封裝的方法。

繼續練習,保持好奇心,並愉快的編程!

Credits: Image by storyset