每天一点python——类

来源:互联网 发布:2017淘宝发展趋势 编辑:程序博客网 时间:2024/06/08 12:07

转自:http://blog.csdn.net/on_1y/article/details/8640012

1 名字和对象

对象有其特性,同一个对象可以有多个名字,这与其它语言中的别名很相似。别名有时候像指针,例如将对象当做 函数参数传递的时候非常高效,因为只传递了指针,这避免了pascal中的两套参数传递机制。

2 Python的域(scopes)和名称空间(namespaces)

在引入类之前,我们讲Python的域规则。类的定义巧妙地运用了名称空间,所以你需要知道域和名称空间如何工作才能理解发生了什么。

 首先从定义开始。 名称空间是名字和对象之间的映射。多数名称空间使用Python的字典来实现,但除非出于性能考虑,我们通常不关心具体如何实现。名称空间的例子有,内置的名称例如abs(),内置的异常名,模块的全局名称,函数调用时的局部名称。在某种程度上,对象的属性也构成名称空间。

关于名称空间最重要的一点是:不同名称空间中的名称没有关系。例如 两个不同模块中都可以包含名为maximize的函数,这不会造成混肴,因为使用这些模块时必须加上模块名作为前缀。 另外,我把任何点后的名称叫做属性。例如,在表达式z.real中,real是对象z的属性。严格来说,引用模块中的名称是对属性的引用,在表达式modname.funcname中,modname是一个模块,funcname是它的一个属性。这个例子中模块属性和模块 内定义的全局名称有着直接的映射,它们有着相同的名称空间。 属性可能是只读的或者可写的,上面的例子中,属性就是可写的,例如:modname.the_ answer = 42.可写的属性可以被删除, 例如 del modname.the_ answer 会删除模块 modname中的 the_ answer属性。 

名称空间在不同的时刻创建,有着不同的生命周期。包含内置名称的名称空间在Python解释器启动时被创建,且不会被删除。 模块的全局名称空间在模块被导入时被创建,正常情况下,模块的名称空间会持续到解释器退出。来自脚本文件或者交互式环境 被解释器最顶层调用执行的语句,被认为是 __ main __ 模块的一部分,所以他们有着自己的全局名称空间。 函数的局部名称空间当函数被调用时被创建,函数返回时或者出现异常而函数又没有提供处理方式时被删除。当然,在递归调用 中每一次调用都有他们自己的局部名称空间。 域(scpoe)是Python程序的一个名称空间可以直接访问的一个文本范围,“直接访问”在这里的意思时当对一个名字的访问没有 前缀时,会尝试在名称空间内查找这个名字。 在执行的任意时刻,至少有三个嵌套域,它们有名称空间可以直接访问。

  • 最内层的域,它会首先被搜索,包含局部名称
  • 任何封装函数的域,从最近的封装域开始搜索,包含非局部,非全局的名称
  • 倒数第二个域,包含当前模块的全局名称
  • 最外层的域,最后被搜索,包含内置名字的名称空间

如果一个名字被声名为全局的,那么所有的引用和赋值都是针对中间层的域,这一层域包含模块的全局名称。 意识到域是由文本决定是非常重要的,定义在模块中的一个函数的全局域就是这个模块的名称空间,无论这个函数在哪儿, 通过哪个别名被调用。

3 初识类

3.1 定义类

<span style="color: rgb(255, 182, 193); font-size: 15px;">class</span> <span style="color: rgb(255, 255, 0); font-size: 15px;">ClassName</span>:    <statement-1>    .    .    .    <statement-N>

类定义,像函数定义一样,在执行时才会起作用。你可以把类定义放在任何地方比如if语句的分支,或者在函数内部。 在实际应用时,定义在类中的语句通常都是函数定义,但是其它语句也是允许出现的,并且有的时候非常有用。 当进入一个类定义时,一个新的名称空间被创建,并且被当作局部域来使用。

3.2 类对象

类对象提供两种操作,属性引用和实例化。 属性引用使用标准句法:obj.name. 有效的属性名是类对象创建时类的名称空间内的所有名字。 例如下面的类定义中,MyClass.i和MyClass.f都是有效的属性名。

