面向对象编程 - QLGQ/learning-python GitHub Wiki
在Python中,类使用class关键字来创建。简单的类的声明可以是关键字后紧跟类名:
class ClassName(bases):
'class documentation string' # '类文档字符串'
class_suite # 类体
基类是一个或多个用于继承的父类的集合;类体由所有声明语句,类成员定义,数据属性和函数组成。
对于Python函数来说,声明与定义类没什么区别,因为它们是同时进行的,定义(类体)紧跟在声明(含class关键字的头行[header line]和可选(但总是推荐使用)的文档字符串后面。同时,所有的方法也必须同时 被定义。
什么是属性呢?属性就是属于另一个对象的数据或者函数元素,可以通过我们熟悉的句点属性标识法来访问。一些Python类型比如复数有数据属性(实部和虚部),而另外一些,像列表和字典,拥有方法(函数属性)。
Python中的所有方法都有一个限制:在调用前,需要创建一个实例。
数据属性仅仅是所定义的类的变量。它们可以像任何其他变量一样在类创建后被使用,并且,要么是由类中的方法来更新,要么是在主程序其他什么地方被更新。
这种属性已为OO程序员所熟悉,即静态变量
,或者静态数据
。它们表示这些数据是与它们所属的类对象绑定的,不依赖于任何类实例。如果你是一位Java或C++程序员,这种类型的数据相当于在一个变量声明前加上一个static关键字。
看下面的例子,使用类数据属性(foo):
class C(object):
foo = 100
print C.foo
100
C.foo = C.foo + 1
print C.foo
101
注意,上面的代码中,看不到任何类实例的引用。
-
方法
方法,仅仅是一个作为类定义一部分定义的函数(这使得方法成为类属性)。方法是通过句点属性标识法与它的实例绑定的。任何像函数一样对方法自身的调用都将失败。甚至由类对象调用方法也失败。class MyClass(object): def myNoActionMethod(self): pass mc = MyClass() mc.myNoActionMethod() myNoActionMethod() Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'myNoActionMethod' is not defined MyClass.myNoActionMethod() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unbound method myNoActionMethod() must be called with MyClass instance as first argument (got nothing instead)
-
绑定(绑定及非绑定方法)
为与OOP惯例保持一致,Python要求严格,没有实例,方法是不能被调用的。
这种限制即Python所描述的绑定概念(binding),在此,方法必须绑定(到一个实例)才能直接被调用。非绑定的方法可能可以被调用,但实例对象一定要明确给出,才能确保调用成功。然而,不管是否绑定,方法都是它所在的类的固有属性,即使它们几乎总是通过实例来调用的。
要知道一个类有哪些属性,有两种方法。最简单的是使用dir()内建函数。另外是通过访问类的字典属性__dict__,这是所有类都具备的特殊属性之一。 dir()返回的仅是对象的属性的一个名字列表,而__dict__返回的是一个字典,它的键(key)是属性名,键值(value)是相应的属性对象的数据值。
对于任何类C,下表显示了类C的所有特殊属性:
属性名 | 属性描述 |
---|---|
C.__name__ | 类C的名字(字符串) |
C.__doc__ | 类C的文档字符串 |
C.__bases__ | 类C的所有父类构成的元组 |
C.__dict__ | 类C的属性 |
C.__module__ | 类C定义所在的模块 |
C.__class__ | 实例C对应的类 |
Python中类的实例化,可以使用函数操作符,如下所示:
class MyClass(object): # 定义类
pass
mc = MyClass() # 初始化类
当类被调用,实例化的第一步是创建实例对象。一旦对象创建了,Python检查是否实现了__init__()方法。默认情况下,如果没有定义(或覆盖)特殊方法__init__(),对实例不会施加任何特别的操作。任何所需的特定操作,都需要程序员实现__init__(),覆盖它的默认行为。如果__init__()没有实现,则返回它的对象,实例化过程完毕。
然而,如果__init__()已经被实现,那么它将被调用,实例对象作为第一个参数(self)被传递进去,像标准方法调用一样。调用类时,传进去的任何参数都交给了__init__()。
实际中,你可以想象成这样:把创建实例的调用当成是对构造器的调用。
采用函数操作符调用类对象会创建一个类实例,也就是说这样一种调用过程返回的对象就是实例。如果定义了构造器,它不应当返回任何对象,因为实例对象是自动在实例化调用后返回的。相应地,__init__()就不应当返回任何对象(应当为None);否则,就可能出现冲突,因为只能返回实例。试着返回非None的任何其他对象都会导致TypeError异常。
class MyClass(object):
pass
mc = MyClass()
mc
<__main__.MyClass object at 0x7fba15177190>
type(mc)
<class '__main__.MyClass'>
class MyClass:
def __init__(self):
print 'initialized'
return 1
mc = MyClass()
initialized
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __init__() should return None
内建函数dir()可以显示类属性,同样还可以打印所有实例属性:
class C(object):
pass
c = C()
c.foo = 'roger'
c.bar = 'shrubber'
dir(c)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bar', 'foo']
与类相似,实例也有一个__dict__特殊属性,它是实例属性构成的一个字典:
c.__dict__
{'foo': 'roger', 'bar': 'shrubber'}
实例仅有两个特殊属性,对任意对象I:
特殊实例属性 | 属性描述 |
---|---|
I.__class__ | 实例化I的类 |
I.__dict__ | I的属性 |
内建类型也是类,对内建类型也可以使用dir(),与其他任何对象一样,可以得到一个包含它属性的名字的列表:
x = 3 + 0.14j
x.__class__
<type 'complex'>
dir(x)
['__abs__', '__add__', '__class__', '__coerce__', '__delattr__', '__div__', '__divmod__', '__doc__', '__eq__', '__float__', '__ffloordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__int__', '__le__', '__long__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__nonzero__', '__pos__', '__pow__', '__radd__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rmod__', '__rmul__', '__rpow__', '__rsub__', '__rtruediv__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', 'conjugate', 'imag', 'real']
[type(getattr(x, i)) for i in ('conjugate', 'imag', 'real')]
[<type 'builtin_function_or_method'>, <type 'float'>, <type 'float'>]
x.imag
0.14
x.real
3.0
x.conjugate()
(3-0.14j)
x.__dict__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'complex' object has no attribute '__dict__'
在内建类型中,不存在__dict__这个属性。
类属性仅是与类相关的数据值,和实例属性不同,类属性和实例无关。这些值像静态成员那样被引用,即使在多次实例化中调用类,它们的值都保持不变。
- 类和实例都是名字空间,类是类属性的名字空间,实例则是实例属性的。
可用类来访问类属性,如果实例没有同名的属性的话,也可以用实例来访问。
使用类属性来修改自身(不是实例属性)
class Foo(object):
x = 1.5
foo = Foo()
Foo.x
1.5
foo.x
1.5
foo.x += 0.2
foo.x
1.7
Foo.x
1.5
del foo.x
foo.x
1.5
从上述例子可以得知,类属性可以通过类或实例来访问。但是,只有当使用类引用version时,才能更新它的值。
如果尝试在实例中设定或更新类属性会创建一个实例属性,后者会阻止对类属性的访问。通过实例访问属性,首先会在实例中搜索属性,接着是类中,再就是继承树中的基类。
但是,在类属性可变的情况下,一切都不同了:
class Foo(object):
x = {2003: 'poe2'}
foo = Foo()
foo.x
{2003: 'poe2'}
foo.x[2004] = 'valid path'
foo.x
{2003: 'poe2', 2004: 'valid path'}
Foo.x
{2003: 'poe2', 2004: 'valid path'}
del foo.x # 没有遮蔽所以不能删除掉
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object attribute 'x' is read-only
- 方法仅仅是类内部定义的函数(这意味着方法是类属性而不是实例属性)。
- 方法只有在其所属的类拥有实例时,才能被调用。当存在一个实例时,方法才被认为是绑定到那个实例了。没有实例时方法就是未绑定的。
任何一个方法定义中的第一个参数都是变量self,它表示调用此方法的实例对象。
self变量用于在类实例方法中引用方法所绑定的实例。因为方法的实例在任何方法调用中总是作为第一个参数传递的,self被选中用来代表实例。你必须在方法声明中放上self,但可以在方法中不使用实例(self)。
方法,不管绑定与否,都是由相同的代码组成的。唯一的不同在于是否存在一个实例可以调用此方法。记得self在每个方法声明中都是作为第一个参数传入的。当你在实例中调用一个绑定的方法时,self不需要明确地传入了。当你还没有一个实例并且需要调用一个非绑定方法的时候你必须传递self参数。
调用非绑定方法并不经常用到。需要调用一个还没有任何实例的类中的方法的一个主要的场景是:你在派生一个子类,而且你要覆盖父类的方法,这时你需要调用那个父类中想要覆盖掉的构造方法。
class EmplAddrBookEntry(AddrBookEntry):
'Employee Address Book Entry class'
def __init__(self, nm, ph, em):
AddrBookEntry.__init__(self, nm, ph)
self.empid = id
self.email = em
我们将在子类构造器中调用父类的构造器并且明确地传递(父类)构造器所需要的self参数(因为我们没有一个父类的实例)。子类中__init__()的第一行就是对父类__init__()的调用。我们通过父类名来调用它,并且传递给它self和其他所需要的参数。一旦调用返回,我们就能定义那些与父类不同的仅存在我们的(子)类中的(实例)定制。
静态方法仅是类中的函数(不需要实例)。事实上,在静态方法加入到Python之前,用户只能在全局名字空间中创建函数,作为这种特性的替代实现——有时在这样的函数中使用类对象来操作类(或者是类属性)。使用模块函数比使用静态类方法更加常见。
通常的方法需要一个实例(self)作为第一个参数,并且对于(绑定的)方法调用来说,self是自动传递给这个方法的。而对于类方法而言,需要类而不是实例作为第一个参数,它是由解释器传给方法。类不需要特别地命名,类似self,不过很多人使用cls作为变量名字。
类中创建静态方法和类方法的一些例子:
class TestStaticMethod:
def foo():
print 'calling static method foo()'
foo = staticmethod(foo)
class TestClassMethod:
def foo(cls):
print 'calling class method foo()'
print 'foo() is part of class: ', cls.__name__
foo = classmethod(foo)
tsm = TestStaticMethod()
TestStaticMethod.foo()
calling static method foo()
tsm.foo()
calling static method foo()
tcm = TestClassMethod()
TestClassMethod.foo()
calling class method foo()
foo() is part of class: TestClassMethod
tcm.foo()
calling class method foo()
foo() is part of class: TestClassMethod
对应的内建函数被转换成它们相应的类型,并且重新赋值给了相同的变量名。如果没有调用这两个函数,二者都会在Python编译器中产生错误,显示需要带self的常规方法声明。我们也可以通过类或者实例调用这些函数,这没什么不同。
通过函数修饰符,可以把一个函数应用到另一个函数对象上,而且新函数对象依然绑定在原来的变量。
class TestStaticMethod:
@staticmethod
def foo():
print 'calling static method foo()'
class TestClassMethod:
@classmethod
def foo(cls):
print 'calling class method foo()'
print 'foo() is part of class: ', cls.__name__
一个类被定义后,目标就是要把它当成一个模块使用,并把这些对象嵌入到你的代码中去,同其他数据类型及逻辑执行流混合使用。有两种方法可以在你的代码中利用类。
- 第一种就是组合(composition),就是让不同的类混合并加入到其他类中,来增加功能和代码重用性。你可以在一个大点的类中创建你自己的类的实例,实现一些其他属性和方法来增强原来的类对象。
- 另一种方法就是通过派生。
class NewAddrBookEntry(object): # 定义类
'new address book entry class'
def __init__(self, nm, ph): # 定义构造器
self.name = Name(nm) # 创建Name实例
self.phone = Phone(ph) # 创建Phone实例
print 'Created instance for: ', self.name
NewAddrBookEntry类由它自身和其他类组合而成。这就在一个类和其他组成类之间定义了一种“有一个”(has-a)的关系。比如,我们的NewAddrBookEntry类“有一个”Name类实例和一个Phone实例。
当类之间有显著的不同,并且(较小的类)是较大的类所需要的组件时,组合表现得很好,但当你设计“相同的类但有一些不同的功能”时,派生就是一个更加合理的选择了。
创建子类
创建子类的语法看起来与普通(新式)类没有区别,一个类名,后跟一个或多个需要从其中派生的父类:
class SubClassName(ParentClass1[, ParentClass2, ... ,ParentClassN]):
'optional class documentation string'
class_suite
如果你的类没有从任何祖先类派生,可以使用object作为父类的名字。那些没有父类的类,它们的__bases__属性为空。经典类的声明唯一不同之处在于其没有从祖先类派生——此时,没有圆括号
:
class ClassicClassWithoutSuperclasses:
pass
class Parent(object): # 定义父类
def parentMethod(self):
print 'calling parent method'
class Child(Parent): # 定义子类
def childMethod(self):
print 'calling child method'
p = Parent() # 父类的实例
p.parentMethod()
calling parent method
c = Child() # 子类的实例
c.childMethod() # 子类调用它的方法
calling child method
c.parentMethod() # 调用父类的方法
calling parent method
继承描述了基类的属性如何“遗传”给派生类。一个子类可以继承它的基类的任何属性,不管是数据属性还是方法。
class P(object): # 父类
'P class'
def __init__(self):
print 'created an instance of ', self.__class__.__name__
class C(P): # 子类
pass
p = P() # 父类实例
created an instance of P
p.__class__ # 显示p所属的类名
<class '__main__.P'>
P.__bases__ # 父类的父类
(<type 'object'>,)
P.__doc__ # 父类的文档字符串
'P class'
c = C() # 子类实例
created an instance of C
c.__class__ # 显示c所属的类名
<class '__main__.C'>
C.__bases__ # 子类的父类
(<class '__main__.P'>,)
C.__doc__ # 子类的文档字符串
C没有声明__init__()方法,然而在类C的实例c被创建时,还是会有输出信息。原因在于C继承了P的__init__()。__bases__元组列出了其父类P。需要注意的是文档字符串对类,函数/方法,还有模块来说都是唯一的,所以特殊属性__doc__不会从基类中继承过来。
class P(object):
def foo(self):
print 'Hi, I am P-foo()'
p = P()
p.foo()
Hi, I am P-foo()
class C(P):
def foo(self):
print 'Hi, I am C-foo()'
c = C()
C.foo()
Hi, I am C-foo()
尽管C继承了P的foo()方法,但因为C定义了它自己的foo()方法,所以P中的foo()方法被覆盖(Overrid)。覆盖方法的原因之一是,你的子类可能需要这个方法具有特定或不同的功能。
此时还是可以调用那个被覆盖的基类方法,但这时就需要你去调用一个未绑定的基类方法,明确给出子类的实例,如下所示:
P.foo(c)
Hi, I am P-foo()
注意,我们上面已经有了一个P的实例p,但上面的这个例子并没有用它。我们不需要P的实例调用P的方法,因为已经有一个P的子类的实例c可用。典型情况下,你不会以这种方式调用父类方法,你会在子类的重写方法里显示地调用基类方法。
class C(P):
def foo(self):
P.foo(self)
print 'Hi, I am C-foo()'
注意,在这个(未绑定)方法调用中我们显示地传递了self。一个更好的方法是使用super()内建方法:
class C(P):
def foo(self):
super(C, self).foo()
print 'Hi, I am C-foo()'
super()不能找到基类方法,而且还为我们传进self,这样我们就不需要做这些事了。现在我们只要调用子类的方法,它会帮你完成一切:
c = C()
c.foo()
Hi, I am P-foo()
Hi, I am C-foo()
类似于上面的覆盖非特殊方法,当从一个带构造器__init__()的类派生,如果你不去覆盖__init__(),它将会被继承并被自动调用。但如果你在子类中覆盖了__init__(),子类被实例化时,基类的__init__()就不会被自动调用。
class P(object):
def __init__(self):
print "calling P's constructor"
class C(P):
def __init__(self):
print "calling C's constructor"
c = C()
calling C's constructor
如果你还想调用基类的__init__(),你需要像上边我们刚说的那样,明确指出,使用一个子类的实例去调用基类(未绑定)方法。相应地更新类C,会出现下面预期的执行结果:
class C(P):
def __init__(self):
P.__init__(self)
print "calling C's constructor"
c = C()
calling P's constructor
calling C's constructor
Python使用基类名来调用类方法,对应在Java中,是用关键字super来实现的,这就是super()内建函数引入到Python中的原因,这样你就可以“依葫芦画瓢”了:
class C(P):
def __init__(self):
super(C, self).__init__()
print "calling C's constructor"
使用super()的漂亮之处在于,你不需要明确给出任何基类名字。“跑腿儿”的事,它帮你干了!使用super()的重点,是你不需要明确提供父类。
这意味着如果你改变了类继承关系,你只需要改一行代码(class语句本身),而不必在大量代码中去查找所有被修改的那个类的名字。
Python允许子类继承多个基类,这种特性就是通常所说的多重继承。
在Python2.2以前的版本中,算法非常简单:深度优先,从左至右进行搜索,取得在子类中使用的属性。
其他Python算法只是覆盖被找到的名字,多重继承则取找到的第一个名字。
由于类、类型和内建类型的子类,都经过全新改造,有了新的结构,这种算法不再可行。这样,一种新的MRO(Method Resolution Order)算法被开发出来。
class P1: # (object): # 父类1
def foo(self):
print 'called P1-foo()'
class P2: # (object): # 父类2
def foo(self):
print 'called P2-foo()'
def bar(self):
print 'called P2-foo()'
class C1(P1, P2): # 子类1,从P1,P2派生
pass
class C2(P1, P1): # 子类2,从P1,P2派生
def bar(self):
print 'called C2-bar()'
class GC(C1, C2): # 定义子孙类
pass # 从C1,C2派生
经典类使用的解释顺序为深度优先
,从左至右。
gc = GC()
gc.foo() # GC -->> C1 -->> P1
called P1-foo()
gc.bar() # GC -->> C1 -->> P1 -->> P2
called P2-bar()
当调用foo()时,它首先在当前类(GC)中查找。如果没找到,就向上查找最亲的父类C1,还是没找到,就继续沿树上访问到父类P1,foo()被找到。如果要调用C2的bar()方法,必须调用它的合法的全名,采用典型的非绑定方式去调用,并且提供一个合法的实例:
C2.bar(gc)
called C2-bar()
新式类采用一种广度优先
的方式,从左至右。
取消类P1和类P2声明中的对(object)的注释,重新执行。新式方法的查询有一些不同:
gc = GC()
gc.foo() # GC -->> C1 -->> C2 -->> P1
called P1-foo()
gc.bar() # GC -->> C1 -->> C2
called C2-bar()
与沿着继承树一步一步上溯不同,它首先查找同胞兄弟。新的解释方式更适合那种要求查找GC更亲近的bar()的方法。当然,如果你还需要调用上一级,使用非绑定的方式即可。
P2.bar(gc)
called P2-bar()
新式类也有一个__mro__属性,告诉你查找顺序是怎样的:
GC.__mro__
(<class '__main__.GC'>, <class '__main__.C1'>, <class '__main__.C2'>, <class '__main__.P1'>, <class '__main__.P2'>, <type 'object'>)
内建函数 | 描述 |
---|---|
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() |
*attr()系列函数可以在各种对象下工作,不限于类(class)和实例(instance)。当使用这些函数时,你传入你正在处理的对象作为第一个参数,但属性名,也就是这些函数的第二个参数,是这些属性的字符串名字
。换句话说,在操作obj.attr时,就相当于调用*attr(obj, 'attr')系列函数。
- dir()作用在实例上(经典类或新式类)时,显示实例变量,还有在实例所在的类及所有它的基类中定义的方法和类属性。
- dir()作用在类上(经典类或新式类)时,则显示类以及它的所有基类的__dict__中的内容,但它不会显示定义在元类的(metaclass)中的类属性。
- dir()作用在模块上时,则显示模块的__dict__的内容。
- dir()不带参数时,则显示调用者的局部变量。
这个函数的目的就是帮助程序员找出相应的父类,然后方便调用相关的属性。一般情况下,程序员可能仅仅采用非绑定式调用祖先类方法。使用super()可以简化搜索一个合适祖先的任务,并且在调用它时,替你传入实例或类型对象。
之前,我们描述了方法解释顺序(MRO),用于在祖先类中查找属性。对于每个定义的类,都有一个名为__mro__的属性,它是一个元组,按照他们被搜索时的顺序,列出了被搜索的类。
super()函数语法如下:
super(type[, obj])
给出type,super()“返回此type的父类”。如果你希望父类被绑定,你可以传入obj参数(obj必须是type类型的),否则父类不会被绑定。obj参数也可以是一个类型,但它应当是type的一个子类。通常,当给出obj时:
- 如果obj是一个实例,isinsatnce(obj, type)就必须返回True。
- 如果obj是一个类或类型,issubclass(obj, type)就必须返回True。
事实上,super()是一个工厂函数,它创造了一个super object,为一个给定的类使用__mro__去查找相应的父类。很明显,它从当前所找的类开始搜索MRO。
super()的主要用途,是来查找父类的属性,比如super(MyClass, self).__init__()。
vars()内建函数与dir()相似,只是给定的对象参数都必须有一个__dict__属性。vars()返回一个字典,它包含了对象存储于其__dict__中的属性(键)和值。如果提供的对象没有这样一个属性,则会引发一个TypeError异常。如果没有提供对象作为vars()的一个参数,它将显示一个包含本地名字空间的属性(键)及其值的字典,也就是,locals()。我们来看一个例子,使用类实例调用vars():
class C(object):
pass
c = C()
c.foo = 100
c.bar = 'Python'
c.__dict__
{'foo':100, 'bar': 'Python'}
vars(c)
{'foo':100, 'bar': 'Python'}
方法有两个重要方面:首先,方法必须在调用前被绑定(到它们相应类的某个实例中)
;其次,有两个特殊方法可以分别作为构造器和解构器的功能,分别名为__init__()和__del__()。
事实上,__init__()和__del__()只是可自定义特殊方法集中的一部分。它们中的一些有预定义的默认行为,而其他一些则没有,留到需要的时候去实现。这些特殊方法是Python中用来扩充类的强有力的方式。它们可以实现:
- 模拟标准类型
- 重载操作符
特殊方法允许通过重载标准操作符+,*,甚至包括分段下标及映射操作操作[]来模拟标准类型。如同其他很多保留标识符,这些方法都是以双下划线(__)开始及结尾的。下表列出了所有特殊方法及其描述。
用来定制类的特殊方法
特殊方法 | 描述 |
---|---|
基本定制型 | |
C.__init__(self[, arg1, ...]) | 构造器(带一些可选的参数) |
C.__new__(self[, arg1, ...]) | 构造器(带一些可选的参数),通常用在设置不变数据类型的子类 |
C.__del__(self) | 解构器 |
C.__str__(self) | 可打印的字符输出,内建str()及print语句 |
C.__repr__(self) | 运行时的字符串输出;内建repr()和''操作符 |
C.__unicode__(self) | Unicode字符串输出,内建unicode() |
C.__call__(self, *args) | 表示可调用的实例 |
C.__nonzero__(self) | 为object定义False值;内建bool()(从2.2开始) |
C.__len__(self) | “长度”(可用于类),内建len() |
对象(值)比较 | |
C.__cmp__(self, obj) | 对象比较,内建cmp() |
C.__lt__(self, obj) and | 小于/小于或等于;对应于<及<=操作符 |
C.__gt__(self, obj) and | 大于/大于或等于;对应>及>=操作符 |
C.__eq__(self, obj) and | 等于/不等于;对应于==及!=或<>操作符 |
属性 | |
C.__getattr__(self, attr) | 获取属性,内建getattr();仅当属性没有找到时调用 |
C.__setattr__(self, attr, val) | 设置属性 |
C.__delattr__(self, attr) | 删除属性 |
C.__getattribute__(self, attr) | 获取属性,内建getattr();总是被调用 |
C.__get__(self, attr) | (描述符)获取属性 |
C.__set__(self, attr, val) | (描述符)设置属性 |
C.__del__(self, attr) | (描述符)删除属性 |
定制类/模拟类型 | |
数值类型:二进制操作符 |
元类让你来定义某些类是如何被创建的,从根本上说,赋予你如何创建类的控制权(你甚至不用去想类实例层面的东西)。你可以把元类想成是一个类种类,或是一个类,它的实例是其他的类。实际上,当你创建一个新类时,你就是在使用默认的元类,它是一个类型对象(对传统的类来说,它们的元类是type.ClassType)。当某个类调用type()函数时,你就会看到它到底是谁的实例。
class C(object):
pass
class CC:
pass
type(C)
<type 'type'>
type(CC)
<type 'classobj'>
import types
type(CC) is type.ClassType
True
元类一般用于创建类。在执行类定义时,解释器必须要知道这个类的正确的元类。解释器会先寻找类属性__metaclass__,如果此属性存在,就将这个属性赋值给此类作为它的元类。如果此属性没有定义,它会向上查找父类中的__metaclass__。所有新风格的类如果没有任何父类,会从对象或类型中继承(type(object)当然是类型。如果还没有发现__metaclass__属性,解释器会检查名字为__metaclass__的全局变量;如果它存在,就使用它作为元类,否则,这个类就是一个传统类,并用type.ClassType作为此类的元类。
在执行类定义的时候,将检查此类正确的(一般是默认的)元类,元类(通常)传递三个参数(到构造器):类名、从基类继承数据的元组和(类的)属性字典。
静态方法、类方法、属性,甚至所有的函数都是描述符。函数是Python中常见的对象,有内置的函数、用户自定义的函数、类中定义的方法、静态方法、类方法,这些都是函数的例子,它们之间唯一的区别在于调用方式的不同。通常,函数是非绑定的;虽然静态方法是在类中被定义的,它也是非绑定的;但方法必须绑定到一个实例上;类方法必须绑定到一个类上。
模块 | 说明 |
---|---|
UserList | 提供一个列表对象的封装类 |
UserDict | 提供一个字典对象的封装类 |
UserString | 提供一个字符串对象的封装类,它又包括一个MutableString子类,如果有需要,可以提供有关功能 |
types | 定义所有Python对象的类型在标准Python解释器中的名字 |
operator | 标准操作符的函数接口 |