本文实例讲述了Python面向对象程序设计OOP。分享给大家供大家参考,具体如下:

类是Python所提供的最有用的的工具之一。合理使用时,类可以大量减少开发的时间。类也在流行的Python工具中使用,例如,tkinter GUI API。

为何使用类

与面向对象的Java一样,类是对现实世界的一种抽象。

从更具体的程序设计观点来看,类是Python的程序组成单元,就像函数和模块一样:类是封装逻辑和数据的另一种方式。实际上,类也定义新的命名空间,在很大程度上就像模块。但是类有三个重要的独到之处,使其在建立对象时更为有用。

1.多重实例

类基本上就是产生对象的工厂。每次调用一个类,就会产生一个有独立命名空间的新对象。每个又类产生的对象都能读取类的属性,并获得自己的命名空间来储存数据,这些数据对于每个对象来说都不同。

2.通过继承进行定制

类也支持OOP的继承的概念。我们可以在类的外部重新定义其属性从而扩充这个类。

3.运算符重载

通过提供特定的协议方法,类可以定义对象来响应在内置类型上的几种运算。例如,通过类创建的对象可以进行切片,级联和索引等运算。Python提供了一些可以由类使用的钩子,从而能够中断并实现任何的内置类型运算。

Python的类与Java类似,所以关于继承、属性和方法以及类和实例的基础知识在这里不再赘述。直接介绍语法内容。

类产生多个实例对象

【OOP模型中的两种对象:类对象和实例对象】

类对象提供默认行为,是实例对象的工厂。实例对象是程序处理的实际对象:各自都有独立的命名空间,但是继承(可自动存取)创建该实例的类中的变量名。类对象来自于语句,而实例来自于调用。每次调用一个类,就会得到这个类的新的实例。

【类对象提供默认行为】

执行class语句,就会得到类对象。以下是Python类的主要特性:

1.class语句创建类对象并将其赋值给变量名。就像函数def语句,Python class语句也是可执行语句,执行时,会产生新的类对象,并将其赋值给class头部的变量名。此外,就像def应用,class语句一般是在其所在文件导入时执行的。

2.class语句内的赋值语句会创建类的属性。class语句内的顶层的赋值语句会产生类对象中的属性。从技术角度讲,class语句的作用域会变成类对象的属性的命名空间,就像模块的全局作用域一样。执行class语句后,类的属性可由变量名点号运算获取object.name。

3.类属性提供对象的状态和行为。类对象的属性记录状态信息和行为,可由这个类创建的所有实例共享。位于类中的函数def语句会生成方法,方法将会处理实例。

【实例对象是具体元素】

当调用类对象时,我们得到了实例对象。以下是类的实例内含的重点概要。

1.像函数那样调用类对象会创建新的实例对象。实例代表了程序领域中的具体元素。

2.每个实例对象继承类的属性并获得了自己的命名空间。由类所创建的实例对象是新的命名空间。一开始是空的,但是会继承创建该实例的类对象内的属性。

3.在方法内对self属性做赋值运算会产生每个实例自己的属性。在类方法函数内,第一个参数(self)会引用正处理的实例对象。对self的属性做赋值运算,会创建或修改实例内的数据,而不是类的数据。

第一个例子

首先定义一个名为FirstClass的类,通过交互模式运行Python class 语句。

>>> class FirstClass:

def setdata(self,value):

self.data = value

def display(self):

print(self.data)

这里是在交互模式下工作,但一般来说,这种语句应该是当其所在的模块文件导入时运行的。就像通过def建立的函数,这个类在Python抵达并执行语句前是不会存在的。

就像所有的复合语句一样,class开头一行会列出类的名称,后面再接一个或多个内嵌并且缩进的语句的主体。

就像之前学过的,def其实是赋值运算。在这里把函数对象赋值给变量名setdata,而display位于class语句范围内,因此会产生附加在类上的属性:FirstClass.setdata和FirstClass.display。事实上,在类嵌套的代码块中顶层的赋值的任何变量名,都会变成类的属性。

