纯净、安全、绿色的下载网站

首页|软件分类|下载排行|最新软件|IT学院

当前位置:首页IT学院IT技术

Python面向对象类编写 Python面向对象类编写细节分析【类,方法,继承,超类,接口等】

KLeonard   2021-03-30 我要评论

本文实例讲述了Python面向对象类编写技术细节。分享给大家供大家参考具体如下:

类代码编写细节

继续学习类、方法和继承。

class语句

以下是class语句的一般形式:

class <name>(superclass,...):
  data = value
  def method(self,...):
    self.member = value

在class语句内任何赋值语句都会产生类属性而且还有特殊名称方法重载运算符。例如名为__init__的函数会在实例对象构造时调用(如果定义过的话)。

例子

类是命名空间也就是定义变量名(属性)的工具。

1.就像函数一样class语句是本地作用域由内嵌的赋值语句建立的变量名就存在于这个本地作用域内。

2.就像模块内的变量名在class语句内赋值的变量名会变成类对象中的属性。

因为class是复合语句所以任何种类的语句都可位于其主体内:print、=、if、def等。当class语句自身执行时class语句内的所有语句都会执行。在class语句内赋值的变量名会创建类属性而内嵌的def则会创建类方法。

例如把简单的非函数的对象赋值给类属性就会产生数据属性由所有实例共享。

>>> class ShareData:
  spam = 42
>>> x = ShareData()
>>> y = ShareData()
>>> x.spam,y.spam
(42, 42)

在这里因为变量名spam是在class语句的顶层进行赋值的因此会附加在这个类中从而为所有的实例共享。我们可通过类名称修改它或者是通过实例或类引用它。

>>> ShareData.spam = 99
>>> x.spam,y.spam,ShareData.spam
(99, 99, 99)

这种类属性可以用于管理贯穿所有实例的信息。例如所产生的实例的数目的计数器。

现在如果通过实例而不是类来给变量名spam赋值时看看会发生什么:

>>> x.spam = 88
>>> x.spam,y.spam,ShareData.spam
(88, 99, 99)

对实例的属性进行赋值运算会在该实例内创建或修改变量名而不是在共享的类中。

对对象属性进行赋值总是会修改该对象除此之外没有其他的影响。例如y.spam会通过继承而在类中查找但是对x.spam进行赋值运算则会把该变量名附加在x本身上。

看下面这个例子可以更容易理解这种行为把相同的变量名储存在两个位置:

>>> class MixedNames:
  data = 'spam'
  def __init__(self,value):
    self.data = value
  def display(self):
    print(self.data,MixedNames.data)

当创建这个类的实例的时候变量名data会在构造函数方法内对self.data进行赋值运算从而把data附加到这些实例上。

>>> x = MixedNames(1)
>>> y = MixedNames(2)
>>> x.display(),y.display()
1 spam
2 spam
(None, None)

【这里的(None,None)是调用display函数的返回值】

结果就是data存在于两个地方:在实例对象内(由__init__中的self.data赋值运算所创建)以及在实例继承变量名的类中(由类中的data赋值运算所创建)。类的display方法打印了这两个版本先以点号运算得到self实例的属性然后才是类。

利用这些技术把属性储存在不同对象内我们可以决定其可见范围。附加在类上时变量名是共享的;附加在实例上时变量名是属于每个实例的数据而不是共享的数据。

方法

方法即函数。方法在class中是由def语句创建的函数对象。从抽象的角度来看方法替实例对象提供了要继承的行为。从程序的角度看方法与简单函数的工作方式完全一致只是有一个重要的差别:方法的第一个参数总是接收方法调用的隐性主体也就是实例对象。

Python会自动把实例方法的调用对应到类方法函数。如下所示方法调用需要通过实例就像这样:

instance.method(args...)

这会自动翻译成以下形式的类方法函数调用:

class.method(instance,args...)

class通过Python继承搜索流程找出方法名称所在之处。事实上两种调用形式在Python中都有效。

在类方法中按惯例第一个参数通常都称为self(严格来说只有其位置重要而不是它的名称)。这个参数给方法提供了一个钩子从而返回调用的主体也就是实例对象:因为类可以产生许多实例对象所以需要这个参数来惯例每个实例彼此各不相同的数据。

