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