Python入门篇之面向对象


Posted in Python onOctober 20, 2014

面向对象设计与面向对象编程的关系
 
面向对象设计(OOD)不会特别要求面向对象编程语言。事实上,OOD 可以由纯结构化语言来实现,比如 C,但如果想要构造具备对象性质和特点的数据类型,就需要在程序上作更多的努力。当一门语言内建 OO 特性,OO 编程开发就会更加方便高效。另一方面,一门面向对象的语言不一定会强制你写 OO 方面的程序。例如 C++可以被认为“更好的C”;而 Java,则要求万物皆类,此外还规定,一个源文件对应一个类定义。然而,在 Python 中,类和 OOP 都不是日常编程所必需的。尽管它从一开始设计就是面向对象的,并且结构上支持 OOP,但Python 没有限定或要求你在你的应用中写 OO 的代码。OOP 是一门强大的工具,不管你是准备进入,学习,过渡,或是转向 OOP,都可以任意支配。考虑用 OOD 来工作的一个最重要的原因,在于它直接提供建模和解决现实世界问题和情形的途径。
 

 
类是一种数据结构,我们可以用它来定义对象,后者把数据值和行为特性融合在一起。类是现实世界的抽象的实体以编程形式出现。实例是这些对象的具体化。可以类比一下,类是蓝图或者模型,用来产生真实的物体(实例)。类还可以派生出相似但有差异的子类。编程中类的概念就应用了很多这样的特征。在 Python 中,类声明与函数声明很相似,头一行用一个相应的关键字,接下来是一个作为它的定义的代码体,如下所示:

def functionName(args): 

    'function documentation string'  #函数文档字符串 

     function_suite  #函数体 

class ClassName(object): 

    'class documentation string'  #类文档字符串 

     class_suite   #类体 

二者都允许你在他们的声明中创建函数,闭包或者内部函数(即函数内的函数),还有在类中定义的方法。最大的不同在于你运行函数,而类会创建一个对象。类就像一个 Python 容器类型。尽管类是对象(在 Python 中,一切皆对象),但正被定义时,它们还不是对象的实现。
 
创建类
 
Python 类使用 class 关键字来创建。简单的类的声明可以是关键字后紧跟类名:

class ClassName(bases): 

    'class documentation string' #'类文档字符串' 

    class_suite #类体 

基类是一个或多个用于继承的父类的集合;类体由所有声明语句,类成员定义,数据属性和函数组成。类通常在一个模块的顶层进行定义,以便类实例能够在类所定义
 
的源代码文件中的任何地方被创建。
 
声明与定义
对于 Python 函数来说,声明与定义类没什么区别,因为他们是同时进行的,定义(类体)紧跟在声明(含 class 关键字的头行[header line])和可选的文档字符串后面。同时,所有的方法也必须同时被定义。如果对 OOP 很熟悉,请注意 Python 并不支持纯虚函数(像 C++)或者抽象方法(如在 JAVA 中),这些都强制程序员在子类中定义方法。作为替代方法,你可以简单地在基类方法中引发 NotImplementedError 异常,这样可以获得类似的效果。
 
类属性
 
属性就是属于另一个对象的数据或者函数元素,可以通过我们熟悉的句点属性标识法来访问。一些 Python 类型比如复数有数据属性(实部和虚部),而另外一些,像列表和字典,拥有方法(函数属性)。

有关属性的一个有趣的地方是,当你正访问一个属性时,它同时也是一个对象,拥有它自己的属性,可以访问,这导致了一个属性链,比如,myThing,subThing,subSubThing.等等
 
类的数据属性
 
数据属性仅仅是所定义的类的变量。它们可以像任何其它变量一样在类创建后被使用,并且,要么是由类中的方法来更新,要么是在主程序其它什么地方被更新。
这种属性已为 OO 程序员所熟悉,即静态变量,或者是静态数据。它们表示这些数据是与它们所属的类对象绑定的,不依赖于任何类实例。如果你是一位 Java 或 C++程序员,这种类型的数据相当于在一个变量声明前加上 static 关键字。静态成员通常仅用来跟踪与类相关的值。
 
看下面的例子,使用类数据属性(foo):

>>> class c(object):

    foo = 100

>>> print c.foo

100

>>> c.foo+=1

>>> c.foo

101

方法
 

>>> class MyClass(object): 

        def myNoActionMethod(self): 

        pass 

>>> mc = MyClass() 

>>> mc.myNoActionMethod()         

任何像函数一样对 myNoActionMethod 自身的调用都将失败:

>>> myNoActionMethod() Traceback (innermost last): 