>>> <span style="color: rgb(255, 182, 193); font-size: 15px;">class</span> <span style="color: rgb(255, 255, 0); font-size: 15px;">MyClass</span>:...     <span style="color: rgb(255, 160, 122);">"""A simple example class"""</span>...     i = 123...     <span style="color: rgb(255, 182, 193); font-size: 15px;">def</span> <span style="color: rgb(30, 144, 255); font-size: 15px;"><strong>f</strong></span>(<span style="color: rgb(255, 182, 193); font-size: 15px;">self</span>):...         <span style="color: rgb(255, 182, 193); font-size: 15px;">return</span> <span style="color: rgb(255, 160, 122);">'hello world'</span>... >>> MyClass.i123>>> MyClass.i = 10

类的实例化使用函数记号,例如:

>>> x = MyClass()>>> x.i10

这个实例化操作创建了一个空对象,许多类在实例化时定义了一些初始化操作。例如:

>>> <span style="color: rgb(255, 182, 193); font-size: 15px;">class</span> <span style="color: rgb(255, 255, 0); font-size: 15px;">MyClass</span>():...     <span style="color: rgb(255, 182, 193); font-size: 15px;">def</span> <span style="color: rgb(30, 144, 255); font-size: 15px;"><strong>__init__</strong></span>(<span style="color: rgb(255, 182, 193); font-size: 15px;">self</span>):...          <span style="color: rgb(255, 182, 193); font-size: 15px;">self</span>.data = []

当一个类定义了__ init __ 方法后,类实例化时会自动调用 __ init __ ().

__ init __ 函数还可以有其它参数,例如:

>>> <span style="color: rgb(255, 182, 193); font-size: 15px;">class</span> <span style="color: rgb(255, 255, 0); font-size: 15px;">Complex</span>:...     <span style="color: rgb(255, 182, 193); font-size: 15px;">def</span> <span style="color: rgb(30, 144, 255); font-size: 15px;"><strong>__init__</strong></span>(<span style="color: rgb(255, 182, 193); font-size: 15px;">self</span>, realpart, imagpart):...         <span style="color: rgb(255, 182, 193); font-size: 15px;">self</span>.r = realpart...         <span style="color: rgb(255, 182, 193); font-size: 15px;">self</span>.i = imagpart...>>> x = Complex(3.0, -4.5)>>> x.r, x.i(3.0, -4.5)

3.3 实例化对象

现在我们可以用实例化的对象做些什么呢?它唯一可以进行的操作是属性引用。有两类有效的属性名,数据属性和方法。 数据属性(data attributes)对应c++中的数据成员,数据属性无需声明,第一次给它赋值时就表明了它的存在。 另一种实例化的属性引用叫做方法(Method).方法是对象内的一个函数。

3.4 方法对象

通常我们调用一个方法的方式是:

x.f()

但是,由于x.f是一个方法对象,所以它可以存储起来,以便以后调用

>>> <span style="color: rgb(255, 182, 193); font-size: 15px;">class</span> <span style="color: rgb(255, 255, 0); font-size: 15px;">MyClass</span>:...     <span style="color: rgb(255, 160, 122);">"""A simple example class"""</span>...     i = 12345...     <span style="color: rgb(255, 182, 193); font-size: 15px;">def</span> <span style="color: rgb(30, 144, 255); font-size: 15px;"><strong>f</strong></span>(<span style="color: rgb(255, 182, 193); font-size: 15px;">self</span>):...         <span style="color: rgb(255, 182, 193); font-size: 15px;">return</span> <span style="color: rgb(255, 160, 122);">'hello world'</span>... >>> x = MyClass()>>> x.f()<span style="color: rgb(255, 160, 122);">'hello world'</span>>>> xf = x.f>>> xf()<span style="color: rgb(255, 160, 122);">'hello world'</span>

你可能已经发现,f名名有一个参数,但是调用时为什么没有使用呢。其实,原因在于 x.f() 与 MyClass.f(x) 是等价的。

>>> MyClass.f(x)<span style="color: rgb(255, 160, 122);">'hello world'</span>

4 漫谈

数据属性如果和方法属性名称相同,前者会覆盖后者。所以为了避免名称冲突,最好养成一些习惯,比如方法名称大写,数据属性 名称前加一个短小,唯一的前缀。或者数据属性用名词,方法属性用动词。 数据属性可以被方法引用,也可以被对象的使用者引用。换句话说,类不能实现为纯抽象数据类型。 通常,方法的第一个参数是self.虽然这只是一个习惯用法,但是遵循一些习惯用法会让你的程序可读性更强。 函数定义没有必要非在类里面,例如:

