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'
>>>

元类:后续单独开辟一篇来理解。

 

posted on 2014-01-23 11:26  tony_cyou  阅读(343)  评论(0编辑  收藏  举报

导航