2015/9/20 Python基础(16):类和实例

面向对象编程
编程的发展已经从简单控制流中按步的指令序列进入到更有组织的方式中,依靠代码块可以形成命名子程序和完成既定的功能。结构化的或过程性编程可以让我们把程序组织成逻辑快,以便重复或重用。创造程序的过程变得更具逻辑性;选出的行为要符合规范,才可以约束创建的数据。迪特尔父子认为结构化编程是“面向行为”的,因为事实上,即使没有任何行为的数据也必须“规定”逻辑性。
然而,如果我们能对数据加上动作呢?如果我们所创建和编写的数据片段,是真实生活中实体的模型,内嵌数据体和动作呢?我们通过一系列已定义的接口(又称存取函数集合)访问数据属性,我们就有一个“对象”系统,从大的方面来看,每一个对象既可以与自身进行交互,也可以与其他对象进行交互。
面向对象编程踩上了进化的步伐,增强了结构化编程,实现了数据与动作的融合:数据层和逻辑层由一个可用以创建这些对象的简单抽象层来描述。

面向对象设计(OOD)与面向对象编程(OOP)
OOD不会特别要求支持OO的语言,事实上,OOD可以由纯结构化语言来实现,比如C,但如果想构造具备对象性质和特点的数据类型,就需要在程序上多做努力,如果一门语言内建OO特性,那么OOP开发就会更加方便高效。
另一方面,一门面向对象的语言不一定会强制写OO方面的程序。例如CPP被认为更好的C。

现实世界中的问题
用OOD的思维来抽象问题,是因为它直接提供建模和解决世界问题及情形的途径。我们按照现实生活中物体或者事件的行为来定义类和实例的行为和属性。

常用术语

  • 抽象/实现

抽象是指对现实世界问题和实体的本质表现、行为和特征建模,建立一个相关的自己,可以用于描述程序结构,从而实现这种模型。
对某种抽象的实现就是对此数据及与之相关接口的现实化。

  • 封装/接口

封装描述了对数据信息进行隐藏的观念,它对数据属性提供接口和访问函数。客户端不需要知道封装后数据属性是如何组织的,只对数据提供相应接口一面客户程序通过不规范的操作来存取封装的数据属性。

  • 合成

合成描述了一个异常复杂的系统,比如一个类由其他类组成,更小的组件也可能是其他的类,数据属性及行为,所有这些合在一起,称之为合成。

  • 派生/继承/继承结构

派生描述了子类的创建,新类保留已存类类型中所余姚的数据和行为,但允许修改或者其他的自定义操作,都不会修改原类的定义。
继承描述了子类属性从祖先类继承这样一种方式。
继承结构表示多“代”派生,可以描述成一个“族谱”。

  • 泛化/特化

泛化便是所有子类与其父类及祖先类有一样的特点,所以子类可以认为同祖先类“是一个”(is-a)的关系,因为一个派生对象(实例)是祖先类的一个“例子”。
特化描述所有子类的自定义,也就是,一些属性让它与其祖先类不同。

  • 多态

多态的概念指出了对象如何通过他们共同的属性和动作来操作及访问,而不需要考虑他们具体的类。多态表明了动态(后来又称运行时)绑定的存在,允许重载及运行时类型确定和验证。

  • 自省/反射

自省表示给予你某种能力来进行像“手工类型检查”的工作,它也被称为反射。


类是一种数据结构,可以用它来定义对象,后者把数据值和行为特性融合在一起。
Python中,类声明与函数声明很相似:

class ClassName(object):
  ‘class documentation string‘
  class_suite

类像一个Python的容器类型。尽管类本身是一个对象,但正被定义的时候,它们还不是对象的实现。类的实例才是对象的实现。
声明与定义类没有什么区别,他们是同时进行的,定义紧跟在声明和可选的文档字符串后面。同时,所有的方法也IXUS同时被定义。

  • 类属性

属性就是属于另外一个对象的数据或者函数元素,可以通过我们熟悉的句点属性标识符来访问。一些Python类型有数据属性,一些有函数属性(方法)。
有些属性本身也是对象,拥有自己的属性,可以访问,导致一个属性连。
类属性仅与其被定义的类相绑定,并且因为实例对象在日常OOP中用得最多,实例数据属性是会一直用到的主要数据属性。类的数据属性仅当需要有更加“静态”数据类型时才变得有用。

类的数据属性
数据属性是所定义的类的变量。它们可以像任何其他变量一样在类创建后被使用,并且,要么是由类中的方法来更新,要么是在主程序其他地方更新。
类的数据属性是一种静态数据属性。在介绍实例属性时,会详细对比这些。

  • 方法
