3 When Objects are Alike

本文深入探讨了Python面向对象编程的基础概念,包括类变量与实例变量的区别、继承机制、内置对象的继承、方法重写及多继承中的调用顺序等问题。

1、class variables && instance variables

class Contact:
    all_contacts = []
    str = ""
    def __init__(self,name,email):
        self.name = name
        self.email = email
        #self.all_contacts.append(self)
        Contact.all_contacts.append(self)

class variables,即类变量,它是类定义的一部分,all_contacts即是,如同C++里的static,是属于类的,所有实例共享这个类变量;instance variables,实例变量,它是属于某个实例的,类中用self.引用的即是。需要注意的时,类变量在引用时一定是用class.classVariable这种方式,而不能用self.classVariable或instance.classVariable,有时这两种方式虽然也是可以的,但更多时间会出错,这涉及到Python变量可变与不可变:

python中,一切都是对象,故不存在传值还是传址,因为全都是传址。而这些变量中,又分为:

  • 可变对象:可变是指它的内容是可变的,有列表、字典等
  • 不可变对象:元组、字符串、数值型等
a = 1 #申请一段内存,值为1,把a指向这段内存
b = a #把b指向a的内存
b = 2 #因为数值型不可变,所以又申请一段内存,值为2,把b指向这段内存
print a is b #False,地址不相同
a = [1,2]
b = a #b指向a指向的内存
b[0] = 3
print a is b #True,因为list的内容是可变的,故还是指向同一段内存
b = [] #类型不可变,故申请一段内存,把b指向这段新的内存,而a不变
print a is b #False


2、继承

class Supplier(Contact):
    def test(self):
        print "YES"

在类后面的括号里写明父类即表示继承,如果没有写父类,python默认会继承object这个类。同样,如果没有重载__init__这个函数,则默认调用父类的__init__。

3、继承内置对象

现在我们有一个需求:需要在all_contents里找到所有name==v的数据:

class Contact:
    all_contacts = []
    str = ""
    
    def __init__(self,name,email):
        self.name = name
        self.email = email
        self.str = name
        Contact.str = email
        Contact.all_contacts.append(self)
        
    def search(self,name):
        match_contacts = []
        for contact in Contact.all_contacts:
            if contact.name == name:
                match_contacts.append(contact)
        return match_contacts
        
        
c = Contact("moment","moment@gmail.com")
b = Contact("mmm","F")
可以在Contact里加一个search函数,然后通过instance.func的方式调用(不能用class.func调用,会报错,不知道为什么,能用这种方式访问变量,却不能访问函数)。但这个方法属于all_contacts更好,因为它是个list,且不属于任何实例。我们可以这样:

class ContactList(list):
    
    def search(self,name):
        m = []
        for contact in self:
            if name is contact.name:
                m.append(contact)
        return m
    
    
class Contact:
    all_contacts = ContactList()
    
    def __init__(self,name,email):
        self.name = name
        self.email = email
        self.str = name
        Contact.str = email
        Contact.all_contacts.append(self)

我们先构造了一个继承list的类,然后定义了search方法,并将all_contacts变为这个类的实例。

当然,如果有需要,object,list,set,dict,file,str等都可以被继承。

4、override

class Supplier(Contact):
    
    def __init__(self,name,email,phone):
        super(Supplier, self).__init__(name,email)
        self.phone = phone
在重载时,我们可以用super(子类,self)来引用父类的函数。

注意:super函数只能应用于新类,新类是指那些什么都不用继承,那就继承object的类。在上面的例子中,Contact什么都没继承,而其子类Supplier中引用了super,所以会报错:TypeError: must be type, not classobj。只要将Contact继承object即可。
5、多继承

假设Supplier有地址需要存储,我们建立AdderssHolder类:

class AddressHolder:
    def __init__(self, city, street):
        self.city = city
        self.street = street
而Supplier可以这样继承:

class Supplier(Contact, AddressHolder):
    
    def __init__(self,name,email,phone, city, street):
        Contact.__init__(self, name, email)
        AddressHolder.__init__(self, city, street)
        self.phone = phone

在初始化时,我们指定了父类,这种方法有潜在的危险:假如Contact和AddressHolder有同一个父类A,那么父类A的__init__可能会被多次调用,如


因为图形如钻石,故称作diamond inheritance。当然,我们会想到super这个函数,如在__init__里直接super().__init__(),但实验发现,用super会导致调用顺序出问题:

class BaseClass(object):
    num_base_calls = 0
    def call_me(self):
        print("Calling method on Base Class")
        self.num_base_calls += 1
        
class LeftSubclass(BaseClass):
    num_left_calls = 0
    def call_me(self):
        print "left: before super"
        super(LeftSubclass,self).call_me()
        print("Calling method on Left Subclass")
        self.num_left_calls += 1
        
class RightSubclass(BaseClass):
    num_right_calls = 0
    def call_me(self):
        print "right: before super"
        super(RightSubclass,self).call_me()
        print("Calling method on Right Subclass")
        self.num_right_calls += 1
        
class Subclass(LeftSubclass, RightSubclass):
    num_sub_calls = 0
    def call_me(self):
        super(Subclass,self).call_me()
        print("Calling method on Subclass")
        self.num_sub_calls += 1


s = Subclass()
s.call_me()
print(s.num_sub_calls, s.num_left_calls, s.num_right_calls,s.num_base_calls)

这里答案会输出:

left: before super
right: before super
Calling method on Base Class
Calling method on Right Subclass
Calling method on Left Subclass
Calling method on Subclass
(1, 1, 1, 1)

很明显,Base是只被执行了一次。但我们看调用顺序,它是先调用了LeftSubclass的call_me,然后它里面的super却指向了RightSubclass。其实,新式类里的调用顺序可以用__mro__来查看:

print Subclass.__mro__

(<class '__main__.Subclass'>, <class '__main__.LeftSubclass'>, <class '__main__.RightSubclass'>, <class '__main__.BaseClass'>, <type 'object'>)

可以看到,类的调用顺序是Subclass -> Left -> Right -> Base,类似“广搜”,一旦找到后就马上返回。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值