File "<stdin>", line 1, in ? 

myNoActionMethod() NameError: myNoActionMethod

甚至由类对象调用此方法也失败了。

>>> MyClass.myNoActionMethod() Traceback (innermost last): 

File "<stdin>", line 1, in ? 

MyClass.myNoActionMethod() 

TypeError: unbound method must be called with class 

instance 1st argument

绑定(绑定及非绑定方法)
为与 OOP 惯例保持一致,Python 严格要求,没有实例,方法是不能被调用的。这种限制即 Python所描述的绑定概念(binding),在此,方法必须绑定(到一个实例)才能直接被调用。非绑定的方法可能可以被调用,但实例对象一定要明确给出,才能确保调用成功。然而,不管是否绑定,方法都是它所在的类的固有属性,即使它们几乎总是通过实例来调用的。
 
决定类的属性
 
要知道一个类有哪些属性,有两种方法。最简单的是使用 dir()内建函数。另外是通过访问类的字典属性__dict__,这是所有类都具备的特殊属性之一。
 
看一下下面的例子:
 

>>> class myclass(object):

    'myclass class definition' #类定义

    myVersion = '1.1'          #静态数据

    def showVesion(self):      #方法

        print myclass.myVersion

        

>>> dir(myclass)

运行结果:

['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'myVersion', 'showVesion']

使用:

>>> myclass.__dict__ 

dict_proxy({'__module__': '__main__', 'showVesion': <function showVesion at 0x0134C9B0>, '__dict__': <attribute '__dict__' of 'myclass' objects>, 'myVersion': '1.1', '__weakref__': <attribute '__weakref__' of 'myclass' objects>, '__doc__': 'myclass class definition'})

从上面可以看到,dir()返回的仅是对象的属性的一个名字列表,而__dict__返回的是一个字典,它的键(keys)是属性名,键值(values)是相应的属性对象的数据值。
结果还显示了 MyClass 类中两个熟悉的属性,showMyVersion 和 myVersion,以及一些新的属性。这些属性,__doc__及__module__,是所有类都具备的特殊类属性(另外还有__dict__)。。内建的 vars()函数接受类对象作为参数,返回类的__dict__属性的内容。

特殊的类属性
 
对任何类C,表显示了类C的所有特殊属性: 
C.__name__        类C的名字(字符串)
C.__doc__         类C的文档字符串
C.__bases__       类C的所有父类构成的元组
C.__dict__        类C的属性
C.__module__      类C定义所在的模块(1.5 版本新增)
C.__class__       实例C对应的类(仅新式类中)

>>> myclass.__name__

'myclass'

>>> myclass.__doc__

'myclass class definition'

>>> myclass.__bases__

(<type 'object'>,)

>>> print myclass.__dict__

{'__module__': '__main__', 'showVesion': <function showVesion at 0x0134C9B0>, '__dict__': <attribute '__dict__' of 'myclass' objects>, 'myVersion': '1.1', '__weakref__': <attribute '__weakref__' of 'myclass' objects>, '__doc__': 'myclass class definition'}

>>> myclass.__module__

'__main__'

>>> myclass.__class__

<type 'type'>

实例
 
如果说类是一种数据结构定义类型,那么实例则声明了一个这种类型的变量。实例是那些主要用在运行期时的对象,类被实例化得到实例,该实例的类型就是这个被实例化的类。
 
初始化:通过调用类对象来创建实例
 
Python 的方式更加简单。一旦定义了一个类,创建实例比调用一个函数还容易------不费吹灰之力。实例化的实现,可以使用函数操作符,如下示:
 
>>> class MyClass(object): # define class 定义类
        pass
>>> mc = MyClass() # instantiate class 初始化类    
__init__()"构造器"方法
 
当类被调用,实例化的第一步是创建实例对象。一旦对象创建了,Python 检查是否实现了__init__()方法。默认情况下,如果没有定义(或覆盖)特殊方法__init__(),对实例不会施加任何特别的操作.任何所需的特定操作,都需要程序员实现__init__(),覆盖它的默认行为。
 
如果__init__()没有实现,则返回它的对象,实例化过程完毕。
 
如果__init__()已经被实现,那么它将被调用,实例对象作为第一个参数(self)被传递进去,像标准方法调用一样。调用类时,传进的任何参数都交给了__init__()。实际中,你可以想像成这样:把创建实例的调用当成是对构造器的调用。
 
__new__()“构造器”方法
 
与__init__()相比,__new__()方法更像一个真正的构造器。需要一种途径来实例化不可变对象,比如,派生字符串,数字,等等。在这种情况下,解释器则调用类的__new__()方法,一个静态方法,并且传入的参数是在类实例化操作时生成的。__new__()会调用父类的__new__()来创建对象(向上代理)。__new__()必须返回一个合法的实例。
 
__del__()"解构器"方法
 
同样,有一个相应的特殊解构器(destructor)方法名为__del__()。然而,由于 Python 具有垃圾对象回收机制(靠引用计数),这个函数要直到该实例对象所有的引用都被清除掉后才会执行。Python 中的解构器是在实例释放前提供特殊处理功能的方法,它们通常没有被实现,因为实例很少被显式释放。
 
注意:Python 没有提供任何内部机制来跟踪一个类有多少个实例被创建了,或者记录这些实例是些什么东西。如果需要这些功能,你可以显式加入一些代码到类定义或者__init__()和__del__()中去。最好的方式是使用一个静态成员来记录实例的个数。靠保存它们的引用来跟踪实例对象是很危险的,因为你必须合理管理这些引用,不然,你的引用可能没办法释放(因为还有其它的引用)!看下面一个例子:
 

>>> class instCt(object):

    count = 0

    def __init__(self):

        instCt.count += 1

    def __del__(self):

        instCt.count -= 1

    def howMany(self):

        return instCt.count

    

>>> a = instCt()

>>> b = instCt()

>>> b.howMany()

2

>>> a.howMany()

2

>>> del b

>>> a.howMany()

1

>>> del a

>>> instCt.count

0

实例属性
 
设置实例的属性可以在实例创建后任意时间进行,也可以在能够访问实例的代码中进行。构造器__init()__是设置这些属性的关键点之一
 
能够在“运行时”创建实例属性,是 Python 类的优秀特性之一,Python 不仅是动态类型,而且在运行时,允许这些对象属性的动态创建。这种特性让人爱不释
手。当然,创建这样的属性时,必须谨慎。一个缺陷是,属性在条件语句中创建,如果该条件语句块并未被执行,属性也就不存在,而你在后面的代码中试着去访问这些属性,就会有错误发生。
 
默认参数提供默认的实例安装
在实际应用中,带默认参数的__init__()提供一个有效的方式来初始化实例。在很多情况下,默认值表示设置实例属性的最常见的情况,如果提供了默认值,我们就没必要显式给构造器传值了。
 

>> class HotelRoomCalc(object):

    'hotel room rate calculate'

    def __init__(self, rt, sales = 0.085, rm = 0.1):

        '''HotelRoomCalc default arguments:

                 sales tax == 8.5% and room tax == 10%'''

        self.salesTax = sales

        self.roomTax = rm

        self.roomRate = rt

    def calcTotal(self, days = 1):

        'Calculate total: default to daily rate'

        daily = round((self.roomRate * 14 * (1+self.roomTax + self.salesTax)),2)

        return float(days) * daily
>>> sfo = HotelRoomCalc(299)

>>> sfo.calcTotal()

4960.41

>>> sfo.calcTotal(2)

9920.82

>>> sea = HotelRoomCalc(189, 0.086, 0.085)

>>> sea.calcTotal()

3098.47

>>> sea.calcTotal(4)

12393.88

函数所有的灵活性,比如默认参数,也可以应用到方法中去。在实例化时,可变长度参数也是一个好的特性
 
__init__()应当返回 None
采用函数操作符调用类对象会创建一个类实例,也就是说这样一种调用过程返回的对象就是实例,下面示例可以看出:

>>> class MyClass(object):

    pass
>>> mc = MyClass()

>>> mc

<__main__.MyClass object at 0x0134E610>

如果定义了构造器,它不应当返回任何对象,因为实例对象是自动在实例化调用后返回的。相应地,__init__()就不应当返回任何对象(应当为 None);否则,就可能出现冲突,因为只能返回实例。试着返回非 None 的任何其它对象都会导致 TypeError 异常:
 

>>> class MyClass:

    def __init__(self):

        print 'initialized'

        return 1
>>> mc = MyClass()

initialized

Traceback (most recent call last):

  File "<pyshell#86>", line 1, in <module>

    mc = MyClass()

TypeError: __init__() should return None

查看实例属性
 
内建函数 dir()可以显示类属性,同样还可以打印所有实例属性:

>>> c = C()

>>> c.foo = 'he'

>>> c.bar = 'isa'

>>> dir(c)

['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bar', 'foo']

与类相似,实例也有一个__dict__特殊属性(可以调用 vars()并传入一个实例来获取),它是实例属性构成的一个字典:

>>> c.__dict__

{'foo': 'he', 'bar': 'isa'}

特殊的实例属性
 
实例仅有两个特殊属性。对于任意对象I:
I.__class__      实例化 I 的类
I.__dict__       I 的属性

>>> class C(object):

    pass

>>> c = C()

>>> dir(c)

['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']

>>> c.__dict__

{}

>>> c.__class__

<class '__main__.C'>>>> #可以看到,c还没有属性

>>> c.foo = 1

>>> c.bar = 'ewe'

>>> '%d can of %s please' % (c.foo, c.bar)

'1 can of ewe please'

>>> c.__dict__

{'foo': 1, 'bar': 'ewe'}

内建类型属性
 
内建类型也是类,对内建类型也可以使用dir(),与任何其它对象一样,可以得到一个包含它属性名字的列表:
 

>>> x = 2 + 2.4j

>>> x.__class__

<type 'complex'>

>>> dir(x)

['__abs__', '__add__', '__class__', '__coerce__', '__delattr__', '__div__', '__divmod__', '__doc__', '__eq__', '__float__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__int__', '__le__', '__long__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__nonzero__', '__pos__', '__pow__', '__radd__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rmod__', '__rmul__', '__rpow__', '__rsub__', '__rtruediv__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', 'conjugate', 'imag', 'real']

试着访问__dict__会失败,因为在内建类型中,不存在这个属性
 
实例属性 vs 类属性
 
类属性仅是与类相关的数据值,和实例属性不同,类属性和实例无关。这些值像静态成员那样被引用,即使在多次实例化中调用类,它们的值都保持不变。不管如何,静态成员不会因为实例而改变它们的值,除非实例中显式改变它们的值。类和实例都是名字空间。类是类属性的名字空间,实例则是实例属性的。
关于类属性和实例属性,还有一些方面需要指出。可采用类来访问类属性,如果实例没有同名的属性的话,你也可以用实例来访问。

访问类属性
类属性可通过类或实例来访问。下面的示例中,类 C 在创建时,带一个 version 属性,这样通过类对象来访问它是很自然的了,比如,C.version
 

>>> class C(object):

    version = 2
>>> c = C()

>>> C.version

2

>>> c.version

2

>>> C.version += 2

>>> C.version

4

>>> c.version

4

从实例中访问类属性须谨慎
 
与通常 Python 变量一样,任何对实例属性的赋值都会创建一个实例属性(如果不存在的话)并且对其赋值。如果类属性中存在同名的属性,副作用即产生。
 

>>> class Foo(object):

    x = 1
>>> foo =Foo()

>>> foo.x

1

>>> foo.x = 2

>>> Foo.x

1

使用del后
 

>>> del foo.x

>>> foo.x

1

静态成员,如其名所言,任凭整个实例(及其属性)的如何进展,它都不理不采(因此独立于实例)。同时,当一个实例在类属性被修改后才创建,那么更新的值就将生效。类属性的修改会影响到所有的实例:
 

>>> class C(object):

    spam = 11

    

>>> c1 = C()

>>> c1.spam

11

>>> C.spam += 2

>>> C.spam

13

>>> c1.spam

13

>>> c2 = C()

>>> c2.spam

13

>>> del c1

>>> C.spam += 3

>>> c2.spam

16

正如上面所看到的那样,使用实例属性来试着修改类属性是很危险的。原因在于实例拥有它们自已的属性集,在 Python 中没有明确的方法来指示你想要修改同名的类属性,修改类属性需要使用类名,而不是实例名。
 
静态方法和类方法
 
静态方法和类方法在 Python2.2 中引入。经典类及新式(new-style)类中都可以使用它。一对内建函数被引入,用于将作为类定义的一部分的某一方法声明“标记”(tag),“强制类型转换”(cast)或者“转换”(convert)为这两种类型的方法之一。
 
现在让我们看一下在经典类中创建静态方法和类方法的一些例子:
 

>>> class TestStaticMethod:

    def foo():

        print 'calling static method foo()'

    foo = staticmethod(foo)
>>> class TestClassMethod:

    def foo(cls):

        print 'calling class method foo()'

        print 'foo() is part of class:', cls.__name__

    foo = classmethod(foo)

对应的内建函数被转换成它们相应的类型,并且重新赋值给了相同的变量名。如果没有调用这两个函数,二者都会在 Python 编译器中产生错误,显示需要带 self 的常规方法声明。
 

>>> tsm = TestStaticMethod()

>>> TestStaticMethod.foo()

calling static method foo()

>>> tsm.foo()

calling static method foo()

>>> tcm = TestClassMethod()

>>> TestClassMethod.foo()

calling class method foo()

foo() is part of class: TestClassMethod

>>> tcm.foo()

calling class method foo()

foo() is part of class: TestClassMethod

使用函数修饰符:
 
在 Python2.4 中加入的新特征。你可以用它把一个函数应用到另个函数对象上, 而且新函数对象依然绑定在原来的变量。我们正是需要它来整理语法。通过使用 decorators,我们可以避免像上面那样的重新赋值:
 

>>> class TestStaticMethod:

    @staticmethod

    def foo():

        print 'calling static method foo()'
>>> class TestClassMethod:

    @classmethod

    def foo(cls):

        print 'calling class method foo()'

        print 'foo() is part of class:', cls.__name__
Python 相关文章推荐
python 判断一个进程是否存在
Apr 09 Python
Python使用Supervisor来管理进程的方法
May 28 Python
python实现图片变亮或者变暗的方法
Jun 01 Python
通过mod_python配置运行在Apache上的Django框架
Jul 22 Python
python实现树形打印目录结构
Mar 29 Python
Python中max函数用于二维列表的实例
Apr 03 Python
Python实现将数据写入netCDF4中的方法示例
Aug 30 Python
python数据挖掘需要学的内容
Jun 23 Python
pandas中的数据去重处理的实现方法
Feb 10 Python
Python图像处理库PIL的ImageEnhance模块使用介绍
Feb 26 Python
pycharm中导入模块错误时提示Try to run this command from the system terminal
Mar 26 Python
浅析Python的命名空间与作用域
Nov 25 Python
Python入门篇之数字
Oct 20 #Python
Python入门篇之正则表达式
Oct 20 #Python
Python入门篇之文件
Oct 20 #Python
Python入门篇之函数
Oct 20 #Python
Python入门篇之条件、循环
Oct 17 #Python
Python入门篇之字典
Oct 17 #Python
Python入门篇之字符串
Oct 17 #Python
You might like
zend api扩展的php对象的autoload工具
2011/04/18 PHP
比较时间段一与时间段二是否有交集的php函数
2011/05/31 PHP
PHP与javascript实现变量交互的示例代码
2013/07/23 PHP
PHP开发中csrf攻击的简单演示和防范
2017/05/07 PHP
Javascript 代码也可以变得优美的实现方法
2009/06/22 Javascript
javascript prototype原型操作笔记
2009/12/07 Javascript
javascript nextSibling 与 getNextElement(node) 使用介绍
2011/10/13 Javascript
JavaScript之编码规范 推荐
2012/05/23 Javascript
JavaScript中的比较操作符&gt;、=、
2014/12/31 Javascript
Jquery获取第一个子元素简单实例
2016/06/02 Javascript
JS实现动态表格的添加,修改,删除功能(推荐)
2016/06/15 Javascript
vue制作加载更多功能的正确打开方式
2016/10/12 Javascript
jQuery插件HighCharts绘制的基本折线图效果示例【附demo源码下载】
2017/03/07 Javascript
bootstrap常用组件之头部导航实现代码
2017/04/20 Javascript
新版小程序登录授权的方法
2018/12/12 Javascript
VuePress 中如何增加用户登录功能
2019/11/29 Javascript
vue使用echarts图表自适应的几种解决方案
2020/12/04 Vue.js
Python中绑定与未绑定的类方法用法分析
2016/04/29 Python
python实现多张图片拼接成大图
2019/01/15 Python
详解使用python绘制混淆矩阵(confusion_matrix)
2019/07/14 Python
Python使用指定端口进行http请求的例子
2019/07/25 Python
django的csrf实现过程详解
2019/07/26 Python
在django中图片上传的格式校验及大小方法
2019/07/28 Python
python3安装OCR识别库tesserocr过程图解
2020/04/02 Python
python基于socket函数实现端口扫描
2020/05/28 Python
详解CSS3的perspective属性设置3D变换距离的方法
2016/05/23 HTML / CSS
PHP如何自定义函数
2016/09/16 面试题
保护环境的标语
2014/06/09 职场文书
2014年初一班主任工作总结
2014/11/08 职场文书
2015年端午节国旗下演讲稿
2015/03/19 职场文书
学校办公室主任岗位职责
2015/04/01 职场文书
2016学习雷锋精神活动倡议书
2015/04/27 职场文书
企业安全生产规章制度
2015/08/06 职场文书
宣传委员竞选稿
2015/11/19 职场文书
毕业生求职自荐信(2016最新版)
2016/01/28 职场文书
 Redis 串行生成顺序编码的方法实现
2022/04/03 Redis