面向对象编程(OOP,object-oriented programming)。
1.1 基本概念
1.1.1 类与实例
类与实例互相关联,类是对象的定义,而实例是“真正的实物”,它存放了类中所定义的对象的基本信息。语法如下所示:
1 class MyNewObjectType(bases): 2 'define MyNewObjectType class' 3 class_suite
关键字class,紧接着是类名,随后是定义类的类体代码,通常是由各种各样的定义和声明组成。参数bases可以是一个(单继承)或多个(多重继承)用于继承的父类。object是“所有类之母”,如果没有继承任何其它父类,object将作为默认的父类,它位于所有类继承结构的最上层。
创建一个实例的过程称作实例化,过程如下:
1 myFirstObject = MyNewObjectType()
类名使用我们熟悉的操作符(()),以“函数调用”的形式出现。
1.1.2 方法
给类添加功能,就是给类添加方法。Python中,方法定义在类定义中,但只能被实例所调用。调用一个方法的最终途径为:(1)定义类(和方法);(2)创建一个实例;(3)用这个实例调用方法。如下:
1 class MyDataWithMethod(object): 2 def printfoo(self): 3 print ‘You invoked printfoo()’
self参数在所有的方法声明中都存在,这个参数代表实例对象本身,当用实例调用方法时,由解释器悄悄的传递给方法的,不需要自己传递self进来,是自动传入的。
1.1.3 创建一个类(类定义)
1 class AddrBookEntry(object): 2 'address book entry class' 3 def __init__(self, nm, ph): #定义构造器 4 self.name = nm 5 self.phone = ph 6 print 'Created instance for:', self.name 7 def updatePhone(self, newph): 8 self.phone = newph 9 print 'Updated phone# for:', self.name
在AddrBookEntry类定义中,定义了__init__()和updatePhone()两个方法,其中__init__()在实例化时被调用,实例化时对__init__()的一种隐式调用。
1.1.4 创建实例
1 >>> john = AddrBookEntry('John', '400-818') 2 Created instance for: John 3 >>> jane = AddrBookEntry('Jane', '412-818') 4 Created instance for: Jane
实例化调用会自动调用__init__()。self把实例对象自动导入__init__()。
1.1.5 访问实例属性
1 >>> john 2 <__main__.AddrBookEntry object at 0x7fbd12bf5e50> 3 >>> john.name 4 'John' 5 >>> john.phone 6 '400-818' 7 >>>
一旦实例被创建后,实例属性被__init__()设置了。
1.1.6 方法调用(通过实例)
1 >>> john.updatePhone('415-785') 2 Updated phone# for: John 3 >>> john.phone 4 '415-785'
1.1.7 创建子类
靠继承来进行子类化是创建和定制新类型的一种方式。Python中,当一个类被派生出来,子类就继承了基类的属性。
1 class EmplAddrBookEntry(AddrBookEntry): 2 'Employee Address Book Entry class' 3 def __init__(self, nm, ph, id, em): 4 AddrBookEntry.__init__(self, nm, ph) 5 self.empid = id 6 self.email = em 7 def updateEmail(self, newem): 8 self.email = newem 9 print 'Updated email address for:', self.name
如果需要,每个子类最好定义自己的构造器,不然基类的构造器将会被调用。
1.1.8 使用子类
1 >>> john = EmplAddrBookEntry('John', '123', 42, 'john@spam.doe') 2 Created instance for: John 3 >>> john 4 <__main__.EmplAddrBookEntry object at 0x7fbd12bf5f90> 5 >>> john.name 6 'John' 7 >>> john.phone 8 '123' 9 >>> john.email 10 'john@spam.doe' 11 >>> john.empid 12 42 13 >>> john.updatePhone('123-345') 14 Updated phone# for: John 15 >>> john.phone 16 '123-345' 17 >>> john.updateEmail('123@163.com') 18 Updated email address for: John 19 >>> john.email 20 '123@163.com'
【注】:类名通常由大写字母开头,这是标准惯例。
1.2 类
类是一种数据结构,可以用来定义对象。
1.2.1 创建类
Python使用关键字class来创建类。
1 class ClassName(bases): 2 ‘class documentation string’ 3 class_suite
1.2.2 声明和定义
定义(类体)紧跟在声明(含class关键字的头行)和可选的文档字符串后面,所有的方法也必须被同时定义。
1.3 类属性
属性是属于另一个对象的数据或者函数元素,可以通过句点标识符来访问。有趣的是当你访问一个属性时,它同时也是一个对象,拥有自己的属性可以访问,这导致了一个属性链。类属性仅与其被定义的类相绑定,实例数据属性是你将会一直用到的主要数据属性。
1.3.1 类的数据属性
数据属性仅仅是所定义的类的变量,可以像其它任何变量一样在类创建后被使用,并且,要么是由类的方法来更新,要么是在主程序其它地方被更新。这种属性称为静态变量或静态数据,表示这些数据是与它们所属的类对象绑定的,不依赖任何类实例。
1 >>> class C(object): 2 ... foo = 100 3 ... 4 >>> C.foo 5 100 6 >>> C.foo += 1 7 >>> C.foo 8 101
1.3.2 Methods
(1)方法
如下:myNoActionMethod方法仅仅作为MyClass类定义一部分定义的函数(这使得方法成为类属性)。表示myNoActionMethod仅应用在MyClass类型的对象(实例)上。
1 >>> class MyClass(object): 2 ... def myNoActionMethod(self): 3 ... pass 4 ... 5 >>> mc = MyClass() 6 >>> mc.myNoActionMethod() 7 >>> myNoActionMethod 8 Traceback (most recent call last): 9 File "<stdin>", line 1, in <module> 10 NameError: name 'myNoActionMethod' is not defined 11 >>> MyClass.myNoActionMethod() 12 Traceback (most recent call last): 13 File "<stdin>", line 1, in <module> 14 TypeError: unbound method myNoActionMethod() must be called with MyClass instance as first argument (got nothing instead) 15 >>>
(2)绑定(绑定及非绑定方法)
Python严格要求,没有实例,方法是不能被调用的。这种概念即Python所描述的绑定概念(binding)。方法必须绑定(到一个实例)才能直接被调用。
1.3.3 决定类的属性
要知道一个类有哪些属性,有两个方法:(1)使用dir()内建函数;(2)通过访问类的字典属性__dict__,返回的是一个字典,键是属性名,键值是对应的属性对象的数据值。
1 >>> class MyClass(object): 2 ... 'MyClass class description' 3 ... myVersion = '1.1' 4 ... def showMyVersion(self): 5 ... print MyClass.myVersion 6 ... 7 >>> dir(MyClass) 8 ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'myVersion', 'showMyVersion'] 9 >>> MyClass.__dict__ 10 <dictproxy object at 0x7fbd12bfdad0> 11 >>> print MyClass.__dict__ 12 {'__module__': '__main__', 'showMyVersion': <function showMyVersion at 0x7fbd12bf7668>, '__dict__': <attribute '__dict__' of 'MyClass' objects>, 'myVersion': '1.1', '__weakref__': <attribute '__weakref__' of 'MyClass' objects>, '__doc__': 'MyClass class description'}
1.3.4 特殊的类属性
对任何类C,下表显示了类C的所有特殊属性:
C.__name__ |
类C的名字(字符串) |
C.__doc__ |
类C的文档字符串 |
C.__bases__ |
类C的所有父类构成的元组 |
C.__dict__ |
类C的属性 |
C.__module__ |
类C定义所在的模块 |
C.__class__ |
实例C对应的类 |
__name__是给定类的字符名字,适用于那种只需要字符串(类对象名字),而非类对象本身的情况。
1.4 实例
如果说类是一种数据结构定义类型,那么实例则声明了一个这种类型的变量。实例是哪些主要用在运行期间时的对象,类被实例化得到实例,该实例的类型就是这个被实例化的类。
1.4.1 初始化:通过调用类对象来创建实例
Python实例化的实现,如下:
1 >>> class MyClass(object): 2 ... pass 3 ... 4 >>> mc = MyClass()
1.4.2 __init__()“构造器”方法
当类被调用,实例化的第一步是创建实例对象,一旦对象创建了,Python会检查是否实现了__init__()方法。如果__init__()没有实现,则返回它的对象。如果__init__()已经被实现,那么它会被调用,实例对象作为第一个参数(self)被传递进去,像标准方法调用一样。调用类时,传进的任何参数都交给了__init__()。
1.4.3 __new__()“构造器”方法
Python用户可以对内建类型进行派生,需要一种途径实例化不可变对象,比如派生字符串、数字等。在这种情况下,解释器则调用类的__new__()方法,一个静态方法,并且传入的参数是在类实例化操作时生成的。__new__()会调用父类的__new__()来创建对象(向上代理)。
1.4.4 __del__()“解构器”方法
由于Python具有垃圾对象回收机制(靠引用计数),这个函数要直到该实例对象所有的引用都被清除掉后才会执行。Python中的解构器是在实例释放前提供特殊处理功能的方法,它们通常没有实现。
注意,解构器只能被调用一次,一旦引用计数为0,则对象就被清除了。总结:
- 不要忘记首先调用父类的__del__()。
- 调动del x不表示调用了x.__del__(),它仅仅是较少x的引用计数。
- 如果你有一个循环引用或其他原因,让一个实例的引用逗留不去,该对象的__del__()可能永远不会被执行。
- __del__()未捕获的异常会被忽略掉,不要在__del__()中干与实例没任何关系的事情。
- 如果定义了__del__,并且实例是某个循环的一部分,垃圾回收器不会终止这个循环—你需要自己显示调用del。
1.5 实例属性
实例拥有数据属性(严格来说是类属性),是某个类的实例相关联的数据值,并且可以通过句点属性标识符来访问。这些值独立于其他实例或类。当一个实例被释放后,它的属性同时被清除了。
1.5.1 “实例化”实例属性(或创建一个更好的构造器)
设置实例的属性可以在实例创建后任意时间进行,也可以在能够访问实例的代码中进行。
(1)在构造器中首先设置实例属性
__init__()是实例创建后第一个被调用的方法,一旦__init__()执行完毕,返回实例对象,即完成了实例化过程。
(2)默认参数提供默认的实例安装
带默认参数的__init__()提供了一个有效的方式来初始化实例,默认参数应该是不变的对象。像列表和字典这样的可变对象可以扮演静态数据,然后在每个方法调用中维护他们的内容。
例如:定义一个类来计算旅馆费用,__init__()构造器对一些实例属性进行初始化。
1 [root@localhost python]# vim hotel.py 2 class HotalRoomCala(object): 3 'Hotel room rate calculator' 4 def __init__(self, rt, sales=0.085, rm=0.1): 5 self.salesTax = sales 6 self.roomTax = rm 7 self.roomRate = rt 8 def calcTotal(self, days=1): 9 daily = round((self.roomRate*(1+self.roomTax + self.salesTax)), 2) 10 return float(days) * daily 11 12 sfo = HotalRoomCala(299) 13 print sfo.calcTotal() 14 print sfo.calcTotal(2) 15 sea = HotalRoomCala(198, 0.086, 0.058) 16 print sea.calcTotal() 17 print sea.calcTotal(4) 18 [root@localhost python]# python hotel.py 19 354.32 20 708.64 21 226.51 22 906.04
(3)__init__()应当返回None
构造器不应当返回任何对象,因为实例对象是自动在实例化调用后返回的,相应的,__init__()就不应该返回任何对象(应当为None);否则,可能出现冲突。
1 >>> class MyClass(): 2 ... def __init__(self): 3 ... print 'initialzed' 4 ... return 1 5 ... 6 >>> mc = MyClass() 7 initialzed 8 Traceback (most recent call last): 9 File "<stdin>", line 1, in <module> 10 TypeError: __init__() should return None
1.5.2 查看实例属性
内建函数dir()可以显示类属性,同样可以打印所有实例属性。相应的,实例也有一个__dict__特殊属性,它是实例属性构成的一个字典。
1 >>> class C(): 2 ... pass 3 ... 4 >>> c = C() 5 >>> c.foo = 'abc' 6 >>> c.bar = '123' 7 >>> dir(c) 8 ['__doc__', '__module__', 'bar', 'foo'] 9 >>> c.__dict__ 10 {'foo': 'abc', 'bar': '123'}
1.5.3 特殊的实例属性
实例仅有两个特殊属性,对于任意对象I:
I.__class__ |
实例化I的类 |
I.__dict__ |
I的属性 |
1.5.4 内建类型属性
内建类型也是类,对内建类型也可以使用dir()。
1.5.5 实例属性vs类属性
类属性仅是与类相关的数据值,和实例属性无关,这些值像静态成员那样被引用,即使在多次实例化中调用类,他们的值都不变。
类和实例都是名称空间,类是类属性的名称空间,实例则是实例属性的。
(1)访问类属性
类属性可以通过类或实例来访问。
1 >>> class C(object): 2 ... version = 1.2 3 ... 4 >>> c = C() 5 >>> c.version 6 1.2 7 >>> c.version = 1.3 8 >>> c.version 9 1.3 10 >>> C.version 11 1.2
(2)从实例中访问类属性必须谨慎
任何对实例属性的赋值都会创建一个实例属性(如果不存在的话)并且对其赋值。如果类属性中存在同名的属性,会“隐藏”类属性,且一旦删除了同名的实例属性,我们操作的就变成了类属性。
1 >>> class C(object): 2 ... version = 1.2 3 ... 4 >>> c = C() 5 >>> c.version 6 1.2 7 >>> c.version = 1.3 8 >>> c.version 9 1.3 10 >>> C.version 11 1.2 12 >>> class Foo(object): 13 ... x = 1.5 14 ... 15 >>> foo = Foo() 16 >>> foo.x 17 1.5 18 >>> foo.x = 1.7 19 >>> Foo.x 20 1.5 21 >>> del foo.x 22 >>> foo.x 23 1.5 24 >>> foo.x += 2 25 >>> foo.x 26 3.5 27 >>> Foo.x 28 1.5 29 >>>
下面的情况有所不同:Python中,属性存在于类字典__dict__中,在类属性可变的情况下,如下所示:
1 >>> class Foo(object): 2 ... x = {1:'poe'} 3 ... 4 >>> foo = Foo() 5 >>> foo.x 6 {1: 'poe'} 7 >>> foo.x[2] = 'valid path' 8 >>> foo.x 9 {1: 'poe', 2: 'valid path'} 10 >>> Foo.x 11 {1: 'poe', 2: 'valid path'} 12 >>> del foo.x #没有屏蔽所以不能删除掉 13 Traceback (most recent call last): 14 File "<stdin>", line 1, in <module> 15 AttributeError: 'Foo' object attribute 'x' is read-only
(3)类属性持久性
静态成员,任凭整个实例(及其属性)如何进展,都不理睬。同时当一个实例在类属性被修改后才创建,那么更新的值就将生效,类属性的修改会影响到所有的实例:
1 >>> class C(object): 2 ... spam = 100 3 ... 4 >>> c1 = C() 5 >>> c1.spam 6 100 7 >>> C.spam += 100 8 >>> c1.spam 9 200 10 >>> c2 = C() 11 >>> c2.spam 12 200 13 >>> del c1 14 >>> C.spam += 200 15 >>> c2.spam 16 400 17 >>>
1.6 绑定和方法调用
Python中的绑定(binding)主要与方法调用相关联。回顾与方法相关知识:方法仅仅是类内部定义的函数;方法只能在其所属的类拥有实例时,才能被调用。当存在一个实例时,方法才被认为绑定到那个实例了。没有实例时方法就是未绑定的。
1.6.1 调用绑定方法
假如现在有一个MyClass类和此类的一个实例mc,调用MyClass.foo()时,只需要调用mc.foo()就可以了。记得self在每一个方法中都是作为第一个参数传递的,当你在实例中调用一个绑定的方法时,self不需要明确的传入了。
1.6.2 调用非绑定方法
调用非绑定方法不经常用到。需要调用一个还没有任何实例中的方法的一个主要场景是:你在派生一个子类,而且你要覆盖父类的方法,这时候你需要调用那个父类中想要覆盖掉的构造方法。
1.7 组合
一个类被定义后,目标是要把它当成一个模块使用,并把这些对象嵌入到你的代码中去,同其它数据类型及逻辑执行流混合使用,可通过组合实现,组合就是让不同的类混合并加入到其他类中,来增加功能和代码重用性。另一种是通过派生。
1.8 子类和派生
面向对象设计(OOD)允许类特征在子孙类或子类中进行继承,这些子类从基类(或称祖先类、超类)继承它们的核心属性。而且,这些派生可以扩展到多代。在一个层次的派生关系中的相关类(或者是在类树图中垂直相邻)是父类和子类的关系。从同一个父类派生出来的这些类(或者在类树图中水平相邻)是同胞关系。父类和所有高层类都被认为是祖先。
(1)创建子类
语法如下:
1 class SubClassName (ParentClass1[, ParentClass2, …]): 2 ‘optional class documentation string’ 3 class_suite
例如:
1 >>> class Parent(object): 2 ... def parentMethod(self): 3 ... print 'calling parent method' 4 ... 5 >>> class Child(Parent): 6 ... def childMethod(self): 7 ... print 'calling child method' 8 ... 9 >>> p = Parent() 10 >>> p.parentMethod() 11 calling parent method 12 >>> c = Child() 13 >>> c.childMethod() 14 calling child method 15 >>> c.parentMethod() 16 calling parent method 17 >>>
1.9 继承
继承描述了基类的属性如何“遗传”给派生类,一个子类可以继承它的基类的任何属性,不管是数据属性还是方法。
1.9.1 __bases__类属性
__bases__类属性对任何(子)类,它是一个包含其父类的集合的元组。“父类”是相对于所有基类(它包括了所有祖先类)而言的。那些没有父类的类,__bases__属性为空。
1 >>> class A(object):pass 2 ... 3 >>> class B(A):pass 4 ... 5 >>> class C(B):pass 6 ... 7 >>> class D(B, A): pass 8 ... 9 >>> A.__bases__ 10 (<type 'object'>,) 11 >>> B.__bases__ 12 (<class '__main__.A'>,) 13 >>> C.__bases__ 14 (<class '__main__.B'>,) 15 >>> D.__bases__ 16 (<class '__main__.B'>, <class '__main__.A'>)
1.9.2 通过继承覆盖方法
例如:
1 >>> class P(object): 2 ... def foo(self): 3 ... print 'Hi, i am P-foo()' 4 ... 5 >>> p = P() 6 >>> p.foo() 7 Hi, i am P-foo() 8 >>> class C(P): 9 ... def foo(self): 10 ... print 'Hi, i am C-foo()' 11 ... 12 >>> c = C() 13 >>> c.foo() 14 Hi, i am C-foo() 15 >>> p.foo() 16 Hi, i am P-foo()
核心笔记:重写__init__不会自动调用基类的__init__
当从一个带构造器__init__()的类派生,如果不去覆盖__init__(),它将会被继承并自动调用。但如果你在子类中覆盖了__init__(),子类被实例化时,基类的__init__()就不会自动调用。
1 >>> class P(object): 2 ... def __init__(self): 3 ... print 'calling p' 4 ... 5 >>> class C(P): 6 ... def __init__(self): 7 ... print 'calling C' 8 ... 9 >>> c = C() 10 calling C 11 >>> class C(P): 12 ... def __init__(self): 13 ... P.__init__(self) 14 ... print 'calling C' 15 ... 16 >>> c = C() 17 calling p 18 calling C 19 >>> class C(P): 20 ... def __init__(self): 21 ... super(C, self).__init__() 22 ... print 'calling C' 23 ... 24 >>> c = C() 25 calling p 26 calling C 27 >>>
使用super()的好处是不需要明确给出任何基类名字,使用super()不需要明确提供父类。
1.9.3 多重继承
Python允许子类继承多个基类。难点在于如何正确找到没有在当前(子)类定义的属性。
(1)方法解释顺序(MRO)
之前的版本,经典类使用的是深度优先算法,Python2.3版本之后,使用的是广度优先。例如:
1 >>> class P1(object): 2 ... def foo(self): 3 ... print 'P1-foo()' 4 ... 5 >>> class P2(object): 6 ... def foo(self): 7 ... print 'P2-foo()' 8 ... 9 >>> class P2(object): 10 ... def foo(self): 11 ... print 'P2-foo()' 12 ... 13 ... def bar(self): 14 ... print 'P2-bar()' 15 ... 16 >>> class C1(P1, P2): 17 ... pass 18 ... 19 >>> class C2(P1, P2): 20 ... def bar(self): 21 ... print 'C2-bar()' 22 ... 23 >>> class GC(C1, C2): 24 ... pass 25 ... 26 >>> gc = GC() 27 >>> gc.foo() 28 P1-foo() 29 >>> gc.bar() 30 C2-bar() 31 >>> GC.__mro__ 32 (<class '__main__.GC'>, <class '__main__.C1'>, <class '__main__.C2'>, <class '__main__.P1'>, <class '__main__.P2'>, <type 'object'>) 33 >>>
关系图如下:
新式类中,gc查找foo()方法的顺序为:GC=>C1=>C2=>P1;查找bar()的顺序为:GC=>C1=>C2
新式类的__mro__属性,可以看出查找的顺序。
1.10 类、实例和其他对象的内建函数
(1)issubclass(sub, sup)
布尔函数,判断一个类是另一个类的子类或子孙类。
1 >>> class A(): pass 2 ... 3 >>> class B(A): pass 4 ... 5 >>> issubclass(B, A) 6 True 7 >>> class C(B): pass 8 ... 9 >>> issubclass(C, A) 10 True
(2)isinstance(object, classinfo)
布尔函数,判断实例object是否是类classinfo的一个实例。
1 >>> class A(): pass 2 ... 3 >>> class B(A): pass 4 ... 5 >>> a = A() 6 >>> b = B() 7 >>> isinstance(a, A) 8 True 9 >>> isinstance(b, B) 10 True 11 >>> isinstance(a, B) 12 False 13 >>> isinstance(b, A) 14 True
(3)hasattr(object, name)、getattr(object, name[, default])、setattr(object, name, value)、delattr(object, name)
*attr()系列函数可在各种对象下工作,不限于类和实例。正在处理的对象作为第一个参数,属性名的字符串名字作为第二个参数。hasattr()函数是布尔型,判断一个对象是否有一个特定的属性;getattr()和setattr()对应的取得和赋值给对象的属性;delattr()函数从一个对象中删除属性。
1 >>> class myClass(object): 2 ... def __init__(self): 3 ... self.foo = 100 4 ... 5 >>> myInst = myClass() 6 >>> hasattr(myInst, 'foo') 7 True 8 >>> getattr(myInst, 'foo') 9 100 10 >>> hasattr(myInst, 'bar') 11 False 12 >>> getattr(myInst, 'bar') 13 Traceback (most recent call last): 14 File "<stdin>", line 1, in <module> 15 AttributeError: 'myClass' object has no attribute 'bar' 16 >>> setattr(myInst, 'bar', 'my attr') 17 >>> getattr(myInst, 'bar') 18 'my attr' 19 >>> delattr(myInst, 'foo') 20 >>> hasattr(myInst, 'foo') 21 False
(4)dir()
dir()作用在实例上,显示实例变量,还有在实例所在的类及所有它的基类中定义的方法和类属性;
dir()作用在类上,显示类以及它的所有基类的__dict__中的内容,但不会显示定义在元类中的类属性;
dir()作用在模块上,显示模块的__dict__的内容;
dir()不带参数时,显示调用者的局部变量。
(5)super(type[, object-or-type])
super()函数的作用是帮助程序员找出相应的父类,然后方便调用相关的属性。
给出type,super()“返回此type的父类”,如果希望父类被绑定,可以传入object参数(object必须是type类型的),否则父类不会被绑定。第二个参数也可以是一个类型,但必须是type的一个子类。通常,当给出object时:
如果object是一个实例,instance(object, type)就必须返回True;
如果object是一个类或类型,issubclass(object, type)就必须返回True。
(6)vars([object])
vars()和dir()类似,只是给定对象必须有一个__dict__属性,vars()返回一个字典,它包含了对象存储于其__dict__中的属性(键)和值。如果没有提供对象作为vars()的一个参数,它将显示一个包含本地名称空间的属性(键)及其值的字典。
1 >>> class C(object): 2 ... pass 3 ... 4 >>> c = C() 5 >>> c.foo = 100 6 >>> c.bar = 'python' 7 >>> c.__dict__ 8 {'foo': 100, 'bar': 'python'} 9 >>> vars(c) 10 {'foo': 100, 'bar': 'python'}
1.11 私有化
默认情况下,属性在Python中都是“公开的”,类所在模块和导入了类所在模块的其它模块的代码都可以访问到。可通过“访问控制符”来限定成员函数的访问。
(1)双下划线(__)
由双下划线开始的属性在运行时被“混淆”,所以直接访问是不允许的,实际上,会在名字前面加上下划线和类名。尽管这样提供了某种层次上的私有化,但算法处于公共域中并且很容易被“击败”,这更多的是一种对导入源代码无法获取的模块或对同一模块中的其它代码的保护机制。
另一个目的是为了保护__XXX变量不与父类名字空间冲突,如果在类中有一个__XXX属性,它将不会被其子类中的__XXX属性覆盖,子类的代码可以就可以安全的使用__XXX,而不必担心它会影响到父类中的__XXX。
1 >>> class A(object): 2 ... __foo = 100 3 ... 4 >>> class B(A): 5 ... __foo = 200 6 ... 7 >>> b = B() 8 >>> dir(b) 9 ['_A__foo', '_B__foo', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__'] 10 >>> b._B__foo 11 200 12 >>> b._A__foo 13 100
(2)单下划线(_)
简单的模块级私有化只需要在属性名前使用一个单下划线字符,这就防止模块的属性用“from mymodule import *”来加载。这是严格基于作用域的,所以同样适用于函数。
附录:类、实例及其他对象的内建函数
内建函数 |
描述 |
issubclass(sub, sup) |
如果类sub是sup的子类,则返回True,否则为False |
isinstance(obj1, obj2) |
如果实例obj1是类obj2或者obj2子类的一个实例;或者如果obj1是obj2的类型,则返回True,否则为False |
hasattr(obj, attr) |
如果obj有属性attr(用字符串给出),返回True,否则返回False |
getattr(obj, attr[, default]) |
获取obj的attr属性,与返回obj.attr类似,如果attr不是obj的属性,如果提供了默认值,则返回默认值,不然会引发一个AttributeError异常 |
setattr(obj, attr, val) |
设置obj的attr属性值为val,替换任何已存在的属性值,不然就创建属性,类似于obj,attr=val |
delattr(obj, attr) |
从obj中删除属性attr(以字符串给出)类似于del obj.attr, |
dir(obj=None) |
返回obj的属性的一个列表,如果没有给定obj,dir()则显示局部名称空间中的属性,也就是locals().keys() |
super(type, obj=None) |
返回一个表示父类类型的代理对象,如果没有传入obj,则返回的super对象是非绑定的,反之,如果obj是一个type,issubclass(obj, type)必为True,否则isinstance(obj, type)必为True |
vars(obj=None) |
返回obj的属性及其值的一个字典,如果没有给出obj,vars()显示局部名字空间字典(属性及其值),也就是locals() |