Python 和 JS 的不同点___类

Python和JS有很多的不同点,最近看到了类,就总结一下在类上它们有什么不同。

__block1__

#Python
__metaclass__=type #新式类
class SongBird:
    song = 'squawk'
    def sing(self):
        print self.song
        
#新建一个实例
bd = SongBird()
bd.sing()
#squawk
__block2__

//JS
//构造函数
function SongBird() {
    this.song = 'squawk';
    this.sing = function(){
        return this.song;
    }
}

SongBird.prototype = {
    sing: function() {
        return this.song
    }
}

//新建一个实例
bd = new SongBird();
bd.sing();
//"squawk"
形式

从形式上看,python类内部的方法必须传self,这是对自身的引用,用来绑定实例,而JS就不一定要传。方法对类内部函数外部变量的调用就是靠self来引用的。而类的属性,对Python来说就好比在函数内定义变量,除非包在子函数内,不用引用self,而JS的属性需要引用this。

创建一个实例时,Python使用bd = SongBird(),而JS使用bd = new SongBird()

Python有自己的一套魔法方法,若要对类进行初始化,会用到__init__(),它在新建实例的时候自动会被调用,可以用这个方法来进行初始化;JS一贯的用或运算来进行初始化,e = e || window.event,或者在新建实例的时候把要初始化的值先传进去。

类的继承

Python中子类可以扩展超类(基类)的定义,通过将其他类名写在class语句后的圆括号内可以指定超类

__block3__

class Filter:
    #魔法方法,新建实例时会自动初始化
    def __init__(self):
        self.blocked = []
        
    def filter(self,sequence):
        #列表推导式,滤除sequence中与self.blocked内容相同的成员
        return [x for x in sequence if x not in self.blocked]

#指定超类Filter,即继承Filter中的方法和属性
class SPAMFilter(Filter):
    def __init__(self):#重写了Filter超类中的__init__方法
        self.blocked =['SPAM']
        
>>>f = Filter()
>>>f.filter([1,2,3])
[1,2,3]

>>>s = SPAMFilter()
>>>s.filter(['SPAM','apple','SPAM','orange','SPAM','SPAM','watermellon'])
['apple','orange','watermellon']

由上面的例子可以看出,s继承了Filter中的filter()方法,子类用了与超类相同名字的方法就会将原来的方法重写,成为自己的方法。Python也可以多重继承,只要在class语句后的圆括号内制定多个超类,用逗号隔开。但是要注意的是,如果超类中有属性或是方法重名了,写在前面的超类会覆盖后面的超类

JS的继承采用原型机制,一个对象的原型继承另一个原型,在继续继承下去就会形成一个原型链。在调用一个方法或属性时,首先从该对象的原型中去找,如果没找到再在上一级原型中找,往复下去知道找到该方法或属性(如果不存在就会返回undefined)。

下图是JS权威指南中关于构造函数,原型和实例的图示。每一次新建一个实例时,它返回的都是以构造函数的原型为原型的一个对象,构造函数中的this其实是对实例的引用。

__block4__

function Animal() {
    this.canEat = true;
}
Animal.prototype = {
    eat: function(){
        if(this.canEat)
            return "Ahaaaa!";
    },
    sing: function(){
        return "mama";
    }
}

function Bird() {
    this.song = "squwak";
}
//以Animal的实例为原型
Bird.prototype = new Animal();
Bird.prototype.sing = function(){
    return this.song;
}

>bd = new Bird();
>bd.sing();
"squwak"
>bd.eat();
"Ahaaaa!"

实例bd调用eat()方法时,首先在自有方法(非继承来的)里找,没有找到该方法。而后又去继承的原型里找,找到Animal的实例,又去该实例的原型里找找到了eat()方法。Animal的原型也可以继承其他的对象,这样就构成了原型链。

重写了父类的方法怎么重新使用?

在代码__block3__和__block4__中子类都有重写父类的方法。如果不再需要这样的方法就没有什么问题,如果说需要,比如说做一些初始化,那子类可能会初始化不充分,有的属性调用时显示undefined或not defined。

那如何重新调用父类被重写的方法呢?

Python有两种方法,调用超类构造方法的未绑定版本,或者使用super函数。

__block5__

#调用超类构造方法的未绑定版本
__metaclass__ = type #使用新式类
class Bird:
    def __init__(self):
        self.hungry = True

    def eat(self):
        if self.hungry:
            print 'Aaah...'
            self.hungry = False
        else:
            print 'No, thanks!'
            
class SongBird(Bird):

    def __init__(self):
        #Bird.__init__(self)
        self.sound = 'squawk'
    
    '''
    def __init__(self):
        super(SongBird,self).__init__()
        self.sound = 'squawk'
    '''

    def sing(self):
        print self.sound

#Bird.__init__(self)未加这句话之前执行结果
>>>sb = SongBird()
>>>sb.sing()
squawk
>>>sb.eat()
AttributeError: SongBird instance has no attribute 'hungry'

#加了Bird.__init__(self)后的执行结果
>>>sb = SongBird()
>>>sb.sing()
squawk
>>>sb.eat()
Aaah...

在调用一个实例的方法时,该方法的self参数会被自动绑定到实例上(即绑定方法)。如果像上面的例子中那样直接调用累的方法(Bird.__init__),实例就不会被绑定,这样就可以自由的提供需要的self参数。

由__block5__的段落注释可以发现,它使用了一个super函数,它只能在新式类中使用,它的参数是当前的类和对象,调用它返回的对象的任何方法都是在调用超类的方法,这时候超类的__init__方法就可以以一个普通的绑定方式被调用。

JS需要调用父类的构造函数,可以用call调用,this指向子类。假设有两个类,子类是Chinese,父类是Person。要在Chinese的实例中调用Person的方法,可按如下方法。第一个参数是this,后面的参数是父类新建实例是需要的参数
Person.call(this[, arg1[, arg2[, ...]]]);

comments powered by Disqus