例子

定义下面这个类:

>>> class NextClass:
  def printer(self,text):
    self.message = text
    print(self.message)

我们通过实例调用printer方法如下:

>>> x = NextClass()
>>> x.printer('instance call')
instance call
>>> x.message
'instance call'

当通过实例进行点号运算调用它时printer会先通过继承将其定位然后它的self参数会自动赋值为实例对象(x)。text参数会获得在调用时传入的字符串('instance call')。注意:因为Python会自动传递第一个参数给self实际上只需要传递一个参数。在printer中变量名self是用于读取或设置每个实例的数据的因为self引用的是当前正在处理的实例。

方法能通过实例或类本身两种方法其中的任意一种进行调用。例如我们也可以通过类的名称调用printer只要明确地传递了一个实例给self参数。

>>> NextClass.printer(x,'class call')#Direct Class Call
class call
>>> x.message
'class call'

通过实例和类的调用具有相同的效果只要在类形式中传递了相同的实例对象。实际上在默认情况下如果尝试不带任何实例调用的方法时就会得到出错信息。

>>> NextClass.printer('bad call')
Traceback (most recent call last):
 File "<pyshell#35>", line 1, in <module>
  NextClass.printer('bad call')
TypeError: printer() missing 1 required positional argument: 'text'

调用超类构造函数

在构造时Python会找出并且只调用一个__init__。如果保证子类的构造函数也会执行超类构造时的逻辑一般都必须通过类明确地调用超类的__init__方法。

class Super:
  def __init__(self,x):
    ...default code...
class Sub(Super):
  def __init__(self,x,y):
    Super.__init__(self,x)
    ...custom code...
I = Sub(1,2)

这种写法便于维护代码之前也介绍过。这种方法扩展了超类的方法而不是完全取代了它。

类接口技术

扩展只是一种与超类接口的方法。下面所示的specialize.py文件定义了多个类示范了一些常用技巧。

Super:定义一个method函数以及在子类中期待一个动作的delegate。
Inheritor:没有提供任何新的变量名因此会获得Super中定义的一切内容。
Replacer:用自己的版本覆盖Super的method
Extender:覆盖并回调默认method从而定制Super的method
Provider:实现Super的delegate方法预期的action方法。

下面是这个文件:

class Super:
  def method(self):
    print('in Super.methon')
  def delegate(self):
    self.action()
class Inheritor(Super):
  pass
class Replacer(Super):
  def method(self):
    print('in Replacer.method')
class Extender(Super):
  def method(self):
    print('starting Extender.method')
    Super.method(self)
    print('ending Extender.method')
class Provider(Super):
  def action(self):
    print('in Provider.action')
if __name__=='__main__':
  for klass in (Inheritor,Replacer,Extender):
    print('\n'+klass.__name__+'...')
    klass().method()
  print('\nProvider...')
  x = Provider()
  x.delegate()

执行结果如下:

Inheritor...
in Super.methon
Replacer...
in Replacer.method
Extender...
starting Extender.method
in Super.methon
ending Extender.method
Provider...
in Provider.action

抽象超类

注意上例中的Provider类是如何工作的。当通过Provider实例调用delegate方法时有两个独立的继承搜索会发生:

1.在最初的x.delegate的调用中Python会搜索Provider实例和它上层的对象直到在Super中找到delegate的方法。实例x会像往常一样传递给这个方法的self参数。

2.在Super.delegate方法中self.action会对self以及它上层的对象启动新的独立继承搜索。因为self指的是Provider实例在Provider子类中就会找到action方法。

这种“填空式”的代码结构一般就是OOP的软件框架。这个例子中的超类有时也称作是抽象超类——也就是类的部分行为默认是由其子类所提供的。如果预期的方法没有在子类中定义当继承搜索失败时Python会引发未定义变量名的异常。

希望本文所述对大家Python程序设计有所帮助。


相关文章

猜您喜欢

网友评论

Copyright 2020 www.Shellfishsoft.com 【贝软下载站】 版权所有 软件发布

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 点此查看联系方式