>>> class MyClass(object):
  def myMethod(self):
    pass

>>> mc = MyClass()
>>> mc.myMethod()

上述代码中,myMethod就是MyClass的方法。通过句点属性标识符与它的实例绑定。
如果直接调用:

>>> myMethod()

Traceback (most recent call last):
File "<pyshell#6>", line 1, in <module>
myMethod()
NameError: name ‘myMethod‘ is not defined

引发了NameError,因为在全局名字中,没有这样的函数存在。

如果是由类对象调用:

>>> MyClass.myMethod()

Traceback (most recent call last):
File "<pyshell#7>", line 1, in <module>
MyClass.myMethod()
TypeError: unbound method myMethod() must be called with MyClass instance as first argument (got nothing instead)

会引发TypeError异常。

  • 绑定(绑定及非绑定方法)

为与OOP惯例保持一致,Python严格要求,没有实例,方法是不能被调用的。这种限制即Python所描述的绑定概念(binding),方法必须绑定(到一个实例)才能直接被调用。非绑定的方法可能可以被调用,但实例对象一定要明确给出,才能确保调用成功。

  • 决定类的属性

要知道一个类有哪些属性,有两种方法,一种是使用dir()内建函数,另外是访问类的字典属性__dict__,这是所有类都有的特殊属性之一。

>>> class MyClass(object):
‘MyClass class definition‘
myVersion = ‘1.1‘
def showMyVersion(self):
print MyClass.myVersion

>>> dir(MyClass)
[‘__class__‘, ‘__delattr__‘, ‘__dict__‘, ‘__doc__‘, ‘__format__‘, ‘__getattribute__‘, ‘__hash__‘, ‘__init__‘, ‘__module__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘__weakref__‘, ‘myVersion‘, ‘showMyVersion‘]
>>> MyClass.__dict__
dict_proxy({‘__module__‘: ‘__main__‘, ‘showMyVersion‘: <function showMyVersion at 0x02ABAEF0>, ‘__dict__‘: <attribute ‘__dict__‘ of ‘MyClass‘ objects>, ‘myVersion‘: ‘1.1‘, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘MyClass‘ objects>, ‘__doc__‘: ‘MyClass class definition‘})

从上面可以看到,dir()放回的仅是对象的属性的一个名字列表,而__dict__返回的一个字典,key是属性名,value是属性对象的数据值。

  • 特殊的类的属性
C.__name__  类C的名字(字符串)
C.__doc__ 类C的文档字符串
C.__bases__ 类C的所有父类构成的元组
C.__dict__  类C的属性
C.__module__ 类C定义所在的模块(1.5 版本新增)
C.__subclasshook__ 实例C对应的类(仅新式类中)

实例

如果说类是一种数据结构定义类型,那么实例则声明了一个这种类型的变量。
也就是说实例时有声明的类。

  • 初始化

通过调用类对象来创建实例
很多OO语言都提供new关键字,通过new可以创建类的实例。Python的方法更简单,一旦定义一个类,通过使用函数操作符就可以实例化类:

>>> class MyClass(object):

>>> mc = MyClass()

当我"call"一个类的时候,解释器就会实例化该对象,并且调用Python所拥有与构造函数最相近的东西来执行最终的定制工作,最后将这个实例返回给你。

  • __init__()“构造器”方法

当类被调用,实例化的第一步是创建实例对象。创建实例对象是调用__init__()方法。默认情况下,如果没有定义(或覆盖)特殊方法__init__(),对实例不会施加任何特别的操作。

  • __new__()构造器方法

与__init__()相比,__new__()方法更像一个真正的构造器。类型和类在Python2.2就统一了,所以需要一种途径来实例化不可变对象,比如派生字符串、数字等。
在这种情况下,解释器则调用类的__new__()方法,一个静态方法,并且传入的参数是在类实例化操作时生成的。__new__()会调用父类的__new__()来创建对象。
为什么__new__()比__init__()更像构造器呢?这是因为__new__()必须返回一个合法的实例,这样解释器调用__init__()时,就可以把这个实例作为self传给它。调用父类的__new__()来创建对象,正像其它语言中使用new关键字一样。

  • __del__()解构器方法

有一个相应的书结构器方法名为__del__()。然而,由于Python具有垃圾对象回收机制,这个函数要知道该实例对象所有的引用都被清除掉后才会执行。Python中的解构器是在实例释放前提供特殊处理功能的方法,通常没有被实现,因为实例很少显式释放。

实例属性

  • 实例化实例属性

