Python基础:17类和实例之一

         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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值