# 私有化及_下划线命名

# 引言

Python中没有真正的私有属性或方法,没有真正的私有化,但有一些和命名有关的约定,让编程人员处理一些需要私有化的情况,我们常常需要区分私有方法、属性和公有方法、属性以方便管理和调用。

在变量、方法命名中有下列几种情况:

  • xx​​​​ 公有变量/方法

  • _xx​​​​ 前置单下划线

    • **建议 ** 不在外部使用
    • import * 无法导入
  • __xx​​​​ 前置双下划线

    • 无法在外部调用
    • 使用名字重整技术将命名object​​变为_Class_object​​的形式
  • __xx__​​​​ 前后双下划线

    • 魔法对象或属性,有着特殊作用。
  • xx_​​​​ 后置单下划线

# _单前置下划线

一般Python约定前置单下划线 _​ 的属性和方法为私有方法或属性,以提示该属性和方法 不应 在外部调用。Python中的前置单下划线只是一个公认的约定,至少在涉及变量名和方法名时是这样的这个约定对Python解释器并没有特殊含义。PEP 8​ 中定义了这个约定( PEP 8​ 是最常用的 Python​ 代码风格指南)。

与Java不同,Python在 “私有”“公共” 变量之间并没有很强的区别。

前置下划线会影响从模块中导入名称的方式,使用 通配符导入 从这个模块中导入所有名称时Python 不会 导入带有前置单下划线的名称(除非模块中定义了__all__​ 列表覆盖了这个行为。

# demo.py
_key = '123'
def _set_key(key):
	global _key
	_key = key
# test.py
from demo import *
print(_key)         # NameError: name '_key' is not defined
_set_key('567')     # NameError: name '_set_key' is not defined

对此解释器会抛出异常:NameError: name '_key' is not defined​​。

但并非 demo.py​ 中的前置单下划线变量/方法在 test.py​ 中就不可以使用,完全可以 import module​,然后通过 module.xxx​ 方式,test.py​ 代码做如下调整:

# test.py
import demo

print(demo._key)		# 正常使用
demo._set_key('789')	# 正常调用
print(demo._key)		# 正常使用

# __前置双下划线

前置双下划线用于对象的数据封装,以此命名的属性或者方法为类的私有属性或者私有方法,在外部无法访问直接访问私有属性或方法。

Python会通过 名字重整 name mangling​机制实现的,目的是以防类意外重写基类的方法或属性。

名字重整 name mangling​ 的技术,又叫 name decoration命名修饰。在很多现代编程语言中,这一技术用来解决 需要唯一名称而引起的问题,比如命名冲突/重载等。

类中所有以双下划线开头的名称都会自动变成 _Class_object​ 的新名称,如 __name​ >>> _Foo__name​ ,我们可以用 dir()​来查看类中成员详情。

通过这种机制可以阻止继承类重新定义或者更改方法的实现,如果在子类中向 __名字​ 赋值,那么会在子类中定义的一个与父类相同名字的属性。

# coding:utf8

class Foo(object):
	def __init__(self):
		self__name = "private attribute"
	def getname():
		return self.__name
	def __method():
		print("private method")
	def run(self):
		self.__method()
In [7]: dir(f)
Out[7]:
['_Foo__method',
 '_Foo__name',
 '__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 'getname',
 'run']

In [8]: f._Foo__name
Out[8]: 'private attribute'

In [9]: f._Foo__method()
private method

# 前后双下划线

前后均带双下划线的命名,一般用于特殊方法的命名,用来实现对象的一些行为或者功能,比如 __new__()​​ 方法用来创建实例,__init__()​​ 方法用来初始化对象,x + y​​操作被映射为方法 x.__add__(y)​​,序列或者字典的索引操作 x[k]​​ 映射为x.__getitem__(k)​​,__len__()、__str__()​​ 分别被内置函数 len()、str()​​调用等等。

# 后置单下划线_

后置单下划线,用于避免与Python关键词的冲突。如下:

list_ = ["wang", "hui", "zack"]
dict_ = { 
    "name": "hui",
    "age": 21
}