设置实例的属性可以在实例创建后任意时间进行,也能在访问实例的代码中进行。
能够在运行时创建实例属性,是Python类的优秀特性之一,Python不仅是动态类型,而且在运行时,允许这些对象属性的动态创建。当然这样的属性必须谨慎。
一个缺陷是,属性在条件语句中创建,如果该条件语句块未被执行,属性也不存在。

  • 查看实例属性

用内建函数dir()可以显示类属性,同时用__dict__属性也可以查看实例属性的字典。

  • 内建类型属性

内建类型也是类,所以对内建类型也可以使用dir(),与其他任何类型对象一样,可以得到一个包含它属性名字的表:

>>> x = 3+0.14j
>>> x.__class__
<type ‘complex‘>
>>> dir(x)
[‘__abs__‘, ‘__add__‘, ‘__class__‘, ‘__coerce__‘, ‘__delattr__‘, ‘__div__‘, ‘__divmod__‘, ‘__doc__‘, ‘__eq__‘, ‘__float__‘, ‘__floordiv__‘, ‘__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‘]

我们知道x是复数属性后,可以访问它的数据属性

>>> x.imag
0.14
>>> x.real
3.0
>>> x.conjugate()
(3-0.14j)
>>> x.__dict__

Traceback (most recent call last):
File "<pyshell#23>", line 1, in <module>
x.__dict__
AttributeError: ‘complex‘ object has no attribute ‘__dict__‘

但是访问__dict__会失败,因为内建类型中不存在这个属性。

实例属性 VS 类属性

类属性仅是与类相关的数据值,和实例属性不同,类属性和实例无关。这些值像静态成员那样被引用,即使再多次实例化中调用类,他们的值都保持不变。除非实例中显式改变它们的值,否则不会因为实例而改变它们的值。
类和实例都是名字空间,类是类属性的名字空间,实例则是实例属性的。

1.访问类属性
类属性可通过类或实例访问。

>>> class Class(object):
version = 1.0

>>> c = Class() # 实例化
>>> Class.version # 通过类来访问
1.0
>>> c.version # 通过实例来访问
1.0
>>> c.version += 0.1 # 通过实例来更新
>>> c.version # 实例访问已经更新
1.1
>>> Class.version # 并没有改变类访问的值
1.0
>>> Class.version += 0.5 #此时更新类访问的值
>>> Class.version # 类访问
1.5
>>> c.version # 实例访问也没有改变
1.1
>>> d = Class() # 实例化
>>> d.version # 实例访问
1.5
>>> Class.version += 0.5 # 类更新
>>> Class.version # 类访问已更新
2.0
>>> d.version # 实例访问已更新
2.0

2.从实例中访问类属性须谨慎
与通常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

很神奇吧
所以,给一个类属性同名的实例属性赋值,我们会有效地“隐藏”类属性,但一旦我们删除了实例属性,类属性又重建天日。
接下来我们通过另一个例子探讨另一个有趣的现象:

>>> class Foo(object):
x = {2003: ‘poe‘}

>>> foo = Foo()
>>> foo.x
{2003: ‘poe‘}
>>> foo.x[2004] = ‘valid path‘
>>> foo.x
{2003: ‘poe‘, 2004: ‘valid path‘}
>>> Foo.x
{2003: ‘poe‘, 2004: ‘valid path‘}
>>> del foo.x

Traceback (most recent call last):
File "<pyshell#8>", line 1, in <module>
del foo.x
AttributeError: ‘Foo‘ object attribute ‘x‘ is read-only

我们知道,假如类属性是不可变对象,我们对实例属性修改是不会改变类属性的值的,但是,当类属性是可变对象时,比如说字典。我们修改类属性就等同于修改了实例属性,因为我们没有改变它引用的形式。所以实例属性在修改后没有“遮盖”类属性,也就不能被删除。

3.类属性持久性
静态成员,顾名思义,任凭整个实例如何进展,它都独立于实例。同时,当一个实例在类属性被修改后才创建,那么更新的值就将生效。类属性的修改会影响所有的实例,而实例的修改不影响类。
这一点,之前的例子里已经有提到,不予赘述。

时间: 2024-09-28 02:52:40

2015/9/20 Python基础(16):类和实例的相关文章

附录A培训实习生-面向对象基础(1):类和实例

