⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 node11.html

📁 同样是来自国外的经典python教材
💻 HTML
📖 第 1 页 / 共 3 页
字号:
  </p>  <p> 当一个类别的定义正常的离开时( 藉由定义的尾端),一个类别物件( <i>class object</i> )就被创造出来了。这个类别物件基本上来说是只是一个包装起来的东西,其内容是由这个类别定义所创造出来的namespace里面的内容。我们在下一节就会有更多有关类别物件(classobjects)的讨论。另外在类别的定义离开时,原来的local scope (在进入类别的定义之前的那一个local space)就会被重新使用,并且这个创造出来的类别物件就会被放在这个localscope里面,并且被连结到你所定义的类别名称(上面的例子里是 <tt class="class">ClassName</tt> )上面。   </p>  <p>  </p>  <h2> <br> 9.3.2 类别物件(Class Objects)  </h2>    <p> 类别物件可以做两件事情,一是attribute的指涉(references),另一个是创造出一个特例来(instantiation)。  </p>  <p> <i>Attribute references</i> 所使用的是在Python里面标准的attribute reference的语法:  <code>obj.name</code> 。有效的attribute的名称指的是当类别物件被创造时,所有在类别的namespace里面的名称。所以,如果你的类别定义如同下面例子的话:  </p>  <p> </p>  <dl>  <dd><pre class="verbatim">class MyClass:<br>    "A simple example class"<br>    i = 12345<br>    def f(x):<br>        return 'hello world'<br></pre>    </dd>    </dl>      <p> 你就可以使用 <code>MyClass.i</code> 以及  <code>MyClass.f</code> 这两个有效的attributereferences语法,它们分别会传回一个整数以及一个method物件来。你也可以设定值给这些类别的attributes,如此你就可以改变 <code>MyClass.i</code> 的值了。  <tt class="member">__doc__</tt> 也是类别物件的一个有效的attribute,其传回值是这个类别的注释字串(docstring),也就是:    <code>"A simple example class"</code> 。   </p>    <p> 类别的特例化( Class <i>instantiation</i> )是使用函式的表示方法。看起来好像这个类别物件是一个没有参数的函式,然后传回来的就是这个类别的的一个特例(instance)。我们再以前面的类别为例子:  </p>    <p> </p>    <dl>    <dd><pre class="verbatim">x = MyClass()<br></pre>      </dd>      </dl>        <p> 就会创造出一个新的类别的 <i>instance</i> ,然后我们再把这个物件设定给 <code>x</code> 这个local的变数。  </p>      <p> 类别的特例化(Class instantiation )这个动作(也就是``呼叫''一个类别物件)所创造出来的是一个空的物件。有许多的类别希望创造出来的物件有一个特定的初始状态,所以你可以在类别里面定义一个特别的method叫做      <tt class="method">__init__()</tt>  ,如同下例:   </p>      <p> </p>      <dl>      <dd><pre class="verbatim">    def __init__(self):<br>        self.data = []<br></pre>        </dd>        </dl>          <p> 当你的类别有定义一个 <tt class="method">__init__()</tt> method时,当你在特例化(instantiation)你的类别时,就会自动的引发        <tt class="method">__init__()</tt> 执行,并且创造出一个类别的特例(instance)。所以,一个新的物件就可以截由底下的呼叫来创造出来:  </p>        <p> </p>        <dl>        <dd><pre class="verbatim">x = MyClass()<br></pre>          </dd>          </dl>            <p> 当然,  <tt class="method">__init__()</tt> 这个method 可以有参数传入,这样可以增加使用时的弹性。在这样做的时候,使用特例化(instantiate)类别的语法时,所传入的参数就会被传到          <tt class="method">__init__()</tt> 里面去。如范例:   </p>          <p> </p>          <dl>          <dd><pre class="verbatim">&gt;&gt;&gt; class Complex:<br>...     def __init__(self, realpart, imagpart):<br>...         self.r = realpart<br>...         self.i = imagpart<br>... <br>&gt;&gt;&gt; x = Complex(3.0,-4.5)<br>&gt;&gt;&gt; x.r, x.i<br>(3.0, -4.5)<br></pre>            </dd>            </dl>              <p>  </p>            <h2> <br> 9.3.3 特例物件(instance objects) </h2>              <p> 现在对于这个被创造出来的特例物件(instance objects),我们又该怎么用呢?对于这样的特例物件,唯一它们懂得的就是attributereferences。有两种的attribute names我们可以使用: </p>            <p> 第一种我叫他是资料特性(  <i>data attributes</i> ),这类似于在Smalltalk中所说的特例变数(``instancevariables'')以及在C++中的资料成员(``data members'')。如同local变数一样,Data attributes不需要再宣告,你第一次设定值给它们的时候它们就自动存在了。举例来说,如果 <code>x</code> 是 <tt class="class">MyClass</tt> 这个物件的一个instance,底下这个程式码就会印出            <code>16</code> 这个结果来:   </p>            <p> </p>            <dl>            <dd><pre class="verbatim">x.counter = 1<br>while x.counter &lt; 10:<br>    x.counter = x.counter * 2<br>print x.counter<br>del x.counter<br></pre>              </dd>              </dl>                <p> 第二种instance objecet可以使用的attribute references叫做方法( <i>methods</i>) 。一个method就是一个隶属于某个物件的函式。(在Python中,method一词并不只特定用于类别的instances,其他的物件资料型态也可以有自己的method,例如list物件就有很多methods像是append,insert,remove,sort等等。但是我们底下用到method这个词的时候,除非特别说明,要不然我们倒是单独指着instanceobjects 的method说的。)   </p>              <p> 一个instance objects 可以用的有效method名称是由其类别所决定的。定义上来说,所有类别里面(使用者定义)为函式物件的attribute,都会成为其instance的相对应method。所以在我们的例子里, <code>x.f</code> 就是一个有效的method的reference,其原因是因为  <code>MyClass.f</code>是一个函式;但是 <code>x.i</code> 就不是一个method的reference,因为  <code>MyClass.i</code> 不是一个函式。但是,要注意的是,  <code>x.f</code> 和  <code>MyClass.f</code> 是两回事,它是一个method物件(              <i>method object</i> ),而非一个函式物件。   </p>              <p>  </p>              <h2> <br> 9.3.4 Method Objects(方法物件)  </h2>                <p> 通常,一个method可以马上被呼叫,例如:   </p>              <p> </p>              <dl>              <dd><pre class="verbatim">x.f()<br></pre>                </dd>                </dl>                  <p> 在我们的例子里,这一个呼叫会传回来 <code>'hello world'</code> 这个字串。但是,因为 <code>x.f</code> 是一个method物件,所以我们没有必要马上就呼叫它,我们可以把它储存起来,然后再稍后再呼叫它。举例如下:                   </p>                <p> </p>                <dl>                <dd><pre class="verbatim">xf = x.f<br>while 1:<br>    print xf()<br></pre>                  </dd>                  </dl>                    <p> 这个例子同样的会一直不断的印出 "<tt class="samp">hello world</tt>"来。   </p>                  <p> 到底,你的method被呼叫时,什么事情发生了呢?你也许注意到了 当我们呼叫 <code>x.f()</code> 的时候并没有传入任何参数,但是我们在类别定义的时候确实有定义 <tt class="method">f</tt> 所传入的参数。到底是怎么回事呢?当然,依照Python的定义,当一个函是需要参数而你呼叫时没有传入参数的话,是会引发一个例外状况(exception)的,甚至这个传入的参数没有被用到也是一样&hellip;  </p>                  <p> 事实上,你也许已经猜到答案了。对于method来说有一个较特殊的事是,method所处的物件会被当作函式传入的第一个参数。所以在我们的例子里面,当我们呼叫                  <code>x.f()</code> 的时候,事实上我们是呼叫 <code>MyClass.f(x)</code> 。一般来说,如果你呼叫method的时候传了  <var>n</var> 个参数,其实你是呼叫背后所代表之类别的函式,而且该method所在的物件会插入在传入的参数中当作第一个参数。  </p>                  <p> 如果你还不了解到底method如何运作的话,你也许可以看看它的实作来更了解它。当一个instance的attribute被reference,而这个attribute又不是一个dataattribute的时候,该instance的类别会被寻找。如果这个class attribute的名字在类别里面代表的是一个函式物件的话,就会有一个method物件被创造出来。这个method物件是一个由这个instance物件(的指标),以及刚刚找到的这个函式物件所包装起来的一个抽象的物件。当这个method物件被带着一串参数呼叫的时候,这个method物件会先打开原来的包装,然后会用instance物件(的指标)以及那一串传进来的参数组成新的参数串,然后我们再用这个新的参数串来呼叫在method物件里面的函式物件。  </p>                  <p>  </p>                  <h1> <br> 9.4 一些随意的想法  </h1>                    <p> [这些东西其实应该更多花点心思加以处理的&hellip;]  </p>                  <p> 如果data attributes和method attributes 有相同名称的话,data attributes会盖过method attributes 。要避免这个命名的冲突(这常常是许多bug的由来),你可能需要一些命名的规则。比如说,让method的名称都是大写的,在dataattribute的前面加上一些小字串(或者是底线),或者对于method都用动词,对data attribute都用名词。   </p>                  <p> 除了一般object的使用者(client)之外,Data attributes也可以在method里面被使用到。也就是说,类别(class)是不能用来实作出纯粹的抽象资料型态(abstractdata types)的。事实上,再Python里面没有东西可以保证资料的隐藏(data hiding),我们只能仰赖彼此的约定及尊重了。(另一方面来说,用C写成的Python是可能完全隐藏其实作的细节并且在需要的时候可以控制对物件的存取权限的;这是用来给C所写成的Python延伸机制(extensionto Python)所使用的。)   </p>                  <p> 使用data attributes的人要特别小心,你有可能把由method所管理的data attributes弄得一蹋糊涂。值得注意的是,类别的使用者可以自行在instance物件里面加入dataattributes,只要小心处理命名的问题,这不会对method的正确性有所影响。再次提醒,你可以用命名的规则来避免此事发生。  </p>                  <p> 从method里面要使用data attributes (或者是其他的methods)并没有捷径。我发现这样的好处是程式的可读性会增加很多,因为当你在读method的程式码的时候,local变数跟instance变数混淆的机会就会少很多。  </p>                  <p> 习惯上,我们把一个method的第一个参数叫做 <code>self</code> 。这只是一个习惯而已, <code>self</code> 这个名字对Python来说完全没有什么特殊的意义。(但是你要注意,如果你不用这一个习惯的话,对于某些读你程式的Python程式设计师来说,也许你程式的可读性就低了一点。而且可能有一些类似像                  <i>class browser</i> 之类的程式是靠这个约定来分辨class的特性,所以你不遵守的话,你的类别它们可能就读不懂)  </p>                  <p> 所有的在类别里面的函式物件,在定义上都是该类别之instance的一个method。在类别里面的意思不限定于一定要在文字上是在类别的定义里面,你也可以把一个函式物件设定给一个在类别里面的local变数,这样也算数的。比如说:  </p>                  <p> </p>                  <dl>                  <dd><pre class="verbatim"># Function defined outside the class<br>def f1(self, x, y):<br>    return min(x, x+y)<br><br>class C:<br>    f = f1<br>    def g(self):<br>        return 'hello world'<br>    h = g<br></pre>                    </dd>                    </dl>                      <p> 现在 <code>f</code>, <code>g</code> 以及 <code>h</code> 都是类别 <tt class="class">C</tt> attributes,而且都指涉(reference)到某个函式物件去。而且,如此做的话,当                    <tt class="class">C</tt> 有instance的时候, <code>f</code> , <code>g</code>  以及 <code>h</code> 都会变成instance的method(事实上 <code>h</code>  所指的函式是跟 <code>g</code> 同一个的)。值得注意的是,如果你真这样做的话,你只是让读你程式的人头昏眼花罢了。  </p>                    <p> 你也可以在method里面呼叫其他的method,你所需要的只是用 <code>self</code> 这个参数的method attribute就可以了。例如:    </p>                    <p> </p>                    <dl>                    <dd><pre class="verbatim">class Bag:<br>    def __init__(self):<br>        self.data = []<br>    def add(self, x):<br>        self.data.append(x)<br>    def addtwice(self, x):<br>        self.add(x)<br>        self.add(x)<br></pre>                      </dd>                      </dl>                        <p> method跟一般的函式物件一样可以使用全域名称(global name)。Method的globalscope所指的是类别的定义所存在的module,(注意:类别本身绝不会是一个global scope!)。 你大概很少有机会在method里面会需要用到globalscope,但是你还是可以使用global scope的,method可以使用在global scope之中所import进来的函式以及module,也可以使用在globalscope里面定义的函式及类别。通常,包含method的这个类别本身就定义在这个global space里面,而且下一段我们就要讲到为什么你会需要在method里面用到自己本身的类别。  </p>                      <p>  </p>                      <h1> <br> 9.5 继承(Inheritance)   </h1>                        <p> 当然啦,一个程式语言如果没有继承的话就不需要担心类别(``class'')这个字了。一个子类别(derivedclass)的定义看起来是这样的:   </p>                      <p> </p>                      <dl>                      <dd><pre class="verbatim">class DerivedClassName(BaseClassName):<br>    &lt;statement-1&gt;<br>    .<br>    .<br>    .<br>    &lt;statement-N&gt;<br></pre>                        </dd>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -