3 When Objects are Alike

来源:互联网 发布:淘宝美国代购推荐 编辑:程序博客网 时间:2024/06/06 03:29

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] = 3print 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 += 1s = Subclass()s.call_me()print(s.num_sub_calls, s.num_left_calls, s.num_right_calls,s.num_base_calls)

这里答案会输出:

left: before superright: before superCalling method on Base ClassCalling method on Right SubclassCalling method on Left SubclassCalling 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,类似“广搜”,一旦找到后就马上返回。



0 0
原创粉丝点击