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'
>>>
元类:后续单独开辟一篇来理解。