Chapter 13. OOP子类如果不定义自己的构造器__init__()基类的构造器就会被调用。然而子类如果重写基类的构造器基类的构造器就不会被自动调用了。这样基类的构造器就必须显式地写出来才会被执行。类名通常用大写字母打头这是标准惯例。Python不支持纯虚函数。bound method 和 unbound method:方法必须绑定到一个实例才能被直接调用非绑定的方法可能可以被调用但实例对象一定要明确给出才能保证调用成功。类的属性要知道一个类有哪些属性有2个方法用内建函数dir()访问类的字典属性__dict__内建的vars()函数接受类对象作为参数返回类的__dict__属性的内容。对于任何类C其必有的所有特殊属性如下C.__name__类C的名字字符串C.__doc__类C的文档字符串文档字符串不会被派生类继承)C.__bases__类C的所有父类构成的元组C.__dict__类C的所有属性C.__module__类C定义所在的模块C.__class__实例C所对应的类仅新式类如’type’__init__()和__new__()用户可以对内建类型如字符串、数字进行派生因此需要一种途径来实例化不可变对象比如派生字符串、派生数字等。在这种情况下解释器调用类的__new__()方法一个静态方法并且传入的参数是类实例化操作时生成的。__new__()会调用父类的__new__()来创建对象向上代理。__new__()必须返回一个合法的实例这样解释器在调用__init__()时就可以把这个实例作为self传给它。所以__new__()的执行是在__init__()之前的。__del__()相应的desctructor方法名为__del__(). 由于Python具有垃圾对象回收机制靠引用计数这个函数要直到该实例对象所有的引用都被清除掉后才会执行。不要忘记首先调用父类的__del__()调用del x仅仅表示减少 x 的引用计数并不会去调用__del__()一般不要去实现__del__()类属性与实例属性对实例属性的赋值会创建一个实例属性如果不存在的话但如果类属性中存在同名属性就会有副作用。示例classFoo(object):x1.5fooFoo()printfoo.x# 1.5foo.x2printfoo.x# 2 (实例属性)printFoo.x# 1.5delfoo.xprintfoo.x# 1.5 (类属性)再看一段代码在类属性可变的情况下就有不同了classFoo(object):x{2003:poe2}fooFoo()printfoo.x# {2003: poe2}foo.x[2004]poe3printfoo.x# {2003: poe2, 2004: poe3}printFoo.x# {2003: poe2, 2004: poe3} 竟然一样了delfoo.x# 会报AttributeError因为没有遮蔽掉类属性绑定和方法调用方法只有在其所属的类拥有实例的时候才能被调用。当存在一个实例时方法才被认为是绑定到那个实例的没有实例时方法就是未绑定的。调用非绑定方法需要调用一个还没有任何实例的类中的方法的一个主要场景是在派生一个子类且要覆盖父类的构造方法此时需调用父类中那个想要被覆盖掉的构造方法。classB(A):def__init__(self,name,phone):A.__init__(self)self.namename self.phonephone所以典型的非绑定方式调用函数如下MyClass.method(instance)静态方法(staticmethod) 和 类方法(classmethod)classTestStaticMethod(object):staticmethoddeffoo():printcalling static method foo()classTestClassMethod(object):classmethoddeffoo(cls):printcalling class method foo()printfoo() is part of class: ,cls.__name__ TestStaticMethod.foo()TestClassMethod.foo()执行效果如下 execfile(./1.py) calling static method foo() calling class method foo() foo() is part of class: TestClassMethod方法解释顺序MRO Method Resolution Order经典类使用深度优先算法。新式类使用新的C3算法广度优先子类在前父类在后。新式类有一个__mro__属性告诉你查找顺序是怎样的。类、实例 和 其他对象的内建函数issubclass(son, parent)判断一个类是否是另一个类的子类或子孙类.从Python2.3开始issubclass()的第二个参数可以是一个元组tuple。这时只要第一个参数是给定元组中任何一个类的子类就会返回True.isinstance(obj, cls)如果obj是类cls的一个实例或者是cls的子类的一个实例就返回True.isinstance()也可以使用一个元组作为第二个参数。如果第一个参数是第二个参数元组中的任何一个类的实例就返回True.hasattr(),getattr(),setattr(),delattr()*attr()系列函数可以在各种对象下工作不限于类和实例。第一个参数是需要被处理的对象第二个参数是某属性名字的字符串。hasattr(obj, attr): 检查某个对象是否具有某个特定的属性。getattr(obj, attr[, default]): 取得某个对象的某属性值。setattr(obj, attr, val): 要么加入一个新的属性要么取代一个已存在的属性。delattr(obj, attr): 从一个对象中删除属性。dir(objNone)dir()作用在实例上经典类或新式类时显示实例变量还有实例所在的类以及所有它的基类中定义的方法和类属性dir()作用在类上经典类或新式类时则显示类以及它的所有基类的__dict__中的内容不会显示定义在元类metaclass中的类的属性dir()作用在模块上时显示模块的__dict__的内容dir()不带参数时显示调用者的局部变量。super()另有博文详述。vars(objNone)vars()内建函数与dir()相似只是给定的对象参数都必须有一个__dict__属性。vars()返回一个字典包含了对象存储于其__dict__中的属性key)和值。如果vars()不带参数则显示一个包含本地名字空间的属性key及其值的字典也就是locals().迭代器 (用类实现)先看示例代码randSeq.py:fromrandomimportchoiceclassRandSeq(object):def__init__(self,seq):self.dataseqdef__iter__(self):returnselfdefnext(self):returnchoice(self.data)以上代码中重要的是函数__iter__(), 它仅返回self就正是如何将一个对象声明为迭代器的方式最后调用next()来得到迭代器中连续的值。这个迭代器是一个没有终点的迭代器。__slots__类属性用户可以用__slots__属性代替__dict__基本上__slots__是一个类变量由一个序列型对象组成由所有合法标识构成的实例属性的集合来表示。它可以是一个列表、元组或可迭代对象也可以是标识实例能拥有的唯一的属性的简单字符串。任何试图创建一个其名不在__slots__中的名字的实例属性都将导致AttributeError异常。classSlottedClass(object):__slots__(foo,bar)cSlottedClass()c.xxxdont think soTraceback(most recent call last):Filestdin,line1,in? AttributeError:SlottedClassobjecthas no attributexxx这种特性的主要目的是节约内存。其副作用是某种类型的“安全”它能防止用户随心所欲地动态增加实例的属性。带__slots__的类定义不会包含__dict__了。__getattribute__特殊方法__getattr__()它仅当属性无法在实例的__dict__或 它的类的__dict__或 祖先类的__dict__中找到时才被调用。__getattr__(), 执行对每一个属性的访问无论是否能找到。如果类同时定义了__getattr__和__getattr__()方法除非明确从__getattr__()调用或__getattr__()引发了AttributeError异常否则后者不会被调用。
Python核心编程笔记 - 第13章 面向对象编程 (一)
Chapter 13. OOP子类如果不定义自己的构造器__init__()基类的构造器就会被调用。然而子类如果重写基类的构造器基类的构造器就不会被自动调用了。这样基类的构造器就必须显式地写出来才会被执行。类名通常用大写字母打头这是标准惯例。Python不支持纯虚函数。bound method 和 unbound method:方法必须绑定到一个实例才能被直接调用非绑定的方法可能可以被调用但实例对象一定要明确给出才能保证调用成功。类的属性要知道一个类有哪些属性有2个方法用内建函数dir()访问类的字典属性__dict__内建的vars()函数接受类对象作为参数返回类的__dict__属性的内容。对于任何类C其必有的所有特殊属性如下C.__name__类C的名字字符串C.__doc__类C的文档字符串文档字符串不会被派生类继承)C.__bases__类C的所有父类构成的元组C.__dict__类C的所有属性C.__module__类C定义所在的模块C.__class__实例C所对应的类仅新式类如’type’__init__()和__new__()用户可以对内建类型如字符串、数字进行派生因此需要一种途径来实例化不可变对象比如派生字符串、派生数字等。在这种情况下解释器调用类的__new__()方法一个静态方法并且传入的参数是类实例化操作时生成的。__new__()会调用父类的__new__()来创建对象向上代理。__new__()必须返回一个合法的实例这样解释器在调用__init__()时就可以把这个实例作为self传给它。所以__new__()的执行是在__init__()之前的。__del__()相应的desctructor方法名为__del__(). 由于Python具有垃圾对象回收机制靠引用计数这个函数要直到该实例对象所有的引用都被清除掉后才会执行。不要忘记首先调用父类的__del__()调用del x仅仅表示减少 x 的引用计数并不会去调用__del__()一般不要去实现__del__()类属性与实例属性对实例属性的赋值会创建一个实例属性如果不存在的话但如果类属性中存在同名属性就会有副作用。示例classFoo(object):x1.5fooFoo()printfoo.x# 1.5foo.x2printfoo.x# 2 (实例属性)printFoo.x# 1.5delfoo.xprintfoo.x# 1.5 (类属性)再看一段代码在类属性可变的情况下就有不同了classFoo(object):x{2003:poe2}fooFoo()printfoo.x# {2003: poe2}foo.x[2004]poe3printfoo.x# {2003: poe2, 2004: poe3}printFoo.x# {2003: poe2, 2004: poe3} 竟然一样了delfoo.x# 会报AttributeError因为没有遮蔽掉类属性绑定和方法调用方法只有在其所属的类拥有实例的时候才能被调用。当存在一个实例时方法才被认为是绑定到那个实例的没有实例时方法就是未绑定的。调用非绑定方法需要调用一个还没有任何实例的类中的方法的一个主要场景是在派生一个子类且要覆盖父类的构造方法此时需调用父类中那个想要被覆盖掉的构造方法。classB(A):def__init__(self,name,phone):A.__init__(self)self.namename self.phonephone所以典型的非绑定方式调用函数如下MyClass.method(instance)静态方法(staticmethod) 和 类方法(classmethod)classTestStaticMethod(object):staticmethoddeffoo():printcalling static method foo()classTestClassMethod(object):classmethoddeffoo(cls):printcalling class method foo()printfoo() is part of class: ,cls.__name__ TestStaticMethod.foo()TestClassMethod.foo()执行效果如下 execfile(./1.py) calling static method foo() calling class method foo() foo() is part of class: TestClassMethod方法解释顺序MRO Method Resolution Order经典类使用深度优先算法。新式类使用新的C3算法广度优先子类在前父类在后。新式类有一个__mro__属性告诉你查找顺序是怎样的。类、实例 和 其他对象的内建函数issubclass(son, parent)判断一个类是否是另一个类的子类或子孙类.从Python2.3开始issubclass()的第二个参数可以是一个元组tuple。这时只要第一个参数是给定元组中任何一个类的子类就会返回True.isinstance(obj, cls)如果obj是类cls的一个实例或者是cls的子类的一个实例就返回True.isinstance()也可以使用一个元组作为第二个参数。如果第一个参数是第二个参数元组中的任何一个类的实例就返回True.hasattr(),getattr(),setattr(),delattr()*attr()系列函数可以在各种对象下工作不限于类和实例。第一个参数是需要被处理的对象第二个参数是某属性名字的字符串。hasattr(obj, attr): 检查某个对象是否具有某个特定的属性。getattr(obj, attr[, default]): 取得某个对象的某属性值。setattr(obj, attr, val): 要么加入一个新的属性要么取代一个已存在的属性。delattr(obj, attr): 从一个对象中删除属性。dir(objNone)dir()作用在实例上经典类或新式类时显示实例变量还有实例所在的类以及所有它的基类中定义的方法和类属性dir()作用在类上经典类或新式类时则显示类以及它的所有基类的__dict__中的内容不会显示定义在元类metaclass中的类的属性dir()作用在模块上时显示模块的__dict__的内容dir()不带参数时显示调用者的局部变量。super()另有博文详述。vars(objNone)vars()内建函数与dir()相似只是给定的对象参数都必须有一个__dict__属性。vars()返回一个字典包含了对象存储于其__dict__中的属性key)和值。如果vars()不带参数则显示一个包含本地名字空间的属性key及其值的字典也就是locals().迭代器 (用类实现)先看示例代码randSeq.py:fromrandomimportchoiceclassRandSeq(object):def__init__(self,seq):self.dataseqdef__iter__(self):returnselfdefnext(self):returnchoice(self.data)以上代码中重要的是函数__iter__(), 它仅返回self就正是如何将一个对象声明为迭代器的方式最后调用next()来得到迭代器中连续的值。这个迭代器是一个没有终点的迭代器。__slots__类属性用户可以用__slots__属性代替__dict__基本上__slots__是一个类变量由一个序列型对象组成由所有合法标识构成的实例属性的集合来表示。它可以是一个列表、元组或可迭代对象也可以是标识实例能拥有的唯一的属性的简单字符串。任何试图创建一个其名不在__slots__中的名字的实例属性都将导致AttributeError异常。classSlottedClass(object):__slots__(foo,bar)cSlottedClass()c.xxxdont think soTraceback(most recent call last):Filestdin,line1,in? AttributeError:SlottedClassobjecthas no attributexxx这种特性的主要目的是节约内存。其副作用是某种类型的“安全”它能防止用户随心所欲地动态增加实例的属性。带__slots__的类定义不会包含__dict__了。__getattribute__特殊方法__getattr__()它仅当属性无法在实例的__dict__或 它的类的__dict__或 祖先类的__dict__中找到时才被调用。__getattr__(), 执行对每一个属性的访问无论是否能找到。如果类同时定义了__getattr__和__getattr__()方法除非明确从__getattr__()调用或__getattr__()引发了AttributeError异常否则后者不会被调用。