位于类中的函数通常称为方法。方法时普通def,支持先去学过的函数的所有内容。在方法函数中,调用时,第一个参数(self)自动接收隐含的实例对象:调用的主体。下面建立这个类两个实例:

>>> x = FirstClass()

>>> y = FirstClass()

以此方式调用类时,【注意后面有小括号】,会产生实例对象。确切的讲,此时有三个对象:两个实例和一个类。

这两个实例一开始是空的,但是它们被连接到创建它们的类。如果对实例以及类对象内的属性名称进行点号运算,Python会通过继承搜索从类取得变量名。

>>> x.setdata('King Arthur')

>>> y.setdata(3.14159)

x或y本身都没有setdata属性,为了寻找这个属性,Python会顺着实例到类的连接搜索。而这就是所谓的Python的继承:继承是在属性点号运算时发生的,而且只与查找连接对象内的变量名有关。

在FirstClass的setdata函数中,传入的值会赋给self.data。在方法中,self(按惯例,这是最左侧参数的名称)会自动引用正在处理的实例(x或y),所以赋值语句会把值储存在实例的命名空间,而不是类的命名空间。

因为类会产生多个实例,方法必须经过self参数才能获取正在处理的实例。当调用类的display方法来打印self.data时,会发现每个实例的值都不同。另外,变量名display在x和y之内都相同,因为它是来自于类的:

>>> x.display()

King Arthur

>>> y.display()

3.14159

注意:在每个实例内的data成员储存了不同对象类型(字符串和浮点数)。就像Python中的其他事物,实例属性并没有声明。首次赋值后,实例就会存在,就像简单的变量。事实上,如果在调用setdata之前,就对某一实例调用display,则会触发未定义变量名的错误:data属性以setdata方法赋值前,是不会在内存中存在的。

我们可以在类的内部或外部修改实例属性。在类内时,通过方法对self进行赋值运算,而在类外时,则可以通过对实例对象进行赋值运算:

>>> x.data = 'New value'

>>> x.display()

New value

虽然比较少见,通过在类方法函数外对变量名进行赋值运算,我们甚至可以在实例命名空间内产生全新的属性:

>>> x.anothername = 'spam'

这里会增加一个名为anothername的新属性,实例对象x的任何类方法都可以使用它。

不过,类通常是通过self参数进行赋值运算从而建立实例的所有属性的。

类通过继承进行定制

除了作为工厂来生成多个实例对象之外,类也可以引入新组件(子类)来进行修改,而不对现有组件进行原地的修改。由类产生的实例对象会继承该类的属性。

在Python中,实例从类中继承,而类继承于超类。以下是属性继承机制的核心观点:

1.超类列在了类开头的括号中。含有继承的类称为子类,而子类所继承的类就是其超类。

2.类从其超类中继承属性。就像实例继承其类中所定义的属性名一样,类也会继承其超类中定义的所有属性名称。当读取属性时,如果它不存在于子类中,Python会自动搜索这个属性。

3.每个object.attribute都会开启新的独立搜索。

4.逻辑的修改是通过创建子类,而不是修改超类。在树中层次较低的子类中重新定义超类的变量名,子类就可以取代并定制所继承的行为。

第二个例子

下个例子建立在上一个例子的基础之上。首先,定义一个新的类SecondClass,继承FirstClass所有变量名,并提供自己的一个变量名。

>>> class secondClass(FirstClass):

def display(self):

print('Current value = " %s "'%self.data)

SecondClass定义display方法以不同格式打印。定义一个和FirstClass中的属性同名的属性,SecondClass有效地取代其超类内的display属性。因为继承搜索会从实例向上进行,之后到子类,然后到超类,直到所找到的属性名称首次出现为止。

