📄 9.html
字号:
<P>作为类属性的任何函数对象都为该类的实例定义一个方法。函数的定义不一定必须在类定义内部:只要在类内把一个函数对象赋给一个局部变量就可以了。例如:</P><PRE># Function defined outside the classdef f1(self, x, y): return min(x, x+y) class C: f = f1 def g(self): return 'hello world' h = g</PRE><P>现在f、g和h都是类C的属性且指向函数对象,所以它们都是C的实例的方法——其中h与g完全等价。注意我们应该避免这种用法以免误导读者。</P><P> 方法可以用代表所属对象的self自变量来引用本类其它的方法,如:<PRE>class Bag: def empty(self): self.data = [] def add(self, x): self.data.append(x) def addtwice(self, x): self.add(x) self.add(x)</PRE><P>实例化操作(“调用”一个类对象)生成一个空对象。许多类要求生成具有已知初识状态的类。为此,类可以定义一个特殊的叫做__init__()的方法,如:</P><PRE> def __init__(self): self.empty()</PRE><P>一个类定义了__init__()方法以后,类实例化时就会自动为新生成的类实例调用调用__init__()方法。所以在Bag例子中,可以用如下程序生成新的初始化的实例:</P><PRE> x = Bag()</PRE><P>当然,__init__()方法可以有自变量,这样可以实现更大的灵活性。在这样的情况下,类实例化时指定的自变量被传递给__init__()方法。例如:</P><PRE>>>> class Complex:... def __init__(self, realpart, imagpart):... self.r = realpart... self.i = imagpart... >>> x = Complex(3.0,-4.5)>>> x.r, x.i(3.0, -4.5)</PRE><P>方法可以和普通函数一样地引用全局名字。方法的全局作用域是包含类定义的模块。(注意类本身并不被用作全局作用域!)虽然我们很少需要在方法中使用全局数据,全局作用域还是有许多合法的用途:例如,导入全局作用域的函数和模块可以被方法使用,在同一模块中定义的函数和方法也可以被方法使用。包含此方法的类一般也在此全局作用域中定义,下一节我们会看到一个方法为什么需要引用自己的类!</P><H2>9.5 继承</H2><P>当然,一个语言如果不支持继承就谈不到“类”。导出类的定义方法如下:<PRE>class 导出类名(基类名): <语句-1> . . . <语句-N></PRE><P>其中“基类名”必须在包含导出类定义的作用域中有定义。除了给出基类名外,还可以给出一个表达式,在基类定义于其它模块中时这是有用的,如:</P><P>class 导出类名 (模块名.基类名):</P><P>导出类定义的运行和基类运行的方法是一样的。生成类对象是,基类被记忆。这用于解决属性引用:如果类中未找到要求的属性就到基类中去查找。如果基类还有基类的话这个规则递归地应用到更高的类。</P><P>导出类在实例化时没有任何特殊规则。“导出类名()”产生该类的一个新实例。方法引用这样解决:搜索相应类属性,如果必要的话逐级向基类查找,如果找到了一个函数对象就是有效的方法引用。</P><P>导出类可以重写基类的方法。因为方法在调用同一对象的其它方法时并无任何特殊权限,如果基类中某一方法调用同一基类的另一方法,在导出类中该方法调用的就可能是已经被导出类重写后的方法了。(对C++程序员而言:Python中所有方法都是“虚拟函数”)。</P><P>导出类中重写的方法可能是需要扩充基类的同名方法而不是完全代替原来的方法。导出类调用基类同名方法很简单:“基类名.方法名(self, 自变量表)”。对类用户这种做法偶尔也是有用的。(注意只有基类在同一全局作用域定义或导入时才能这样用)。</P><H3>8.5.1 多重继承</H3><P>Python也支持有限的多重继承。有多个基类的类定义格式如下:<PRE>class 导出类名 (基类1, 基类2, 基类3): <语句-1> . . . <语句-N></PRE><P>关于多重继承只需要解释如何解决类属性引用。类属性引用是深度优先,从左向右进行的。所以,如果在导出类定义中未找到某个属性,就先在基类1中查找,然后(递归地)在基类1的基类中查找,如果都没有找到,就在基类2中查找,如此进行下去。</P><P>(对某些人来说宽度优先——先在基类2和基类3中查找再到基类1的基类中查找——看起来更自然。然而,这需要你在确定基类1与基类2的属性冲突时明确知道这个属性是在基类1本身定义还是在其基类中定义。深度优先规则不区分基类1的一个属性到底是直接定义的还是继承来的)。</P><P>很显然,如果不加约束地使用多重继承会造成程序维护的恶梦,因为Python避免名字冲突只靠习惯约定。多重继承的一个众所周知的问题是当导出类有两个基类恰好从同一个基类导出的。尽管很容易想到这种情况的后果(实例只有一份“实例变量”或数据属性被共同的基类使用),但是这种做法有什么用处却是不清楚的。</P><H2>9.6 私有变量</H2><P>Python对私有类成员有部分支持。任何象__spam这样形式的标识符(至少有两个前导下划线,至多有一个结尾下划线)目前被替换成_classname__spam,其中classname是所属类名去掉前导下划线的结果。这种搅乱不管标识符的语法位置,所以可以用来定义类私有的实例、变量、方法,以及全局变量,甚至于保存对于此类是私有的其它类的实例。如果搅乱的名字超过255个字符可能会发生截断。在类外面或类名只有下划线时不进行搅乱。</P><P>名字搅乱的目的是给类一种定义“私有”实例变量和方法的简单方法,不需担心它的其它类会定义同名变量,也不怕类外的代码弄乱实例的变量。注意搅乱规则主要是为了避免偶然的错误,如果你一定想做的话仍然可以访问或修改私有变量。这甚至是有用的,比如调试程序要用到私有变量,这也是为什么这个漏洞没有堵上的一个原因。(小错误:导出类和基类取相同的名字就可以使用基类的私有变量)。</P><P>注意传递给exec,eval()或evalfile()的代码不会认为调用它们的类的类名是当前类,这与global语句的情况类似,global的作用局限于一起字节编译的代码。同样的限制也适用于getattr(),setattr()和delattr(),以及直接访问__dict__的时候。</P><P>下面例子中的类实现了自己的__getattr__和__setattr__方法,把所有属性保存在一个私有变量中,这在Python的新旧版本中都是可行的:</P><PRE>class VirtualAttributes: __vdict = None __vdict_name = locals().keys()[0] def __init__(self): self.__dict__[self.__vdict_name] = {} def __getattr__(self, name): return self.__vdict[name] def __setattr__(self, name, value): self.__vdict[name] = value</PRE><H2>9.7 补充</H2><P>有时我们希望有一种类似Pascal的“record”或C的“struct”的类型,可以把几个有名的数据项组合在一起。一个空类可以很好地满足这个需要,如:</P><PRE>class Employee: pass john = Employee() # 生成一个空职员记录 # 填充记录的各个域john.name = 'John Doe'john.dept = 'computer lab'john.salary = 1000</PRE><P>一段需要以某种抽象数据类型作为输入的Python程序经常可以接受一个类作为输入,该类只是模仿了应输入的数据类型的方法。例如,如果你有一个函数是用来格式化一个文件对象中的数据,就可一个定义一个具有方法read()和readline()的类,该类可以不从文件输入而是从一个字符串缓冲区输入,把这个类作为自变量。</P><P> 实例方法对象也有属性:m.im_self是方法所属的实例,m.im_func是方法对应的函数对象。 </P><H3>9.7.1 例外可以是类</H3><P>用户自定义的例外除了可以是字符串对象以外还可以是类。这样可以定义可扩充的分层的类例外结构。</P><P>raise语句有两种新的有效格式:</P><PRE>raise 类, 实例 raise 实例</PRE><P>在第一种形式中,“实例”必须是“类”的实例或“类”的导出类的实例。第二种形式是</P><PRE>raise instance.__class__, instance</PRE><P>的简写。except语句除了可以列出字符串对象外也可以列出类。execpt子句中列出的类如果是发生的例外类或基类则是匹配的(反过来不对——except中如果是导出类而发生的例外属于基类时是不匹配的)。例如,下面的程序会显示B、C、D:</P><PRE>class B: passclass C(B): passclass D(C): pass for c in [B, C, D]: try: raise c() except D: print "D" except C: print "C" except B: print "B"</PRE><P>注意如果把except子句的次序颠倒过来的话(“except B”放在最前),程序将显示B,B,B——因为第一个匹配的except子句被引发。</P><P>当没有处理的例外是类的时候,类名显示在错误信息中,后面跟着一个冒号和一个空格,最后是实例用内置函数str()转换成字符串的结果。</P></BODY><!-- Mirrored from www.math.pku.edu.cn/teachers/lidf/docs/Python/9.html by HTTrack Website Copier/3.x [XR&CO'2005], Fri, 08 Jul 2005 11:49:15 GMT --></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -