Python基础(9) - 类
Python
看下面一个简单类:
>>> class MyClass(object): ... """ ... this is a class with a doc string ... """ ... pass ... >>> print MyClass.__doc__ this is a class with a doc string
实例化一个类和函数调用的语法是一样的:
>>> myClass = MyClass()
类属性是类的所有对象所共享的属性,它与C++中的static属性类似:
>>> class MyClass(object): ... """ ... this is a class with a doc string ... """ ... str = 'this is a class attribute' ... >>> print MyClass.str this is a class attribute >>> a,b = MyClass(), MyClass() >>> id(a.str) 25889584 >>> id(b.str) 25889584
实例属性是每个对象独有的属性,每个对象的实例属性的值都可能不同:
>>> class MyClass(object): ... ClassData = 'This is a Class Attribute' ... >>> obj1, obj2 = MyClass(), MyClass() >>> obj1.NewData, obj2.NewData = 'New Data of obj1', 'New Data of obj2' >>> id(obj1.NewData) 29413928 >>> id(obj2.NewData) 29413968 >>>
Python中,对象的属性是可以动态增加的。
类的所有对象,都可以读取类属性;
但类属性不能通过对象修改,在修改时,Python会自动为对象创建一个实例属性,并屏蔽掉类属性。
>>> obj1.ClassData = 'Change obj1\'s ClassDate' >>> print obj1.ClassData Change obj1's ClassDate >>> print MyClass.ClassData This is a Class Attribute >>> print obj2.ClassData This is a Class Attribute >>> del obj1.ClassData >>> print obj1.ClassData This is a Class Attribute
类方法是通过类名就可以直接调用的方法,与C++中的static方法类型,函数的第一个参数为类本身,约定俗成将参数命名为cls:
>>> class ClassWithClassMethod(object): ... @classmethod ... def classFunc(cls): ... print 'This is a class method:',id(ClassWithClassMethod),id(cls) ... >>> ClassWithClassMethod.classFunc() This is a class method: 29525656 29525656
实例方法是必须通过对象调用的方法,方法的第一个参数是对象本身,约定俗成将参数命名为self:
>>> ClassWithObjMethod.objFunc() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unbound method objFunc() must be called with ClassWithObjMethod instance as first argument (got nothing instead) >>> >>> obj = ClassWithObjMethod() >>> obj.objFunc() This is a obj method: 29392080 >>> id(obj) 29392080 >>>
Python中,也可以对构造了析构的行为进行定义:
__init__会在对象构造时调用,通常会在这里进行为对象的属性赋值等初始化动作。在__init__中,必须显示的调用父类的__init__方法。
super(type, obj)返回父对象,这行代码也可以用object.__init__(self)替代。
__del__是在对象被Python垃圾回收时调用,因为垃圾回收是由Python控制,所以通常不会重写__del__方法。
>>> class CtorAndDest(object): ... def __init__(self,val): ... super(CtorAndDest, self).__init__() ... self.data = val ... print 'construct an object' ... def __del__(self): ... print 'destroy an object' ... >>> obj = CtorAndDest(100) construct an object >>> obj.data 100 >>> del obj destroy an object
封装、继承、多态是面向对象的三个基本要素,通过继承可以让我们在子类中重用父类的属性、方法。
Python支持多继承,多个父类间以逗号分隔。如果子类不重写__init__方法,在构造时会自动调用父类的__init__。
>>> class ParentClass(object): ... def __init__(self): ... self.data = 100 ... print 'ParentClass constructor' ... def fonc(self): ... print 'Parent function' ... >>> class AnotherParentClass(object): ... def func(self): ... print 'another parent function' ... >>> class ChildClass(ParentClass, AnotherParentClass): ... pass ... >>> >>> >>> obj = ChildClass() ParentClass constructor >>> obj.fonc() Parent function >>> obj.func() another parent function >>> obj.data 100
类或对象中的数据,通常需要有不同的作用域。面向对象开发时不建议由客户直接访问对象的数据,而是由对象提供的方法进行访问,以便对象数据在修改时不影响客户代码。
Python对没有像提供C++一样的public/protected/private关键字,不过可以通过下划线的使用达到一定的可见性控制。
不以下划线开关的普通属性,相当于C++中的public,所有对象都可以访问。双下划线__开头的属性,代表是私有属性,无法被其它对象访问。
>>> class VisibleClass(object): ... def __init__(self): ... super(VisibleClass, self).__init__() ... self.publicData = 'This is public data' ... self.__privateData = 'This is private data' ... >>> class SubClass(VisibleClass): ... def func(self): ... print self.publicData,self.__privateData ... >>> vobj = VisibleClass() >>> vobj.publicData 'This is public data' >>> vobj.__privateData Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'VisibleClass' object has no attribute '__privateData' >>> >>> obj = SubClass() >>> obj.func() This is public data Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in func AttributeError: 'SubClass' object has no attribute '_SubClass__privateData' >>>
所谓多态,可以简单理解为子类使用与父类相同的接口,但提供不同的功能。通过在子类中重写父类中定义的方法,可以实现多态。
>>> class SortClass(object): ... def sort(self,lst): ... pass ... >>> class SwapSortClass(SortClass): ... def sort(self,lst): ... print 'This is Swap sort' ... >>> class QuickSortClass(SortClass): ... def sort(self,lst): ... print 'This is quick sort' ... >>> def sort(lst, algo = SwapSortClass()): ... return algo.sort(lst) ... >>> sort([1,3,2]) This is Swap sort >>> sort([1,3,2], QuickSortClass()) This is quick sort >>>
在上例中,由SortClass定义sort接口,子类中重写对应的方法提供不同的排序算法。在sort函数中,可以直接通过指定不同的参数使用不同的算法,而不需要修改sort函数的实现,代码可扩展性更强。
因为Python是弱类型的语言,所以上例中Sort类并不是必须的,实际不需要通过子类重写,只要传入的对象提供了相同的接口就可以达到相同的目的。
内置函数dir()可以查看对象/模块的属性列表,对象的__dict__则显示对象的所有属性及属性的值(等价于内置函数vars):
通过__dict__的例子可以看到,对象的所有属性及值是以dict的形式保存在对象的__dict__属性中的,而不是像C++一样,对象的所有属性是在内存中连续分布的。带来的好处是可以为对象动态的增加和删除属性,并可以通过名字方便的访问这些属性;坏处是会占用更大的内存,不过可以通过__slots__使对象属性固定下来,并减小内存占用。
>>> obj = DictClass() >>> import sys >>> sys.getsizeof(obj) 32 >>> obj.a = 1 >>> sys.getsizeof(obj) 32 >>> obj.__dict__['b'] = 2 >>> obj.a 1 >>> obj.b 2 >>>
注意:不要像例子中一样通过__dict__访问对象的属性!
>>> class SlotsClass(object): ... __slots__ = () ... >>> import sys >>> sys.getsizeof(SlotsClass())
getattr可以获取一个对象的某个属性的值,如果属性不存在,可以返回一个给定的默认值;
setattr用来设置对象属性的值,属性不存在时会为对象增加属性;
delattr用来删除对象的属性。
hasattr用来判断对象是否存在某个属性。
这四个方法事实上都是通过操作对象的__dict__来进行的。
Isinstance方法可以用来确定一个对象是不是一个类或它的子类的实例:
还可以使用type(obj) is Type进行判断,此方法对于obj是Type的子类的实例的情况将返回False。
Issubclass用来判断一个类是否是另一个类的子类,包括直接子类或间接子类。
>>> isinstance(0,int) True >>> isinstance(True,bool) True >>> bool.__bases__ (<type 'int'>,) >>> obj = QuickSortClass() >>> isinstance(obj,QuickSortClass) True >>> isinstance(obj,SortClass) True >>> type(obj) is QuickSortClass True >>> type(obj) is SortClass False >>>
>>> issubclass(bool,int) True >>> issubclass(type,object) True >>> issubclass(str,object) True >>> issubclass(SwapSortClass,SortClass) True >>> SwapSortClass.__bases__ (<class '__main__.SortClass'>,) >>>
Python为对象提供了一系统的方法重载,以改变对象在执行某此操作时的行为。下面提供了一个简单的示例,展示了如何使对象支持+, +=, <, if判断等操作。
可以在《Python核心编程》等资料中找到Python支持的特殊方法的全集。
数据描述符:(类似于C#中的属性)
重写了__get__和__set__方法的对象被称为数据描述符。数据描述符可以用于对象属性访问的封装和控制,如下例:
>>> class DataDescripterA(object): ... def __init__(self): ... self.data = None ... def __get__(self,attr,type=None): ... return 'returned by __get__:%s'%self.data ... def __set__(self,attr,val): ... if isinstance(val,str): ... self.data = val ... else: ... assert False,'Must set to string' ... >>> class Sample(object); File "<stdin>", line 1 class Sample(object); ^ SyntaxError: invalid syntax >>> class Sample(object): ... desc = DataDescripterA() ... >>> obj = Sample() >>> obj.desc = 10 Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 10, in __set__ AssertionError: Must set to string >>> >>> >>> obj.desc = 'a string' >>> obj.desc 'returned by __get__:a string' >>>
元类:后续单独开辟一篇来理解。