有时候,我们把这种在树中较低处发生的重新定义的、取代属性的动作称为【重载】。结果就是,SecondClass改变了display的行为,把FirstClass特定化了。另外,SecondClass(以及其任何实例)依然会继承FirstClass的setdata方法:

>>> z = SecondClass()

>>> z.setdata(42)

>>> z.display()

Current value = " 42 "

这里有一个和OOP相关的很重要的事情要留意:SecondClass引入的专有化完全是在FirstClass外部完成的。也就是说,不影响当前存在的或未来的FirstClass对象,就像上一个例子中的x:

>>> x.display()

New value

类是模块内的属性

类的名称没有什么神奇之处。当class语句执行时,这只是赋值给对象的变量,而对象可以用任何普通表达式引用。

例如,如果FirstClass是写在模块文件内,而不是在交互模式下输入的,就可将其导入,在类开头的那行可以正常地使用它的名称。

from modulename import FirstClass

class SecondClass(FirstClass):

def display(self):...

或者,其等效写法:

import modulename

class SecondClass(module.FirstClass):

def display():...

像其他事物一样,类名称总是存在于模块中。每个模块可以任意混合任意数量的变量、函数以及类。文件food.py示例如下:

#food.py

var = 1

def func():

...

class spam:

...

class ham:

...

class eggs:

...

如果模块和类碰巧有相同的名称,也是如此。文件person.py,写法如下:

class person:

...

需要像往常一样通过模块获取类:

import person

x = person.person()

person.person()指的是person模块内的person类。只写person只会取得模块,而不是类,除非使用from语句。

from person import person

x = person()

Python的通用惯例之处,类名应该以一个大写字母开头,以使得他们更为清晰:

import person

x = person.Person()

类可以截获Python运算符:运算符重载

运算符重载就是让类写成的对象,可以截获并响应用在内置类型上的运算:加法、切片、打印和点号运算等。

因为运算符重载,可以让我们自己的对象行为就像内置对象那样,这可促进对象接口更为一致并更易于学习,而且可让类对象由预期的内置类型接口的代码处理。以下是重载运算符主要概念的概要:

1.以双下划线命名的方法(__X__)是特殊钩子。Python运算符重载的实现是提供特殊命名的方法来拦截运算。Python语言替每种运算和特殊命名的方法之间,定义了固定不变的映射关系。

2.当实例出现在内置运算时,这类方法会自动调用。例如,如果实例对象继承了__add__方法,当对象出现在+表达式内时,该方法就会调用。该方法的返回值变成相应表达式的结果。

3.类可覆盖多数内置类型运算。有几十种特殊运算符重载的方法的名称,几乎可以截获并实现内置类型的所有运算。它不仅包括了表达式,而且像打印和对象建立这类基本运算也包括在内。

4.运算符覆盖方法没有默认值,而且也不需要。如果类没有定义或继承运算符重载方法,就是说相应的运算在类实例中并不支持。例如,如果没有__add__,+表达式就会引发异常。

5.运算符可让类与Python的对象模型相集成。

【不过,要注意的是,运算符重载是可选的功能,一般的应用程序开发并不需要,除非真的有特殊的需求需要模仿内置类型接口。】

第三个例子

这一次,要定义SecondClass的子类,实现三个特殊名称的属性,让Python自动进行调用:

1.当新的实例构造时,会调用__init__(self是新的ThirdClass对象)

2.当ThirdClass实例出现在+表达式中时,则会调用__add__。

3.当打印一个对象的时候(从技术上讲,当通过str内置函数或者其Python内部的等价形式来将其转换为打印字符串的时候),运行__str__

新的子类也定义了一个常规命名的方法,叫做mul,它在原处修改该实例的对象。如下是一个新的子类:

>>> class ThirdClass(SecondClass):

def __init__(self,value):

self.data = value

def __add__(self,other):

return ThirdClass(self.data+other)

def __str__(self):

