1:类通常在一个模块的顶层进行定义。对于Python来说,声明与定义类是同时进行的。
2:类属性仅与其类相绑定,类数据属性仅当需要有更加“静态”数据类型时才变得有用,这种属性是静态变量。它们表示这些数据是与它们所属的类绑定的,不依赖于任何类实例。类似于Java或C++中在一个变量声明前加上static 关键字。
3:方法,比如下面,类MyClass 中的myNoActionMethod 方法,仅仅是一个作为类定义一部分定义的函数。(这使得方法成为类属性)。这表示myNoActionMethod 仅应用在MyClass 类型的对象(实例)上。
classMyClass(object):defmyNoActionMethod(self):pass
>>> mc =MyClass()>>>mc.myNoActionMethod()
任何像函数一样对myNoActionMethod自身的调用都将失败:
>>>myNoActionMethod()
Traceback (innermost last):
File"",line 1, in?
myNoActionMethod() NameError:myNoActionMethod
引发了NameError 异常,因为在全局名字空间中,没有这样的函数存在。甚至由类调用此方法也失败了。
>>>MyClass.myNoActionMethod()
Traceback (innermost last):
File"", line 1, in?
MyClass.myNoActionMethod()
TypeError: unbound method must be called withclass instance 1st argument
这是因为,Python 严格要求,没有实例,方法是不能被调用的。这种限制即Python所描述的绑定概念(binding),在此,方法必须绑定(到一个实例)才能直接被调用。
然而,不管是否绑定,方法都是它所在的类的固有属性,即使它们几乎总是通过实例来调用的。
4:要知道一个类有哪些属性,有两种方法。最简单的是使用dir()内建函数。另外是通过访问类的字典属性__dict__,这是所有类都具备的特殊属性之一。例子:
classMyClass(object):'MyClass class definition'myVersion= '1.1'
defshowMyVersion(self):printMyClass.myVersion>>>dir(MyClass)
['__class__', '__delattr__','__dict__', '__doc__','__getattribute__', '__hash__', '__init__','__module__','__new__', '__reduce__', '__reduce_ex__','__repr__','__setattr__', '__str__', '__weakref__', 'myVersion','showMyVersion']
>>> print MyClass.__dict__{'showMyVersion':,'__dict__': ,'myVersion': '1.1','__weakref__': , '__doc__':'MyClassclass definition'}
注意,dir返回的结果,除了包含类自身的属性之外,还包括所有父类的属性。而__dict__则只包含类本身的属性。类的__dict__是只读的,不可更新的。dir的结果包含了__dict__。
dir()返回的仅是属性的一个名字列表,而__dict__返回的是一个字典,它的键(keys)是属性名,键值(values)是相应的属性对象的数据值。
内建的vars()函数接受类对象作为参数,返回类的__dict__属性的内容。
5:对任何类C,都具有特殊属性:
C.__name__
类C的名字(字符串)
C.__doc__
类C的文档字符串
C.__bases__
类C的所有父类构成的元组
C.__dict__
类C的属性
C.__module__
类C定义所在的模块(1.5 版本新增)
C.__class__
实例C对应的类(仅新式类中)
根据上面定义的类MyClass,有如下结果:
>>>MyClass.__name__
'MyClass'
>>> MyClass.__doc__
'MyClass class definition'
>>> MyClass.__bases__(,)>>> print MyClass.__dict__{'__doc__': None, 'myVersion': 1, 'showMyVersion': , '__module__': '__main__'}>>> MyClass.__module__
'__main__'
>>> MyClass.__class__
__name__是给定类的字符名字。一些内建的类型也有这个属性。可以使用类型对象的__name__属性来取得相应的字符串名。如下例示:
>>> stype = type('Whatis your quest?')>>>stype
>>> stype.__name__
'str'
>>>
>>> type(3.14159265)
>>>type(3.14159265).__name__
'float'
__doc__是类的文档字符串,与函数及模块的文档字符串相似,必须紧随头行(header line)后的字符串。文档字符串不能被派生类继承,也就是说派生类必须含有它们自己的文档字符串。
__bases__包含了一个由所有父类组成的元组。
__dict__属性包含一个字典,由类的属性组成。访问一个类属性的时候,Python 解释器将会搜索字典以得到需要的属性。如果在__dict__中没有找到,将会在基类的字典中进行搜索。对类的修改会仅影响到此类的字典;基类的__dict__属性不会被改动的。
Python支持模块间的类继承。1.5 版本中引入了__module__,这样类名就完全由模块名所限定。看一下下面的例子:
>>> classC(object):
...pass...>>>C
>>>C.__module__
'__main__'
类C 的全名是“__main__.C”,比如,source_module.class_name。如果类C 位于一个导入的模块中,如mymod,像下面的:
>>> from mymod importC>>>C
>>>C.__module__
'mymod'
最后,由于类型和类的统一性,当访问任何类的__class__属性时,将发现它就是一个类型对象的实例。对于经典类,这个属性并未定义。
>>> class test(object): pass...>>>test.__class__
>>>
>>> class test: pass...>>>test.__class__Traceback (most recentcall last):
File"", line 1, inAttributeError:class test has no attribute '__class__'
6:实例
类和类型在2.2 版本中就统一了,新式类如下:
>>> mc =MyClass()>>>type(mc)
>>>type(0)
MyClass 和 int,你将会发现二者都是类型(type):
>>>type(MyClass)
>>>type(int)
在Python2.2 版本之前,对于经典类,类是类类型,实例是实例类型。在这两个对象类型之间没有任何关系,除了实例的__class__属性引用了该类。经典类如下:
>>>type(mc)
>>>type(0)
>>>type(MyClass)
>>>type(int)
7:当类被调用,实例化的第一步是创建实例对象。一旦对象创建了,Python 检查是否实现了__init__()方法。默认情况下,如果没有定义(或覆盖)特殊方法__init__(),对实例不会施加任何特别的操作,实例化过程完毕。
如果__init__()已经被实现,那么它将被调用,实例对象作为第一个参数(self)被传递进去,像标准方法调用一样。__init__()是在解释器为你创建一个实例后调用的第一个方法,在你开始使用它之前,这一步可以让你做些准备工作。
如果定义了__init__,它不应当返回任何对象,否则,就可能出现冲突,试着返回非None 的任何其它对象都会导致TypeError 异常:
>>> classMyClass:
...def __init__(self):
...print 'initialized'...return 1...>>> mc =MyClass()
initialized
Traceback (innermost last):File"", line 1, in?
mc=MyClass()
TypeError:__init__() should return None
__new__()“构造器”方法
与__init__()相比,__new__()方法更像一个真正的构造器。它是一个类方法,并且传入的参数是在类实例化操作时生成的。__new__()会调用父类的__new__()来创建对象(向上代理)。
__new__()必须返回一个合法的实例,__new__()和__init__()在类创建时,都传入了(相同)参数。它们的参数必须一致,否则会出错。
__del__()"解构器"方法
有一个相应的特殊解构器(destructor)方法名为__del__()。这个函数要直到该实例对象所有的引用都被清除掉后才会执行。它通常没有被实现,因为实例很少被显式释放。举例:
classC(P):def __init__(self):print 'initialized'
def __del__(self):
P.__del__(self)>>> c1 =C()>>> c2 =c1>>> c3 =c1>>>id(c1), id(c2), id(c3)
(11938912, 11938912, 11938912)>>> delc1>>> delc2>>> delc3
deleted
在上面的例子中,解构器是在类C 实例所有的引用都被清除掉后,才被调用的。一旦引用计数为0,则对象就被清除了。
总结:
不要忘记首先调用父类的__del__()。
调用 del x 不表示调用了x.__del__() -----它仅仅是减少x 的引用计数。
__del__()未捕获的异常会被忽略掉(因为一些在__del__()用到的变量或许已经被删除了)。
不要在__del__()中干与实例没任何关系的事情。
除非你知道你正在干什么,否则不要去实现__del__()。
8:实例属性
实例仅拥有数据属性,方法是类属性,实例的数据属性只是与某个类的实例相关联的数据值,这些值独立于其它实例或类。当一个实例被释放后,它的属性同时也被清除了。
设置实例的属性可以在实例创建后任意时间进行,也可以在能够访问实例的代码中进行。可以能够在“运行时”创建实例属性,是Python 类的优秀特性之一。但是这样创建属性时,必须谨慎。
内建函数dir()同样还可以打印所有实例属性:
>>> classC(object):
...pass
>>> c =C()>>> c.foo = 'roger'
>>> c.bar = 'shrubber'
>>>dir(c)
['__class__', '__delattr__','__dict__', '__doc__', '__getattribute__', '__hash__', '__init__','__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__','__str__', '__weakref__', 'bar', 'foo']
实例也有一个__dict__特殊属性(可以调用vars()并传入一个实例来获取),它是实例属性构成的一个字典:
>>>c.__dict__{'foo': 'roger', 'bar': 'shrubber'}
dir(实例)也包含类的属性。实例的__dict__只有实例属性,它是可写的。没有类属性或特殊属性。
实例仅有两个特殊属性,对于任意对象I:
I.__class__
实例化I 的类
I.__dict__
I 的属性
现在使用类C 及其实例C 来看看这些特殊实例属性:
>>> classC(object):
...pass...>>> c =C()>>>dir(c)
['__class__', '__delattr__','__dict__', '__doc__', '__format__', '__getattribute__', '__hash__','__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__','__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']>>> c.__dict__{}>>> c.__class__
可以看到,c 现在还没有数据属性,但我们可以添加一些再来检查__dict__属性,看是否添加成功了:
>>> c.foo = 1
>>> c.bar = 'SPAM'
>>> '%d can of %splease' %(c.foo, c.bar)'1 can of SPAM please'
>>>c.__dict__{'foo': 1, 'bar':'SPAM'}
对类和实例来说,尽管__dict__属性是可修改的,但还是建议你不要修改这些字典,使用熟悉的句点属性标识来访问及操作属性会更易于接受。
9:内建类型属性
内建类型也是类,对内建类型也可以使用dir(),与任何其它对象一样,可以得到一个包含它属性名字的列表:
>>> x = 3+0.14j
>>> x.__class__
>>>dir(x)
['__abs__', '__add__','__class__', '__coerce__','__delattr__', '__div__', '__divmod__', '__doc__','__eq__','__float__', '__floordiv__', '__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__', '__str__', '__sub__','__truediv__', 'conjugate', 'imag', 'real']>>>x.imag2.0
>>>x.real1.0
>>>x.conjugate()
(1-2j)
试着访问__dict__会失败,因为在内建类型中,不存在这个属性:
>>> x.__dict__Traceback (innermost last):File"", line 1, in?
AttributeError:__dict__
10:实例属性 vs 类属性
类属性和实例无关。这些值像静态成员那样被引用,即使在多次实例化中调用类,它们的值都保持不变。不管如何,静态成员不会因为实例而改变它们的值,除非实例中显式改变它们的值。
类和实例都是名字空间。类是类属性的名字空间,实例则是实例属性的。
可采用类来访问类属性,如果实例没有同名的属性的话,你也可以用实例来访问。
类属性可通过类或实例来访问。下面的示例中,类C 在创建时,带一个version 属性,这样通过类对象来访问它是很自然的了,比如,C.version。当实例c 被创建后,对实例c 而言,访问c.version 时,Python 首先会在实例中搜索名字version,然后是类,再就是继承树中的基类。本例中,version 在类中被找到了:
>>> classC(object):
... version= 1.2...>>> c =C()>>>C.version1.2
>>>c.version1.2
>>>C.version += 0.1
>>>C.version1.3
>>>c.version1.3
只有当使用类引用version 时,才能更新它的值,像上面的C.version 递增语句。如果尝试在实例中设定或更新类属性会创建一个实例属性c.version,后者会阻止对类属性C.versioin 的访问,因为第一个访问的就是c.version,这样可以对实例有效地“遮蔽”类属性C.version,直到c.version 被清除掉。
从实例中访问类属性须谨慎
与通常Python 变量一样,任何对实例属性的赋值都会创建一个实例属性(如果不存在的话)并且对其赋值。如果类属性中存在同名的属性,有趣的副作用即产生。(经典类和新式类都存在)
>>> classFoo(object):
... x= 1.5...>>> foo =Foo()>>>foo.x1.5
>>> foo.x =1.7
>>>foo.x1.7
>>>Foo.x1.5
>>> delfoo.x>>>foo.x1.5
所以,给一个与类属性同名的实例属性赋值,我们会有效地“隐藏”类属性,但一旦我们删除了这个实例属性,类属性又重见天日。
现在再来试着更新类属性,但这次,尝试一下增量动作:
>>> foo.x += 2
>>>foo.x1.7
>>>Foo.x1.5
同样创建了一个新的实例属性,类属性原封不动。(深入理解Python 相关知识:属性已存于类字典[__dict__]中。通过赋值,其被加入到实例的__dict__中了。)赋值语句右边的表达式计算出原类的变量,增加0.2,并且把这个值赋给新创建的实例属性。
但在类属性可变的情况下,一切都不同了,注意:使用实例属性来试着修改类属性是很危险的:
>>> classFoo(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'}>>> delfoo.x
Traceback (most recent calllast): File"", line 1, in?delfoo.x
AttributeError: x>>>
当一个实例在类属性被修改后才创建,那么更新的值就将生效。类属性的修改会影响到所有的实例:
>>> classC(object):
... spam= 100...>>> c1 =C()>>>c1.spam100
>>> C.spam += 100
>>>C.spam200
>>>c1.spam200
>>> c2 =C()>>>c2.spam200
>>>delc1>>> C.spam += 200
>>>c2.spam400
访问类属性或者实例属性的时候,都需要加上名称空间,比如:
classtest(object):
name=‘test’def __init__(self):
self.name=‘instance’deffoo(self):
name=‘foo’print ‘name is’, nameprint ‘1class name is’, test.nameprint ‘2class name is ’, self.__class__.nameprint ‘3class name is’, type(self).nameprint ‘self name is’, self.name
testobj=test()
testobj.foo()
结果是:
name isfoo
1class name istest
2class name istest
3class name istest
self nameis instance