zoukankan      html  css  js  c++  java
  • Python 点滴 IV

    【继承示意图】

    类是实例的工厂, OOP就是在树中搜索属性,类事实上就是变量名与函数打成的包



    . 每一个class语句会生成一个新的类对象

    . 每次类调用时,就会生成一个新的实例对象

    . 实例自己主动连接到创建这些实例的类

    . 类连接到超类的方式是,将超类列在类头部(),其从左到右的顺序会决定树中的次序


    有几点须要注意:

    . 属性一般是在class语句中通过赋值语句加入在类中,而不是嵌入函数的def语句中

    . 属性一般是在类中,对传给函数的特殊參数self。做赋值运算而加入在实例中的


    【方法调用的两种方式】

    def 语句出如今类中。通常称为方法:

    通过实例调用:bob.giveRise()

    通过类来调用:Employee.giveRise(bob)

    【类与模块】

    从最底层来看,类差点儿就是命名空间,非常像模块。但和模块不同的之处是:

    1. 类也支持多个对象的产生(多态)

    2. 命名空间继承(继承)

    3. 运算符重载(重载)   

    【PYTHON类的主要特点】

    . class语句创建对象并将其赋值给变量名(类似于def)

    . class语句内的赋值语句会创建类的属性(属性获得:object.name)

    . 类属性提供对象的状态和行为

    【类的实例概要】

    . 像函数那样调用对象会创建新的实例对象

    . 每一个实例对象继承类的属性并获得自己的命名空间

    . 在方法内对self属性做赋值运算会产生每一个实例自己的属性

    【第一个样例】

    class FirstClass:              #定义类的对象
        def setdata(self,value):   #定义类方法
            self.data = value      #self是个实例
        def display(self):
            print self.data        #self-data: 每一个实例共享

    创建两个实例,每一个实例拥有自己的命名空间

    >>> x = FirstClass()
    >>> y = FirstClass()

    会产生三个对象: 两个实例,一个类


    >>> x.setdata('Hello')     #调setdata方法,self就是x本身
    >>> y.setdata(100)         #执行: FirstClass.setdata(y,100)

    x,y创建不同实例对象的命名空间,所以虽然都调用display方法,值却不同

    >>> x.display()            #self.data在每一个实例中不一样
    Hello
    >>> y.display()
    100

    还有一种调用方式:

    >>> x.data = "New Value"   #可以获得或设置属性
    >>> x.display()
    New Value

    也能够设置成一个全新的属性

    >>> x.anothername = 'spam'

    【PYTHON类继承的核心观点】

    . 超类列在类开头的括号里

    . 类从超类中继承属性,当读入属性时,假设不存在于子类中,PYTHON会自己主动搜索这个属性

    . 实例会继承全部可读取类的属性. 搜索路径: 实例 ==> 创建实例的类 ==> 全部超类

    . 每一个object.attribute都会开启新的独立搜索

    . 逻辑的改动是通过创建子类,而不是改动超类

    这样的搜索的结果及主要目的就是:

    第一: 类支持了程序的分解和定制。比迄今为止所见到的其它不论什么语言工具都要好。

    第二: 能够把程序的冗余度降到最低。降低维护成本。也就是把操作分解为单一,共享的实现

    第三: 这样敲代码时。也能够让我们队现有的程序代码进行定制,而不是实地改动或从头開始

    【第二个样例】

    class SecondClass(FirstClass):     #继承FirstClass类中的setdata方法
        def display(self):             #改动display方法,也叫方法的重载
            print 'Current value = %s' % self.data

    搜索从实例開始==>子类==>超类,直到找到第一个为止,所以display会覆盖父类方法

    >>> z = SecondClass()
    >>> z.setdata('Hello,World!')      #从FirstClass中发现setdata方法
    >>> z.display()                    #SecondClass中发现display方法
    Current value = Hello,World!


    NOTE:作为一条规则,由于继承能够让我们在外部组件内(也就是在子类内)进行改动。类所支持的扩展和重用通常比函数或模块更好!

    【第三个样例】: 关于运算符重载

    定义SecondClass的子类,实现三个特殊名称的属性,让PYTHON自己主动调用:

    当新的实例构造时,会调用__init__(self是新的ThirdClass对象)

    当ThirdClass实例出如今+或*表达式中时。则分别调用__add__和__mul__

    class ThirdClass(SecondClass):                 #ThirdClass类,继承自SecondClass
        def __init__(self,value):                  #ThirdClass类中的值
            self.data = value
        def __add__(self,other):                   #注意这样的调用方式 self + other
            return ThirdClass(self.data + other)
        def __mul__(self,other):
            self.data = self.data * other          #self * other

    运行结果:

    >>> a = ThirdClass('AB')                       #调用__init__构造函数
    >>> a.display()                                #继承的方法
    Current value = AB
    >>> b = a + 'XY'                               #新的__add__:造一个新的实例
    >>> b.display()
    Current value = ABXY
    >>> a * 3                                      #新的__mul__:在当地改变实例
    >>> a.display()
    Current value = ABABAB


    NOTE:

    __add__方法创建并返回这个类的新的实例对象

    __mul__方法会在原处改动当前的实例对象

    【OOP最重要的两个概念】

    方法函数中的特殊self參数和__init__构造器方法是PYTHON中OOP的两个基石

    【最经常使用的运算符重载】

    运算符重载是由特定名称的方法写成的。

    这些方法的开头和结尾都是双下划线,通过这样的方法使其变得独特。这些不是内置或保留字。

    当实例出如今相应的运算中时。PYTHON就会自己主动运行它们。

    PYTHON为这些运算和特殊方法的名称定义了相应的关系。

    __init__构造器是最经常使用的。差点儿每一个类都使用这种方法为实例属性进行初始化,以及运行其它的启动任务。

    【变量名同样的样例】

    class MixedNames:                  #定义类:MixedNames
    data = 'spam'              #赋值类属性:data

    def __init__(self,value):  #赋值方法名:  
    self.data = value  #赋值实例属性:data

    def display(self):
    print MixNames.data,self.data   #类属性,实例属性

    输出结果:

    >>> ins1 = MixedNames(1)       #类工厂造两个实例对象x,y
    >>> ins2 = MixedNames(2)       #每一个实例对象有自己的数据
    >>> ins1.display();ins2.display() #self.data不同,subclass.data却是同样的。都是从data = 'spam'继承而来
    spam 1
    spam 2

    >>> MixedNames.display(ins1)   #这样的写也是能够的        

    【还有一个样例】

    message = 'Global Message!'
    class NextClass:
        message = 'Class Message'

        def printer(self,value):
    self.message = value
    print 'message', message
    print 'NextClass.message',NextClass.message
    print 'self.message',self.message

    输出结果:

    >>> x = NextClass()                     #造实例
    >>> x.printer('Instance Message!')      #调实例的方法
    message is:            Global Message!
    NextClass.message is:  Class Message!
    self.message is:       Instance Message!

    其它调用:

    >>> NextClass.printer(x,'class call')    #正确:直接调用类
    >>> NextClass.printer('class call')      #错误:必须在第一个位置放实例名
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: unbound method printer() must be called with NextClass instance as first argument (got str instance instead)

    【调用超类的构造器】

    假设除了想调用自身的构造函数。还想调用超类的。那么就必须用以下的方式

    class Super:
    def __init__(self,x):
    pass

    class Sub(Super):
    def __init__(self,x,y):
    Super.__init__(self.x)
    pass

    I = Sub(1,2)

    【继承方法的专有化】

    继承树搜索模式是将系统专有化的最好的方式。继承会先在子类中寻找变量名,然后才查找超类。子类能够对超类的属性又一次定义来代替默认的行为。

    实际上,能够把整个系统做成类的层次,再新增外部的子类来对其进行扩展,而不是在原处改动已重载的逻辑。

    . 子类能够全然代替继承的属性

    . 子类能够找到并获得超类属性

    . 子类能够通过已覆盖的方法回调超类来扩展超类的方法

    class Super:
    def method(self):
    print 'start Super.method'

    class Sub(Super):
    def method(self):                           #重写方法
    print 'starting Sub.method'         #添加行为
    Super.method(self)                  #执行默认的行为,NOTE:self的使用
    print 'ending Super.method'

    直接调用超类方法是这里的重点。Sub类代替了Super的方法函数.可是。代替时。Sub又回调了Super所导出的版本号,从而实现了默认的行为。也就是说:Sub.method仅仅是扩展了Super.method的行为,而不是全然代替。

    输出结果:

    >>> x = Super()                                     #造一个Super实例
    >>> x.method()                                      #调Super类中的method方法
    Start Super.method

    >>> x = Sub()                                       #造一个Sub实例
    >>> x.method()                                      #调Sub类中的method,当中又回调了父类的方法
    Start Sub.method
    Start Super.method
    Ending Super.method

    【类接口技术】

    扩展仅仅是一种同超类接口的方式。以下示范了specialize.py文件定义了多个类,示范了一些经常使用的技巧

    Super:     定义了一个method函数以及一个delegate函数

    Inheritor: 没有提供不论什么新的变量名,因此会获得Super中定义的一切内容

    Replacer:  用自己的版本号来覆盖Super的method

    Extender:  覆盖并回调默认的method。从而定制Super的method

    Provider:  实现Super的delegate方法预期的action方法

    #File:  specialize.py

    <span style="font-family:SimHei;font-size:14px;">class Super:
    	def method(self):
    		print 'in Super.method'
    	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 '
    ' + klass.__name__ + '...'
    		klass().method()
    		print '
    Provider...'
    		x = Provider()
    		x.delegate()</span>
    NOTE:

    1. 末尾的自我測试代码在for循环中建立三个不同类的实例

    2. 类也有特殊的__name__属性,就像模块。

    它默认类首行中的类名称的字符串
    执行结果:

    D:>python specialize.py

    Inheritor...
    in Super.method

    Provider...
    in Provider.action

    Replacer...
    in Replacer.method

    Provider...
    in Provider.action

    Extender...
    starting Extender.method
    in Super.method
    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的软件框架。从delegate方法的角度来看。这个样例中的超类也称为抽象类--也就是类的部分行为默认是由其子类来实现。

    假设逾期的方法没有在子类中定义。当继承搜索失败时,PYTHON会引发没有定义变量名的异常。

    通常会使用assert语句。使这样的子类须要更为明显,或者引发内置的异常NotImplementedError:

    <span style="font-family:SimHei;font-size:14px;">class Super:
    	
    	def method(self):
    		print 'In Super.method'
    
    	def delegate(self):
    		self.action
    
    	def action(self):
    		assert 0, 'action must be defined!'</span>

    【运算符重载】

    . 运算符重载让类拦截常规的PYTHON运算

    . 类可重载全部PYTHON表达式运算符

    . 类可重载打印、函数调用、属性点号运算等运算

    . 重载使类实例的行为像内置类型

    . 重载是通过特殊名称的类方法实现的

    简单样例

    <span style="font-family:SimHei;font-size:14px;">class Number:
    	def __init__(self,start):                  #on Number(start)
    		self.data = start
    
    	def __sub__(self,other):                   #On instance - other       
    		return Number(self.data - other)   #Result is a new instance</span>
    输出结果:

    >>> from number import Number  #Fetch class from Module
    >>> X = Number(5)              #Number.__init__(X,5)
    >>> Y = X -2                   #Number.__sub__(X,2)
    >>> Y.data                     #Y is new Number instance
    3

    【常见的运算符重载】


    【__getitem__拦截索引运算】

    以下的类将返回索引值的平方

    <span style="font-family:SimHei;font-size:14px;">class indexer:
    	def __getitem__(self,index):
    		return index ** 2</span>
    输出结果:

    <span style="font-family:SimHei;font-size:14px;">>>> X = indexer()
    >>> X[2]
    4
    >>> for i in range(5):
    ...   print X[i],
    ...
    0 1 4 9 16</span>
    【__getitem__和__iter__实现迭代】

    <span style="font-family:SimHei;font-size:14px;">class stepper:
    	def __getitem__(self,i):
    		return self.data[i]
    </span>
    输出结果:

    <span style="font-family:SimHei;font-size:14px;">>>> X = stepper()         #X is a stepper object
    >>> X.data = 'spam'
    >>> X[1]                  #Indexing calls __getitem__
    'p'
    >>> for item in X:        #for loops call __getitem__
    ...   print item,         #for index items 0..N
    ...
    s p a m</span>
    其它的一些引用:

    >>> 'p' in X
    True
    >>> [c for c in X]
    ['s', 'p', 'a', 'm']
    >>> map(None,X)
    ['s', 'p', 'a', 'm']
    >>> (a,b,c,d) = X
    >>> a,c,d
    ('s', 'a', 'm')
    >>> list(X);tuple(X);''.join(X)
    ['s', 'p', 'a', 'm']
    ('s', 'p', 'a', 'm')
    'spam'
    >>> X
    <number.stepper instance at 0x00000000025EE088>

    【__getattr__和__setattr__捕捉属性的引用】

    __getattr__方法是拦截属性点号运算。当通过没有定义属性名称和实例进行点号运算时,就会用属性名称为字符串调用这种方法。

    __getattr__样例:

    class empty:
    def __getattr__(self,attrname):
    if attrname == "age":
    return 40
    else:
    raise AttributeError,attrname

    执行结果

    >>> X = empty()
    >>> X.age
    40
    >>> X.name
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "number.py", line 6, in __getattr__
        raise AttributeError,attrname
    AttributeError: name

    __setattr__样例:

    class accesscontrol:
    def __setattr__(self,attr,value):
    if attr == 'age':
    self.__dict__[attr] = value
    else:
    raise AttributeError,attr + 'not allowed'

    执行结果:

    >>> X = accesscontrol()
    >>> X.age = 40
    >>> X.age
    40
    >>> X.name = 'bob'
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "number.py", line 6, in __setattr__
        raise AttributeError,attr + 'not allowed'
    AttributeError: namenot allowed

    【__call__拦截调用】

    当实例调用时,使用__call__方法。

    能够让实例的外观和使用方法类似于函数。

    class Prod:
    def __init__(self,value):
    self.value = value


    def __call__(self,other):
    return self.value * other

    >>> X = Prod(2)
    >>> X(3)
    6

    以下的样例也能提供类似功能:

    class Prod:
    def __init__(self,value):
    self.value = value


    def comp(self,other):
    return self.value * other

  • 相关阅读:
    c# 坑人的发邮件组件
    生成拼音
    FileDb
    WMI tester
    c# 纯代码调用 webservice
    c# 中 利用 CookieContainer 对 Cookie 进行序列化和反序列化校验
    在经过身份验证的服务中不支持跨域 javascript 回调
    c# 使用 namedpipe 通信
    c++ 创建线程以及参数传递
    c#函数地址传入c++
  • 原文地址:https://www.cnblogs.com/zsychanpin/p/6784547.html
Copyright © 2011-2022 走看看