return '[ThirdClass:%s]'%self.data

def mul(self,other):

self.data*=other

>>>

>>> a = ThirdClass('abc')

>>> a.display()

Current value = " abc "

>>> print(a)

[ThirdClass:abc]

>>> b = a + 'xyz'

>>> b.display()

Current value = " abcxyz "

>>> a.mul(3)

>>> print(a)

[ThirdClass:abcabcabc]

ThirdClass是一个SecondClass对象,所以其实例会继承SecondClass的display方法。但是,ThirdClass生成的调用现在会传递一个参数(例如,‘abc'),这是传给__init__构造函数内的参数value的,并将其值赋给self.data。直接效果就是,ThirdClass计划在构建时自动设置data属性,而不是在构建之后请求setdata调用。

此外,ThirdClass对象现在可以出现在+表达式和print调用中。对于+,Python把左侧的实例对象传给__add__中的self参数,而把右边的值传给other。__add__返回的内容成为+表达式的结果。对于print,Python把要打印的对象传递给__str__中的self;该方法返回的字符串看作是对象的打印字符串。使用__str__,我们可以用一个常规的print来显示该类的对象,而不是调用特殊的display方法。

__init__、__add__和__str__这样的特殊命名的方法会由子类和实例继承,就像这个类中赋值的其他变量名。Python通常会自动调用,但偶尔也能由程序代码调用。

【注意:只有在实现本质为数学的对象时,才会用到许多运算符重载方法。例如,向量或矩阵类可以重载加法运算符,但员工类可能就不用。就较简单的类而言,可能根本不会用到重载】

【几乎每个实例的类都会出现一个重载方法是:__init__构造函数。虽然Python不会对实例的属性进行声明,但通常也可以通过找到类的__init__方法的代码,而了解实例有哪些属性。】

注意:Python中没有Java中的方法重载,即方法名相同,但参数和参数类型不同,比如__init__函数只能有一个,取最后一个赋给__init__的函数对象。

>>> class Test:

def __init__():

pass

def __init__(self,name,age,sex):

self.name = name

self.age = age

self.sex = sex

>>> a = Test()

Traceback (most recent call last):

File "", line 1, in

a = Test()

TypeError: __init__() missing 3 required positional arguments: 'name', 'age', and 'sex'

把两个__init__换了位置之后就没有报错了,因为__init__函数已经更改成了没有参数的:

>>> class Test:

def __init__(self,name,age,sex):

self.name = name

self.age = age

self.sex = sex

def __init__(self):

pass

>>> a = Test()

世界上最简单的Python类

实际上,我们建立的类可以什么东西都没有,下列语句建立一个类,其内完全没有附加的属性:

>>> class rec:pass

因为没有写任何方法,所以我们需要无操作的pass语句。以交互模式执行此语句,建立这个类后,就可以完全在最初的class语句外,通过赋值变量名给这个类增加属性:

>>> rec.name = 'Bob'

>>> rec.age = 40

通过赋值语句创建这些属性后,就可以用一般的语法将它们取出。这样用时,类差不多就像C的struct,我们也可以用字典的键做类似的事情,但是需要额外的字符。

>>> print(rec.name)

Bob

现在建立两个该类的实例:

>>> x = rec()

>>> y = rec()

>>> x.name,y.name

('Bob', 'Bob')

这些实例本身没有属性,它们只是从类对象那里取出name属性。不过,如果把一个属性赋值给一个实例,就会在该对象内创建(或修改)该属性,而不会因属性的引用而启动继承搜索,因为属性赋值运算只会影响属性赋值所在的对象。在这里,x得到自己的name,但y依然继承附加在他的类上的name:

>>> x.name = 'Gavin'

>>> rec.name,x.name,y.name

('Bob', 'Gavin', 'Bob')

事实上,命名空间对象的属性通常都是以字典的形式实现的。例如,__dict__属性是针对大多数基于类的对象的命名空间字典。如下,名称和__X__内部名称集合所出现的顺序可能随着版本的不同而有所不同:

>>> rec.__dict__.keys()

dict_keys(['__weakref__', 'name', '__module__', '__doc__', 'age', '__dict__'])

>>> list(x.__dict__.keys())

['name']

>>> list(y.__dict__.keys())

[]

在这里,类的字典显示出我们进行赋值了的name和age属性,x有自己的name,而y依然是空的。不过,每个实例都连接至其类以便于继承,如果你想查看的话,这个连接叫做__class__:

>>> x.__class__

类也有一个__bases__属性,它是其超类的元祖:

>>> rec.__bases__

(,)

这两个属性时Python在内存中类树常量的表示方式。

即使是方法也可以完全独立地在任意类对象的外部创建。例如,下列在任意类之外定义了一个简单函数,并带有一个参数:

>>> def upperName(self):

return self.name.upper()

这里与类完全没有什么关系——这是一个简单函数,在此时就能予以调用,只要我们传入一个带有name属性的对象:

>>> upperName(x)

'GAVIN'

不过,如果我们把这个简单函数赋值成类的属性,就会变成方法,可以由任何实例调用:

>>> rec.method = upperName

>>> x.method()

'GAVIN'

>>> y.method()

'BOB'

>>> rec.method(x)

'GAVIN'

类与字典的关系

看如下字典的示例:

>>> rec = {}

>>> rec['name'] = 'mel'

>>> rec['age'] = 45

>>> rec['job'] = 'trainer/writer'

>>>

>>> print(rec['name'])

mel

这段代码模拟了像其他语言中记录这样的工具,这里也可以用类做同样的事情:

>>> class rec:pass

>>> rec.name = 'mel'

>>> rec.age = 45

>>> rec.job = 'trainer/writer'

>>>

>>> print(rec.age)

45

这段代码的语法比其字典等价形式要少很多。它使用了一个空的class语句来产生一个空的命名空间。

这是有效的,但是,对于我们将需要的每一条不同的记录,都需要一条新的class语句。更通俗的讲,我们可以产生一个空类的实例来表示每条不同的记录:

>>> class rec:pass

>>> pers1 = rec()

>>> pers1.name='mel'

>>> pers1.job = 'trainer'

>>> pers1.age = 40

>>>

>>> pers2 = rec()

>>> pers2.name = 'vls'

>>> pers2.job = 'developer'

>>>

>>> pers1.name,pers2.name

('mel', 'vls')

这里,我们通过对属性赋值来填充记录,实际上,同一个类的实例甚至不一定必须有相同的一组属性名称,在这个示例中,pers1有唯一的age属性。每一个实例都有一个不同的属性字典。

最后,我们可以编写一个更完整的类来实现记录及处理:

>>> class Person:

def __init__(self,name,job):

self.name = name

self.job = job

def info(self):

return (self.name,self.job)

>>> rec1 = Person('mel','trainer')

>>> rec2 = Person('vls','developer')

>>>

>>> rec1.job,rec2.info()

('trainer', ('vls', 'developer'))

希望本文所述对大家Python程序设计有所帮助。

python不完全支持面向对象程序设计_Python面向对象程序设计OOP入门教程【类,实例,继承,重载等】...相关推荐

  1. python完全支持面向对象编程_python面向对象编程----009

    本篇内容: 1.反射 2.面向对象编程 3.面向对象三大特性 4.类成员 5.类成员修饰符 6.类的特殊成员 7.单例模式 反射 python中的反射功能是由以下四个内置函数提供:hasattr.ge ...

  2. python完全支持面向对象编程_Python 面向对象编程概要

    面向对象三大特性 面向对象的三大特性是指:封装.继承和多态. 封装 封装,顾名思义就是将内容封装到某个地方,以后再去调用被封装在某处的内容. 所以,在使用面向对象的封装特性时,需要: 将内容封装到某处 ...

  3. python支持面向对象设计_python 面向对象设计

    从函数引出面向对象设计: 举这样一个例子: 歌手唱歌 : singer1={ 'name':'王菲', 'gender':'女', 'type':'歌手',} def singing(singer): ...

  4. python面向对象思路_Python面向对象三要素-继承(Inheritance)

    Python面向对象三要素-继承(Inheritance) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.继承概述 1>.基本概念 前面我们学习了Python的面向对象三 ...

  5. python的核心理念是什么_Python 面向对象编程的核心概念知识点简介

    本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理. 以下文章来源于 无量测试之道 ,作者: 无量测试之道 刚接触Python的新手.小白,可以复制下面 ...

  6. python函数的四个特点_Python面向对象三大特征之封

    本文,我们先说封装,封装就是根据职责将属性和方法封装到一个抽象的类中: 一.面向对象基本概念 我们之前学习的编程方式就是面向过程的 面向过程和面向对象,是两种不同的编程方式 对比 面向过程 的特点,可 ...

  7. python docx 字体大小_Python操作Word的入门教程

    Python操作Word的入门教程 前言 今天来介绍下,如何用 Python 来操作 Word. 再来介绍操作 Word 之前,先来说一个最近看书学到的法则,即 3W 法则. 3W:3W分别指 Wha ...

  8. python操作word详细操作_Python操作Word的入门教程

    Python操作Word的入门教程 前言 今天来介绍下,如何用 Python 来操作 Word. 再来介绍操作 Word 之前,先来说一个最近看书学到的法则,即 3W 法则. 3W:3W分别指 Wha ...

  9. python numpy教程_Python中的Numpy入门教程

    这篇文章主要介绍了 Python 中的 Numpy 入门教程,着重讲解了矩阵中的数组操作 , 需要的 朋友可以参考下 1 . Numpy 是什么 很简单, Numpy 是 Python 的一个科学计算 ...

最新文章

  1. 使用next_permutation()的坑,你中招了么?
  2. 盲人可以也做软件工程师,反思一下老哥
  3. 【译】Using Objects to Organize Your Code
  4. Java IO: PipedInputStream
  5. BeetleX.Http.Clients访问https服务
  6. Cronolog 分割 Tomcat8 Catalina.out日志 (转)
  7. HTML 5.1 的 14 个新特性(含使用案例)
  8. python3导入ssl报错_python3中pip3安装出错,找不到SSL的解决方式
  9. JMeter使用命令行模式生成HTML测试报告
  10. 详解ico图标制作方法
  11. java实现百度网盘爬虫
  12. 零基础入门CV - Task 03 字符识别模型.md
  13. Android手机无法识别SD卡的处理方法
  14. 《Python股票量化交易从入门到实践》随书赠送“回测框架”的使用帮助
  15. 在利用PS进行绘画时,线条画不直怎么办?
  16. 使用Python计算身份证号码最后检验位
  17. 深入理解计算机系统---程序运行过程
  18. MySQL查询一年、一周、三个月的数据
  19. 连的共享打印机显示服务器脱机,打印机显示服务器脱机
  20. Linux 没声音问题解决方案之一——设置正确的模组

热门文章

  1. iis如何处理并发请求
  2. iOS面试总结(待完善)
  3. TabTopUnderLineLayout【自定义顶部选项卡(带下划线)】
  4. Theano2.1.11-基础知识之稀疏
  5. android AVD详解
  6. 仿新浪微博实现ListBox下拉刷新和到底部自动加载
  7. hubbledotnet 查询提示:已添加了具有相同键的项
  8. 药盒识别/垃圾分类—高精度AI模型训练及边缘部署分享
  9. 深圳 | 腾讯 Robotics X 招聘语义视觉方向实习生和正式员工
  10. 天津大学推出大型无人机航拍车辆数据集DroneVehicle