Python __func 与 _func 的区别引起的思考

Python中的__function_function有不同的约定和用途。

__function

__function(双下划线前缀):

  • 这被称为”name mangling”(名称修饰)
  • 用于实现类的私有属性和方法
  • Python会将这样的名称在内部重命名,使其更难从外部直接访问
  • 主要目的是防止子类意外覆盖或重写这些方法
  • 例如,__init__是一个特殊的魔术方法(构造函数)
  • 通过名称修饰,__method会被改变为_ClassName__method

示例:

1
2
3
4
5
6
7
8
9
10
11
12
class MyClass:
def __private_method(self):
print("This is a private method")

def public_method(self):
# 可以在类的内部正常调用私有方法
self.__private_method()

obj = MyClass()
# obj.__private_method() # 这会报错
# 但实际上可以这样访问:
# obj._MyClass__private_method() # 不推荐这样做

_function

_function(单下划线前缀):

  • 这是一个约定,表示”这是一个供内部使用的方法”
  • 不是严格的私有方法
  • 只是一个社区约定,表示”这个方法不应该被直接外部调用”
  • 从语法上讲,仍然可以直接访问
  • 通常用于表示”这是一个内部实现,不应该被视为公共API的一部分”

示例:

1
2
3
4
5
6
7
8
9
class MyClass:
def _internal_method(self):
print("This is an internal method")

def public_method(self):
self._internal_method() # 正常使用

obj = MyClass()
obj._internal_method() # 虽然可行,但不推荐

主要区别总结:

  • __function:通过名称修饰实现更强的私有性,Python会改变其名称
  • _function:只是一个约定,表示不应直接使用,但实际上仍可访问

最佳实践:

  • 尊重_function的约定,不要直接调用带有单下划线的方法
  • 如果真的需要严格的私有性,使用__function
  • 在设计类时,考虑方法的intended使用方式

深入名称修饰机制

名称修饰(Name Mangling)是Python中一种特殊的标识符重命名机制,主要用于类的属性和方法。

当你在类中定义一个以双下划线(__)开头的属性或方法时,Python会自动修改其名称。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class AdvancedClass:
def __init__(self):
self.__secret = "私有属性"
self._protected = "受保护属性"

def __private_method(self):
print("这是一个私有方法")

def _protected_method(self):
print("这是一个受保护方法")

def access_private(self):
# 展示名称修饰的实际机制
print(self.__dict__) # 查看实际属性名
self.__private_method() # 内部可以调用


obj = AdvancedClass()
# print(obj.__secret) # 直接访问会报错
print(obj.__dict__) # 会发现__secret实际上被重命名
print([item for item in dir(obj) if not item.startswith('__')])
print(obj._AdvancedClass__secret) # 这才是正确的访问方式
"""
{'_AdvancedClass__secret': '私有属性', '_protected': '受保护属性'}
['_AdvancedClass__private_method', '_AdvancedClass__secret', '_protected', '_protected_method', 'access_private']
私有属性
"""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Parent:
def __init__(self):
self.__secret = "父类秘密"


class Child(Parent):
def __init__(self):
super().__init__()
self.__secret = "子类秘密" # 这实际上是一个新的属性

def print_secrets(self):
print("父类秘密:", self._Parent__secret)
print("子类秘密:", self.__secret)


child = Child()
child.print_secrets()
"""
父类秘密: 父类秘密
子类秘密: 子类秘密
"""

名称修饰的目的

  1. 防止意外重写:子类不会意外覆盖父类的私有属性
  2. 提供一定的封装:虽然不是绝对私有,但增加了访问的复杂性
  3. 命名冲突避免:在继承中防止命名冲突

实现原理

Python 的名称修饰是在编译时进行的:

  • 编译器检测到以__开头的标识符
  • 自动将类名插入到标识符前
  • 创建一个新的、唯一的名称
  • 以双下划线开头并以双下划线结尾的方法(如__init__)不会被修饰

查看名称修饰的字节码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import dis

class InspectMangling:
def __init__(self):
self.__hidden = "隐藏内容"

