Python封装一方面指把数据和方法收进类里,另一方面指控制外部能直接碰哪些内部细节。约定上,单下划线表示内部使用。双下划线会触发名字改写,从类外直接访问会更困难。真正要加校验时,常见做法是私有属性配合 getter / setter,或用 @property 把读写写成属性外观。

私有属性示例

余额不希望被外部随意改写成任意数,就写成 __balance,只通过 depositget_balance 这类方法接触:

class BankAccount:
    def __init__(self, owner, balance):
        self.owner = owner
        self.__balance = balance

    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount

    def get_balance(self):
        return self.__balance

acc = BankAccount("Tom", 1000)
acc.deposit(500)
print(acc.get_balance())
# print(acc.__balance)   # AttributeError

为什么要藏起来

属性全部公开时,外部可以绕过任何规则直接赋值。把字段收进私有名字,再在方法里检查,才能拒绝明显不合法的值:

class Bad:
    def __init__(self):
        self.balance = 100

b = Bad()
b.balance = -9999

双下划线与名字改写

以双下划线开头、结尾少于两个下划线的属性,会被改写成 _类名__属性。外部写 obj.__code 通常失败,但若执意访问改写后的名字,技术上仍可能读到。它是提醒与阻碍,不是加密:

class Secret:
    def __init__(self):
        self.__code = 123

s = Secret()
# print(s.__code)         # AttributeError
print(s._Secret__code)    # 能读到,但不要依赖这种写法

单下划线 _name 没有改写机制,只是团队约定别从外面碰。需要更强一点的隔离时,才用双下划线。

getter 与 setter

读用 get 方法,写用 set 方法,并在 set 里做范围检查,是常见的封装读写方式:

class Student:
    def __init__(self, name, score):
        self.name = name
        self.__score = score

    def get_score(self):
        return self.__score

    def set_score(self, value):
        if 0 <= value <= 100:
            self.__score = value
        else:
            print("成绩必须在 0~100 之间")

s = Student("Tom", 85)
s.set_score(95)
s.set_score(200)
print(s.get_score())

@property

若希望外部仍写成 s.score / s.score = 95,同时又走校验逻辑,可以用 @property@score.setter。看起来像属性,执行的仍是方法:

class Student:
    def __init__(self, name, score):
        self.name = name
        self.__score = score

    @property
    def score(self):
        return self.__score

    @score.setter
    def score(self, value):
        if 0 <= value <= 100:
            self.__score = value
        else:
            raise ValueError("成绩必须在 0~100 之间")

s = Student("Tom", 85)
print(s.score)
s.score = 95
print(s.score)

私有方法

只给类内部复用的辅助步骤,也可以做成双下划线方法。外部不能直接调用,类内其它方法可以:

class Calculator:
    def __init__(self, n):
        self.n = n

    def __validate(self):
        if self.n < 0:
            raise ValueError("不能是负数")

    def square(self):
        self.__validate()
        return self.n * self.n

c = Calculator(4)
print(c.square())
# c.__validate()   # AttributeError

常见误区

  • _x 不是强制私有,类外仍可读写。
  • 双下划线改写挡不住刻意访问,也不承担保密职责。
  • 不必把所有字段都做成私有再配一套存取方法。没有校验需求的简单数据,公开属性往往更清晰。