理解Python命名机制
本文最初发表于恋花蝶的博客(http://blog.csdn.net/lanphaday),欢迎转载,但必须保留此声明且不得用于商业目的。谢谢。
<iframe id="alimamaifrm" style="WIDTH: 750px; HEIGHT: 110px" border="0" name="alimamaifrm" marginwidth="0" marginheight="0" src="http://p.alimama.com/cpacode.php?t=A&pid=mm_10108440_0_0&w=750&h=110&rn=1&cn=3&ky=%CA%E9&cid=50000072&bgc=FFFFFF&bdc=E6E6E6&tc=0000FF&dc=000000" frameborder="0" width="750" scrolling="no" height="110"></iframe>
引子
我热情地邀请大家猜测下面这段程序的输出:
class A(object):
def __init__(self):
self.__private()
self.public()
def __private(self):
print 'A.__private()'
def public(self):
print 'A.public()'
class B(A):
def __private(self):
print 'B.__private()'
def public(self):
print 'B.public()'
b = B()
初探
正确的答案是:
A.__private()
B.public()
如果您已经猜对了,那么可以不看我这篇博文了。如果你没有猜对或者心里有所疑问,那我的这篇博文正是为您所准备的。
一切由为什么会输出“A.__private()”开始。但要讲清楚为什么,我们就有必要了解一下Python的命名机制。
据 Python manual,变量名(标识符)是Python的一种原子元素。当变量名被绑定到一个对象的时候,变量名就指代这个对象,就像人类社会一样,不是吗?当变量名出现在代码块中,那它就是本地变量;当变量名出现在模块中,它就是全局变量。模块相信大家都有很好的理解,但代码块可能让人费解些。在这里解释一下:
代码块就是可作为可执行单元的一段Python程序文本;模块、函数体和类定义都是代码块。不仅如此,每一个交互脚本命令也是一个代码块;一个脚本文件也是一个代码块;一个命令行脚本也是一个代码块。
接下来谈谈变量的可见性,我们引入一个范围的概念。范围就是变量名在代码块的可见性。如果一个代码块里定义本地变量,那范围就包括这个代码块。如果变量定义在一个功能代码块里,那范围就扩展到这个功能块里的任一代码块,除非其中定义了同名的另一变量。但定义在类中的变量的范围被限定在类代码块,而不会扩展到方法代码块中。
迷踪
据上节的理论,我们可以把代码分为三个代码块:类A的定义、类B的定义和变量b的定义。根据类定义,我们知道代码给类A定义了三个成员变量(Python的函数也是对象,所以成员方法称为成员变量也行得通。);类B定义了两个成员变量。这可以通过以下代码验证:
>>> print '\n'.join(dir(A))
_A__private
__init__
public
>>> print '\n'.join(dir(B))
_A__private
_B__private
__init__
public
咦,为什么类A有个名为_A__private的 Attribute 呢?而且__private消失了!这就要谈谈Python的私有变量轧压了。
探究
懂Python的朋友都知道Python把以两个或以上下划线字符开头且没有以两个或以上下划线结尾的变量当作私有变量。私有变量会在代码生成之前被转换为长格式(变为公有)。转换机制是这样的:在变量前端插入类名,再在前端加入一个下划线字符。这就是所谓的私有变量轧压(Private name mangling)。如类A里的__private标识符将被转换为_A__private,这就是上一节出现_A__private和__private消失的原因了。
再讲两点题外话:
一是因为轧压会使标识符变长,当超过255的时候,Python会切断,要注意因此引起的命名冲突。
二是当类名全部以下划线命名的时候,Python就不再执行轧压。如:
>>> class ____(object):
def __init__(self):
self.__method()
def __method(self):
print '____.__method()'
>>> print '\n'.join(dir(____))
__class__
__delattr__
__dict__
__doc__
__getattribute__
__hash__
__init__
__method # 没被轧压
__module__
__new__
__reduce__
__reduce_ex__
__repr__
__setattr__
__str__
__weakref__
>>> obj = ____()
____.__method()
>>> obj.__method() # 可以外部调用
____.__method()
现在我们回过头来看看为什么会输出“A.__private()”吧!
真相
相信现在聪明的读者已经猜到答案了吧?如果你还没有想到,我给你个提示:真相跟C语言里的宏预处理差不多。
因为类A定义了一个私有成员函数(变量),所以在代码生成之前先执行私有变量轧压(注意到上一节标红的那行字没有?)。轧压之后,类A的代码就变成这样了:
class A(object):
def __init__(self):
self._A__private() # 这行变了
self.public()
def _A__private(self): # 这行也变了
print 'A.__private()'
def public(self):
print 'A.public()'
是不是有点像C语言里的宏展开啊?
因为在类B定义的时候没有覆盖__init__方法,所以调用的仍然是A.__init__,即执行了self._A__private(),自然输出“A.__private()”了。
下面的两段代码可以增加说服力,增进理解:
>>> class C(A):
def __init__(self): # 重写__init__,不再调用self._A__private
self.__private() # 这里绑定的是_C_private
self.public()
def __private(self):
print 'C.__private()'
def public(self):
print 'C.public()'
>>> c = C()
C.__private()
C.public()
############################
>>> class A(object):
def __init__(self):
self._A__private() # 调用一个没有定义的函数,Python会把它给我的 ^_^~
self.public()
def __private(self):
print 'A.__private()'
def public(self):
print 'A.public()'
>>>a = A()
A.__private()
A.public()
<iframe id="alimamaifrm" style="WIDTH: 750px; HEIGHT: 110px" border="0" name="alimamaifrm" marginwidth="0" marginheight="0" src="http://p.alimama.com/cpacode.php?t=A&pid=mm_10108440_0_0&w=750&h=110&rn=1&cn=3&ky=&cid=251602&bgc=FFFFFF&bdc=E6E6E6&tc=0000FF&dc=000000" frameborder="0" width="750" scrolling="no" height="110"></iframe>
分享到:
相关推荐
Python的命名机制是其语言设计的核心部分之一,它关乎到变量的定义、访问以及作用域。在Python中,变量名(标识符)是程序的基本构建块,它们与对象绑定,代表这些对象。根据变量出现的位置,它们可以是局部变量、...
本文将详细介绍Python中如何处理XML命名空间,包括理解命名空间的基本概念、如何定义和使用命名空间前缀以及如何在Python代码中操作带有命名空间的XML文档。 #### 一、命名空间简介 命名空间是XML文档中用来区分...
在深入理解Python的过程中,我们需要了解多个方面的知识,包括但不限于Python的环境搭建、基础语法、数据类型、函数、模块、面向对象编程、文件操作、网络编程、以及Web开发等。以下是根据文档内容总结的详细知识点...
深入理解Python的特性对于提升编程技能和优化代码至关重要。以下是对标题和描述中提到的几个关键知识点的详细解析: 1. **动态类型**:Python是动态类型的,这意味着在编写代码时无需预先声明变量的数据类型。变量...
### Python命名空间与作用域详解 #### 一、Python命名空间 在Python中,命名空间是一个核心概念,它主要用于组织并隔离程序中的不同元素(如变量、函数、类等),从而确保这些元素能够独立地存在,即使它们拥有...
深入理解Python特性 Python是一种高级的、解释型的编程语言,具有简洁、易学、易用、灵活、跨平台等特点。 Python的特性包括: 1.2 本书作用:本书旨在帮助读者深入理解Python语言的特性和编程理念,从基础知识...
在本文中,我们将深入探讨如何使用Python和PyTorch实现Lattice LSTM(晶格长短期记忆网络)进行中文命名实体识别(NER)。命名实体识别是自然语言处理(NLP)领域的一个核心任务,它涉及到从文本中识别出具有特定...
总的来说,Python-LSTMCRF命名实体识别序列标记是一个结合了深度学习和统计建模的强大工具,它利用LSTM的上下文理解能力和部分CRF的序列建模能力,为NLP任务提供了一种有效的解决方案。对于想要在NER领域深入研究...
这是Python的封装机制,保护数据不被随意修改。 8. **专有变量**:以双下划线开头和结尾的变量,如`__doc__`、`__class__`,是Python的内置特殊变量,不应随意创建类似命名的变量。 9. **函数和方法**:函数名同样...
Python源码剖析是深入理解Python这门编程语言的关键途径。Python作为一种广泛应用于科学计算、数据分析、人工智能、网站开发等领域的高级编程语言,其源代码的分析能够帮助开发者理解Python的内部工作原理,提升编程...
例如,通过`PyDictObject`来模拟变量名到变量值的映射,这是理解Python如何处理变量和命名空间的关键。 #### 二、SmallPython中的对象机制 - **基础对象结构**:SmallPython中定义了一个通用的`PyObject`结构体,...
Stevedore的文档提供了一个全面的框架,用于理解和实现在Python应用程序中使用插件的模式,这些模式在OpenStack等大型分布式系统中经常用到。通过阅读这些内容,开发者可以学习如何扩展和定制自己的应用程序,以支持...
### 深入理解Python中的命名空间查找规则LEGB #### 一、引言 Python是一种高级编程语言,因其简洁的语法和强大的功能而受到广大开发者的喜爱。Python中的命名空间是一个十分重要的概念,理解其背后的机制对于编写...
本篇将深入理解Python中的模块机制,并通过分析"runoob-python-module-test"这个示例来探讨相关知识点。 首先,模块化编程是提高代码可读性、可维护性和复用性的关键。Python通过`import`语句来引入模块,例如: `...
- **语言的大致分类**:在此基础上,进一步探讨了编程语言的不同分类方式,帮助读者理解Python在众多编程语言中的位置。 - **读音问题**:解决了一些初学者对于Python名称发音上的疑问,有助于消除学习初期的语言...
总的来说,这个示例提供了MFC与Python集成的一个简单范例,对于了解不同语言间的协作机制,以及如何在Windows环境中利用Python的强大功能扩展C++应用,具有很好的学习价值。在实际开发中,这样的跨语言调用技术可以...
在Python中,程序执行是由解释器逐步解释和执行代码的,理解命名和绑定对于理解Python的作用域和生命周期非常重要。而异常机制是Python中处理错误和异常情况的标准方法。 导入系统部分解释了如何在Python代码中导入...