Python - 访问修饰符
你好,有抱负的Python程序员们!今天,我们将开始一段激动人心的旅程,深入了解Python中的访问修饰符。如果你是编程新手,不用担心;我会一步一步引导你理解这个概念,沿途会有大量的例子和解释。那么,让我们开始吧!
Python中的访问修饰符
在面向对象编程中,访问修饰符用于控制类成员(属性和方法)的可见性和可访问性。虽然许多编程语言有严格的访问修饰符,如public、private和protected,但Python采用了一种更为宽松的方法。它遵循一种常被称为“我们这里都是成年人”的哲学。
在Python中,我们有三种访问修饰符:
- 公共
- 受保护
- 私有
让我们通过例子来探讨每一个。
公共成员
在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
在这个例子中,name
、age
和display_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
装饰器的例子,这是实现getter和setter的更Pythonic方式:
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
在这个例子中,我们为name
和age
创建了getter和setter方法。setter方法包括验证,以确保设置的值满足某些条件。
以下是我们讨论的方法的总结表:
方法 | 描述 | 例子 |
---|---|---|
公共 | 任何地方都可访问 | 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