zoukankan      html  css  js  c++  java
  • 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']
    
    >>> print MyClass.__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

     

     

  • 相关阅读:
    android 带图片的文本框
    Android 调用相册 拍照 实现系统控件缩放 切割图片
    Android核心分析索引
    Layout 水平平分空间、垂直平分空间
    Android中ActivityManagerService与应用程序(客户端)通信模型分析
    Android采用Movie播放GIF动画
    根据银行卡卡号判断银行
    TextView 显示效果
    实现通讯录的弹窗效果
    微软 DLinq技术来临前的国内 .NET 的 ORM 发展之局势
  • 原文地址:https://www.cnblogs.com/gqtcgq/p/7247198.html
Copyright © 2011-2022 走看看