对象是一个自包含的实体,用一组可识别的特性和行为来标识. 面向对象编程,Object-Oriented Programming,其实就是针对对象进行编程的意思. 类就是具有相同属性和功能的对象的抽象的集合. 在编程过程中注意: 第一,类名称首字母记着要大写.多个单词则各个首字母大写. 第二,对外公开的方法需要用public修饰符. 实例,就是一个真实的对象. 实例化就是创建对象的过程,使用new关键字来创建. 下面是一个例子: 这是一个类, 1 using System; 2 using Sys

2015/9/17 Python基础(13):函数

函数是对程序逻辑进行结构化或过程化的一种编程方法. Python的函数返回值当什么也不返回时,返回了None和大多数语言一样,Python返回一个值或对象.只是在返回容器对象时,看起来像返回多个对象.这样在操作的时候显得很灵活,虽然它本质上只是反悔了一个对象. 调用函数我们用一对圆括号电泳函数.任何输入的参数都应该放在括号中. 关键字参数这个概念是针对函数调用的,比如我们有这样的函数 def fun(value, count): fun_suite 我们可以标准调用: fun(12,20) 也可

2015/10/9 Python基础(21):可调用和可执行对象

在Python中有多种运行外部程序的方法,比如,运行操作系统命令或另外的Python脚本,或执行一个磁盘上的文件,或通过网络来运行文件.这完全取决于想要干什么.特定的环境包括: 在当前脚本继续运行 创建和管理子进程 执行外部命令或程序 执行需要输入的命令 通过网络来调用命令 执行命令来创建需要处理的输出 执行其他的Python脚本 执行一系列动态生成的Python语句 导入Python模块 Python中,内建和外部模块都可以提供上述各种功能.程序员得根据实现的需要,从这些模块中选择合适的处理方

2015/9/21 Python基础(17):绑定和方法调用

绑定和方法调用现在我们需要再次阐述Python中绑定(binding)的概念,它主要与方法调用相关联.方法是类内部定义的函数,这意味着方法是类属性而不是实例属性.其次,方法只有在其所属的类拥有实例时,才能被调用.当存在一个实例时,方法才被认为是绑定到那个实例了,没有实例时,方法就是未绑定的.任何一个方法定义中都有一个参数是变量self.它表示调用此方法的实例对象. 核心笔记:self变量用于在类实例方法中引用方法所绑定的实例.方法的实例在任何方法调用中总是作为第一参数传递的,self代表方法的实

2015/9/22 Python基础(18):组合、派生和继承

一个类被定义后,目标就是把它当成一个模块来使用,并把这些对象嵌入到你的代码中去,同其他数据类型及逻辑执行流混合使用.有两种方法可以在你的代码中利用类.第一种是组合,就是让不同的类混合并加入到其他类中,来增强功能和代码重用性.你可以在一个大点的类中创建你自己的类的实例,实现一些其他属性和方法来增强原来的类对象.另一种是派生,通过子类从基类继承核心属性,不断地派生扩展功能实现. 组合举例来说,我们想对之前做过的地址本类作加强性设计.如果在设计的过程中,为names.addresses等创建了单独的类

2015/8/31 Python基础(5):字符串

字符串是Python最常见的一种类型.通过在引号间包含字符的方式创建它.Python里单双引号的作用是一致的.Python的对象类型里不存在字符型,一般用单个字符的字符串来使用.Python的字符串是一种直接量或者说标量,Python解释器在处理字符串时把它作为单一值并且不会包含其他Python类型的.Python的字符串也是不可改变类型.字符串里的字符可以通过切片操作访问.Python有3类字符串,通常意义字符串(str),Unicode字符串(unicode)和抽象类字符串(basestri

python :理解类与实例

面向对象最重要的概念就是类(Class)和实例(Instance),必须牢记类是抽象的模板,比如Student类,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同. 仍以Student类为例,在Python中,定义类是通过class关键字: class Student(object): pass class后面紧接着是类名,即Student,类名通常是大写开头的单词,紧接着是(object),表示该类是从哪个类继承下来的,继承的概念我们后面再讲,通常,

Python 面向对象编程——类和实例

1        面向对象编程 面向对象编程: 面向对象编程--Object OrientedProgramming OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数. 面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行.为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度. 面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消

Python学习_11_类和实例

类和实例 类是对象创建实例的模板,而实例则是对象的实体.类使用class关键字定义: class MyClass:? ? pass python中创建实例直接使用工厂函数(类名加上一对括号),和其他的语言使用new关键字有所不同: my_obj = MyClass() 一般来说,类名以大写字母开头,而对象名则以小写字母或者下划线开头. 实例化对象时,会执行类定义中的__init__()方法,该方法执行时包含实例的各种初始化操作. 方法和函数的区别:方法由对象调用,在方法定义中,第一个参数必须是显