<span style="color: rgb(144, 238, 144);"><em># Function defined outside the class</em></span><span style="color: rgb(255, 182, 193); font-size: 15px;">def</span> <span style="color: rgb(30, 144, 255); font-size: 15px;"><strong>f1</strong></span>(<span style="color: rgb(255, 182, 193); font-size: 15px;">self</span>, x, y):    <span style="color: rgb(255, 182, 193); font-size: 15px;">return</span> <span style="color: rgb(135, 206, 250);"><u>min</u></span>(x, x+y)<span style="color: rgb(255, 182, 193); font-size: 15px;">class</span> <span style="color: rgb(255, 255, 0); font-size: 15px;">C</span>:    f = f1    <span style="color: rgb(255, 182, 193); font-size: 15px;">def</span> <span style="color: rgb(30, 144, 255); font-size: 15px;"><strong>g</strong></span>(<span style="color: rgb(255, 182, 193); font-size: 15px;">self</span>):        <span style="color: rgb(255, 182, 193); font-size: 15px;">return</span> <span style="color: rgb(255, 160, 122);">'hello world'</span>    h = g

一个方法可以通过self参数调用其它方法,

>>> <span style="color: rgb(255, 182, 193); font-size: 15px;">class</span> <span style="color: rgb(255, 255, 0); font-size: 15px;">Bag</span>:...     <span style="color: rgb(255, 182, 193); font-size: 15px;">def</span> <span style="color: rgb(30, 144, 255); font-size: 15px;"><strong>__init__</strong></span>(<span style="color: rgb(255, 182, 193); font-size: 15px;">self</span>):...          <span style="color: rgb(255, 182, 193); font-size: 15px;">self</span>.data = []...     <span style="color: rgb(255, 182, 193); font-size: 15px;">def</span> <span style="color: rgb(30, 144, 255); font-size: 15px;"><strong>add</strong></span>(<span style="color: rgb(255, 182, 193); font-size: 15px;">self</span>, x):...          <span style="color: rgb(255, 182, 193); font-size: 15px;">self</span>.data.append(x)...     <span style="color: rgb(255, 182, 193); font-size: 15px;">def</span> <span style="color: rgb(30, 144, 255); font-size: 15px;"><strong>addtwice</strong></span>(<span style="color: rgb(255, 182, 193); font-size: 15px;">self</span>, x):...          <span style="color: rgb(255, 182, 193); font-size: 15px;">self</span>.add(x)...          <span style="color: rgb(255, 182, 193); font-size: 15px;">self</span>.add(x)... >>> b = Bag()>>> b.data[]>>> b.add(<span style="color: rgb(255, 160, 122);">'1'</span>)>>> b.data[<span style="color: rgb(255, 160, 122);">'1'</span>]>>> b.addtwice(<span style="color: rgb(255, 160, 122);">'x'</span>)>>> b.data[<span style="color: rgb(255, 160, 122);">'1'</span>, <span style="color: rgb(255, 160, 122);">'x'</span>, <span style="color: rgb(255, 160, 122);">'x'</span>]

5 派生

派生类的形式如下:

<span style="color: rgb(255, 182, 193); font-size: 15px;">class</span> <span style="color: rgb(255, 255, 0); font-size: 15px;">DerivedClassName</span>(BaseClassName):    <statement-1>    .    .    .    <statement-N>

BaseClassName必须在包含派生类的域内定义,BaseClassName可以是一个表达式,例如:

<span style="color: rgb(255, 182, 193); font-size: 15px;">class</span> <span style="color: rgb(255, 255, 0); font-size: 15px;">DerivedClassName</span>(modname.BaseClassName):

当派生类的对象引用了一个属性时,会先在派生类内查找这个属性名,如果找不到,再到基类中查找。 派生类可以覆盖基类中的方法,即使基类中的方法被覆盖了,也可以使用下面的方法来调用

BaseClassName.methodname(<span style="color: rgb(255, 182, 193); font-size: 15px;">self</span>, arguments)

6 多重继承

Python 支持有限的多重继承:

<span style="color: rgb(255, 182, 193); font-size: 15px;">class</span> <span style="color: rgb(255, 255, 0); font-size: 15px;">DerivedClassName</span>(Base1, Base2, Base3):    <statement-1>    .    .    .    <statement-N>