def show_bytecode(self):
print(dis.dis(self.__init__))

obj = InspectMangling()
obj.show_bytecode()
"""
6 0 LOAD_CONST 1 ('隐藏内容')
2 LOAD_FAST 0 (self)
4 STORE_ATTR 0 (_InspectMangling__hidden)
6 LOAD_CONST 0 (None)
8 RETURN_VALUE
None
"""

属性访问控制的高级模式

在属性的 setter 方法中调用私有方法是一种常见且实用的模式。这种方式可以帮助我们实现更复杂的验证、转换或预处理逻辑。

基本模式扩展

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class SmartAccessClass:
def __init__(self):
self.__private_attr = None

@property
def private_attr(self):
"""
获取属性,可以添加额外的访问控制逻辑
"""
if self.__private_attr is None:
return 0 # 提供默认值
return self.__private_attr

@private_attr.setter
def private_attr(self, value):
"""
setter方法中调用私有方法进行验证和转换
"""
converted_value = self.__convert_value(value)
if self.__validate_value(converted_value):
self.__private_attr = converted_value
else:
raise ValueError("值未通过验证")

def __convert_value(self, value):
try:
return int(value)
except (TypeError, ValueError):
return 0

def __validate_value(self, value):
return 0 <= value <= 100


class InheritanceTest(SmartAccessClass):
def __init__(self):
super().__init__()
# 尝试访问父类的私有属性会失败
# self.__private_attr = 10 # 这会创建一个新的属性,而不是访问父类的

def attempt_access(self):
# 这种方式无法直接访问父类的私有属性
# print(self.__private_attr) # 会报错
pass

复杂的转换和验证逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class AdvancedAccessControl:
def __init__(self):
self.__sensitive_data = None

@property
def sensitive_data(self):
"""
安全地获取敏感数据
"""
return self.__decrypt_data(self.__sensitive_data)

@sensitive_data.setter
def sensitive_data(self, value):
"""
设置敏感数据,包括加密和验证
"""
# 先验证输入
if not self.__is_valid_input(value):
raise ValueError("输入不合法")
# 进行数据转换和加密、存储
encrypted_value = self.__encrypt_data(value)
self.__sensitive_data = encrypted_value

def __is_valid_input(self, value):
return (
isinstance(value, str) and
len(value) > 5 and
any(char.isdigit() for char in value)
)

def __encrypt_data(self, value):
return ''.join(chr(ord(c) + 1) for c in value)

def __decrypt_data(self, encrypted_value):
if encrypted_value is None:
return None
return ''.join(chr(ord(c) - 1) for c in encrypted_value)

obj = AdvancedAccessControl()
obj.sensitive_data = "secret123" # 设置成功
print(obj.sensitive_data) # 解密后输出

带有日志和审计的访问控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import logging
from functools import wraps


class AuditedAccessClass:
def __init__(self):
self.__critical_value = None
self.__logger = logging.getLogger(self.__class__.__name__)

def audit_access(method):
"""
装饰器:记录方法调用
"""

@wraps(method)
def wrapper(self, *args, **kwargs):
try:
result = method(self, *args, **kwargs)
self.__logger.info(f"成功调用 {method.__name__}")
return result
except Exception as e:
self.__logger.error(f"调用 {method.__name__} 失败: {str(e)}")
raise

return wrapper

@property
@audit_access
def critical_value(self):
"""
带审计的属性获取
"""
return self.__critical_value

@critical_value.setter
@audit_access
def critical_value(self, value):
"""
带审计的属性设置
"""
# 调用私有方法进行处理
processed_value = self.__process_critical_value(value)
# 存储处理后的值
self.__critical_value = processed_value

def __process_critical_value(self, value):
"""
私有方法:处理关键值
"""
# 复杂的处理逻辑
if not isinstance(value, (int, float)):
raise TypeError("必须是数值类型")

return round(float(value), 2)


audit_obj = AuditedAccessClass()
audit_obj.critical_value = 123.456 # 将被四舍五入为123.46
print(audit_obj.critical_value) # 123.46