在旧风格的类中,唯一的规则是深度优先,从左到右。以上述类定义为例,如果一个属性没有在 DerivedClassName中被 找到,那么会继续搜索Base1,Base2等等 在新风格的类中,对方法的解析次序是动态改变的,这是因为类的继承关系会呈现出一个或多个菱形。例如新风格的类都由 object类派生出,这样就会就多条路径通向object。为了避免基类被多次搜索,使用了线性化算法将所有基类排列成从左 到右的顺序

7 私有变量和类局部引用

实例的私有变量只能在对象内部使用,python中常常使用例如 _ spam 的形式来代表API的非公有部分,无论是函数,方法还是 数据成员。类私有成员的特性的一种有效的用法是可以避免与子类中定义的名字冲突,这种机制叫做 mangling:

<span style="color: rgb(255, 182, 193); font-size: 15px;">class</span> <span style="color: rgb(255, 255, 0); font-size: 15px;">Mapping</span>:    <span style="color: rgb(255, 182, 193); font-size: 15px;">def</span> <span style="color: rgb(30, 144, 255); font-size: 15px;"><strong>__init__</strong></span>(<span style="color: rgb(255, 182, 193); font-size: 15px;">self</span>, iterable):        <span style="color: rgb(255, 182, 193); font-size: 15px;">self</span>.items_list = []        <span style="color: rgb(255, 182, 193); font-size: 15px;">self</span>.__update(iterable)    <span style="color: rgb(255, 182, 193); font-size: 15px;">def</span> <span style="color: rgb(30, 144, 255); font-size: 15px;"><strong>update</strong></span>(<span style="color: rgb(255, 182, 193); font-size: 15px;">self</span>, iterable):        <span style="color: rgb(255, 182, 193); font-size: 15px;">for</span> item <span style="color: rgb(255, 182, 193); font-size: 15px;">in</span> iterable:            <span style="color: rgb(255, 182, 193); font-size: 15px;">self</span>.items_list.append(item)    __update = update <span style="color: rgb(144, 238, 144);"><em># private copy of original update() method</em></span><span style="color: rgb(255, 182, 193); font-size: 15px;">class</span> <span style="color: rgb(255, 255, 0); font-size: 15px;">MappingSubclass</span>(Mapping):    <span style="color: rgb(255, 182, 193); font-size: 15px;">def</span> <span style="color: rgb(30, 144, 255); font-size: 15px;"><strong>update</strong></span>(<span style="color: rgb(255, 182, 193); font-size: 15px;">self</span>, keys, values):        <span style="color: rgb(144, 238, 144);"><em># provides new signature for update()</em></span>        <span style="color: rgb(144, 238, 144);"><em># but does not break __init__()</em></span>        <span style="color: rgb(255, 182, 193); font-size: 15px;">for</span> item <span style="color: rgb(255, 182, 193); font-size: 15px;">in</span> <span style="color: rgb(135, 206, 250);"><u>zip</u></span>(keys, values):            <span style="color: rgb(255, 182, 193); font-size: 15px;">self</span>.items_list.append(item)

注意上述代码中 __ update 的使用,避免了子类中对update的覆盖影响到基类 __ init__ 中的 update.

8 结构体

有时候我们可能需要像C中的struct那样的数据类型,把少量的数据项放在一起。Python中可以使用定义一个空类来实现这一点:

<span style="color: rgb(144, 238, 144);"><em># filename:p.py</em></span><span style="color: rgb(255, 182, 193); font-size: 15px;">class</span> <span style="color: rgb(255, 255, 0); font-size: 15px;">Employee</span>:    <span style="color: rgb(255, 182, 193); font-size: 15px;">pass</span><span style="color: rgb(255, 215, 0);">john</span> = Employee() <span style="color: rgb(144, 238, 144);"><em># Create an empty employee record</em></span><span style="color: rgb(144, 238, 144);"><em># Fill the fields of the record</em></span>john.name = <span style="color: rgb(255, 160, 122);">'John Doe'</span>john.dept = <span style="color: rgb(255, 160, 122);">'computer lab'</span>john.salary = 1000
>>> <span style="color: rgb(255, 182, 193); font-size: 15px;">import</span> p>>> p.john<p.Employee instance at 0xb71f50ac>>>> p.john.name<span style="color: rgb(255, 160, 122);">'John Doe'</span>>>> p.john.dept<span style="color: rgb(255, 160, 122);">'computer lab'</span>>>> p.john.salary1000
0 0