Python的类机制使用尽可能少的新语法和语义将类引入语言。python的类提供了面向对象程序设计语言所有的 标准特性:类继承机制允许有多个基类,一个派生类可以覆盖基类中的任何方法,一个方法可以使用相同的名字调用 基类中的方法。

1 名字和对象

对象有其特性,同一个对象可以有多个名字,这与其它语言中的别名很相似。别名有时候像指针,例如将对象当做 函数参数传递的时候非常高效,因为只传递了指针,这避免了pascal中的两套参数传递机制。

2 Python的域(scopes)和名称空间(namespaces)

在引入类之前,我们讲Python的域规则。类的定义巧妙地运用了名称空间,所以你需要知道域和名称空间如何工作才能理解发生了什么。

首先从定义开始。 名称空间是名字和对象之间的映射。多数名称空间使用Python的字典来实现,但除非出于性能考虑,我们通常不关心具体如何实现。名称空间的例子有,内置的名称例如abs(),内置的异常名,模块的全局名称,函数调用时的局部名称。在某种程度上,对象的属性也构成名称空间。

关于名称空间最重要的一点是:不同名称空间中的名称没有关系。例如 两个不同模块中都可以包含名为maximize的函数,这不会造成混肴,因为使用这些模块时必须加上模块名作为前缀。 另外,我把任何点后的名称叫做属性。例如,在表达式z.real中,real是对象z的属性。严格来说,引用模块中的名称是对属性的引用,在表达式modname.funcname中,modname是一个模块,funcname是它的一个属性。这个例子中模块属性和模块 内定义的全局名称有着直接的映射,它们有着相同的名称空间。 属性可能是只读的或者可写的,上面的例子中,属性就是可写的,例如:modname.the_ answer = 42.可写的属性可以被删除, 例如 del modname.the_ answer 会删除模块 modname中的 the_ answer属性。

名称空间在不同的时刻创建,有着不同的生命周期。包含内置名称的名称空间在Python解释器启动时被创建,且不会被删除。 模块的全局名称空间在模块被导入时被创建,正常情况下,模块的名称空间会持续到解释器退出。来自脚本文件或者交互式环境 被解释器最顶层调用执行的语句,被认为是 __ main __ 模块的一部分,所以他们有着自己的全局名称空间。 函数的局部名称空间当函数被调用时被创建,函数返回时或者出现异常而函数又没有提供处理方式时被删除。当然,在递归调用 中每一次调用都有他们自己的局部名称空间。 域(scpoe)是Python程序的一个名称空间可以直接访问的一个文本范围,“直接访问”在这里的意思时当对一个名字的访问没有 前缀时,会尝试在名称空间内查找这个名字。 在执行的任意时刻,至少有三个嵌套域,它们有名称空间可以直接访问。

  • 最内层的域,它会首先被搜索,包含局部名称
  • 任何封装函数的域,从最近的封装域开始搜索,包含非局部,非全局的名称
  • 倒数第二个域,包含当前模块的全局名称
  • 最外层的域,最后被搜索,包含内置名字的名称空间

如果一个名字被声名为全局的,那么所有的引用和赋值都是针对中间层的域,这一层域包含模块的全局名称。 意识到域是由文本决定是非常重要的,定义在模块中的一个函数的全局域就是这个模块的名称空间,无论这个函数在哪儿, 通过哪个别名被调用。

3 初识类

3.1 定义类

class ClassName:<statement-1>...<statement-N>

类定义,像函数定义一样,在执行时才会起作用。你可以把类定义放在任何地方比如if语句的分支,或者在函数内部。 在实际应用时,定义在类中的语句通常都是函数定义,但是其它语句也是允许出现的,并且有的时候非常有用。 当进入一个类定义时,一个新的名称空间被创建,并且被当作局部域来使用。

3.2 类对象

类对象提供两种操作,属性引用和实例化。 属性引用使用标准句法:obj.name. 有效的属性名是类对象创建时类的名称空间内的所有名字。 例如下面的类定义中,MyClass.i和MyClass.f都是有效的属性名。

>>> class MyClass:
...     """A simple example class"""
...     i = 123
...     def f(self):
...         return 'hello world'
...
>>> MyClass.i
123
>>> MyClass.i = 10

类的实例化使用函数记号,例如:

>>> x = MyClass()
>>> x.i
10

这个实例化操作创建了一个空对象,许多类在实例化时定义了一些初始化操作。例如:

>>> class MyClass():
...     def __init__(self):
...          self.data = []

当一个类定义了__ init __ 方法后,类实例化时会自动调用 __ init __ ().

__ init __ 函数还可以有其它参数,例如:

>>> class Complex:
...     def __init__(self, realpart, imagpart):
...         self.r = realpart
...         self.i = imagpart
...
>>> x = Complex(3.0, -4.5)
>>> x.r, x.i
(3.0, -4.5)

3.3 实例化对象

现在我们可以用实例化的对象做些什么呢?它唯一可以进行的操作是属性引用。有两类有效的属性名,数据属性和方法。 数据属性(data attributes)对应c++中的数据成员,数据属性无需声明,第一次给它赋值时就表明了它的存在。 另一种实例化的属性引用叫做方法(Method).方法是对象内的一个函数。

3.4 方法对象

通常我们调用一个方法的方式是:

x.f()

但是,由于x.f是一个方法对象,所以它可以存储起来,以便以后调用

>>> class MyClass:
...     """A simple example class"""
...     i = 12345
...     def f(self):
...         return 'hello world'
...
>>> x = MyClass()
>>> x.f()
'hello world'
>>> xf = x.f
>>> xf()
'hello world'

你可能已经发现,f名名有一个参数,但是调用时为什么没有使用呢。其实,原因在于 x.f() 与 MyClass.f(x) 是等价的。

>>> MyClass.f(x)
'hello world'

4 漫谈

数据属性如果和方法属性名称相同,前者会覆盖后者。所以为了避免名称冲突,最好养成一些习惯,比如方法名称大写,数据属性 名称前加一个短小,唯一的前缀。或者数据属性用名词,方法属性用动词。 数据属性可以被方法引用,也可以被对象的使用者引用。换句话说,类不能实现为纯抽象数据类型。 通常,方法的第一个参数是self.虽然这只是一个习惯用法,但是遵循一些习惯用法会让你的程序可读性更强。 函数定义没有必要非在类里面,例如:

# Function defined outside the class
def f1(self, x, y):return min(x, x+y)class C:f = f1def g(self):return 'hello world'h = g

一个方法可以通过self参数调用其它方法,

>>> class Bag:
...     def __init__(self):
...          self.data = []
...     def add(self, x):
...          self.data.append(x)
...     def addtwice(self, x):
...          self.add(x)
...          self.add(x)
...
>>> b = Bag()
>>> b.data
[]
>>> b.add('1')
>>> b.data
['1']
>>> b.addtwice('x')
>>> b.data
['1', 'x', 'x']

5 派生

派生类的形式如下:

class DerivedClassName(BaseClassName):<statement-1>...<statement-N>

BaseClassName必须在包含派生类的域内定义,BaseClassName可以是一个表达式,例如:

class DerivedClassName(modname.BaseClassName):

当派生类的对象引用了一个属性时,会先在派生类内查找这个属性名,如果找不到,再到基类中查找。 派生类可以覆盖基类中的方法,即使基类中的方法被覆盖了,也可以使用下面的方法来调用

BaseClassName.methodname(self, arguments)

6 多重继承

Python 支持有限的多重继承:

class DerivedClassName(Base1, Base2, Base3):<statement-1>...<statement-N>

在旧风格的类中,唯一的规则是深度优先,从左到右。以上述类定义为例,如果一个属性没有在 DerivedClassName中被 找到,那么会继续搜索Base1,Base2等等 在新风格的类中,对方法的解析次序是动态改变的,这是因为类的继承关系会呈现出一个或多个菱形。例如新风格的类都由 object类派生出,这样就会就多条路径通向object。为了避免基类被多次搜索,使用了线性化算法将所有基类排列成从左 到右的顺序

7 私有变量和类局部引用

实例的私有变量只能在对象内部使用,python中常常使用例如 _ spam 的形式来代表API的非公有部分,无论是函数,方法还是 数据成员。类私有成员的特性的一种有效的用法是可以避免与子类中定义的名字冲突,这种机制叫做 mangling:

class Mapping:def __init__(self, iterable):self.items_list = []self.__update(iterable)def update(self, iterable):for item in iterable:self.items_list.append(item)__update = update # private copy of original update() methodclass MappingSubclass(Mapping):def update(self, keys, values):# provides new signature for update()# but does not break __init__()for item in zip(keys, values):self.items_list.append(item)

注意上述代码中 __ update 的使用,避免了子类中对update的覆盖影响到基类 __ init__ 中的 update.

8 结构体

有时候我们可能需要像C中的struct那样的数据类型,把少量的数据项放在一起。Python中可以使用定义一个空类来实现这一点:

# filename:p.py
class Employee:passjohn = Employee() # Create an empty employee record# Fill the fields of the record
john.name = 'John Doe'
john.dept = 'computer lab'
john.salary = 1000
>>> import p
>>> p.john
<p.Employee instance at 0xb71f50ac>
>>> p.john.name
'John Doe'
>>> p.john.dept
'computer lab'
>>> p.john.salary
1000

9 异常(Exceptions)也是类

用户定义的异常也可以用类来表示,使用这种机制可以创建出可扩展,层次化的异常。 raise 语句有两种新的形式

raise Class, instance
raise instance

第一种形式中,instance必须是Class的一个实例,或者是由它派生出的类。 第二种形式是下面这种形式的缩写

raise instance.__class__, instance

下面这个例子会依次打印出B,C,D

class B:pass
class C(B):pass
class D(C):passfor c in [B,C,D]:try:raise c()except D:print "D"except C:print "C"except B:print "B"
>>> import f
B
C
D

注意如果 B写在最前面,会打印出BBB,这是因为raise C和raise D时,执行到except B是都会 print "B". 因为B是C,D的基类.

10 迭代器

现在你可能已经注意到了多数容器对象都可以使用for语句来循环

>>> for elem in [1,2,3]:
...     print elem
...
1
2
3
>>> for elem in (1,2,3):
...     print elem
...
1
2
3

这一风格清晰,简捷,方便。迭代器的使用在Python中非常普便。for语句的背后,其实是对容器对象调用 iter(). 这个函数返回一个迭代器对象,它定义了next()函数,每次访问容器中的一个元素。当没有元素的时候,next()返回一个 StopIteration异常,告诉for语句循环结束了。

>>> s = 'asdf'
>>> it = iter(s)
>>> it
<iterator object at 0xb71f590c>
>>> it.next()
'a'
>>> it.next()
's'
>>> it.next()
'd'
>>> it.next()
'f'
>>> it.next()
Traceback (most recent call last):File "<stdin>", line 1, in <module>
StopIteration

理解了迭代机制,就可以很容易地把迭代器加入你的类中,定义__ iter__ ()方法,返回一个有next()方法的对象。 如果一个类定义了next()函数,__ iter__ () 可以仅返回 self:

# q.py
class Reverse:def __init__(self, data):self.data = dataself.index = len(data)def __iter__(self):return selfdef next(self):if self.index == 0:raise StopIterationself.index = self.index -1return self.data[self.index]
>>> import q
>>> rev = q.Reverse('spam')
>>> iter(rev)
<q.Reverse instance at 0xb71f588c>
>>> for char in rev:
...     print char
...
m
a
p
s

11 生成器(Generators)

生成器是创建迭代器的一个简单而强大的工具。它们像正常函数一样,只是需要返回数据时使用 yield语句。

# d.py
def reverse(data):for index in range(len(data)-1, -1, -1):yield data[index]
>>> import d
>>> for char in d.reverse('golf'):
...     print char
...
f
l
o
g

任何可以使用生成器做的事,都可以使用前一版本的reverse实现,生成器之所以实现紧凑是因为自动创建了 __ iter() 和 next() 方法。

python学习笔记(八)类(classes)相关推荐

  1. Python学习笔记 (类与对象)

    Python学习笔记 (类与对象) 1.类与对象 面向对象编程语言类: 一个模板, (人类)-是一个抽象的, 没有实体的对象: (eg: 张三, 李四) 属性: (表示这类东西的特征, 眼睛, 嘴巴, ...

  2. python面向对象编程72讲_2020-07-22 Python学习笔记27类和面向对象编程

    一些关于自己学习Python的经历的内容,遇到的问题和思考等,方便以后查询和复习. 声明:本人学习是在扇贝编程通过网络学习的,相关的知识.案例来源于扇贝编程.如果使用请说明来源. 第27关 类与面向对 ...

  3. Python学习笔记(八)

    Python基础总结 Python基础总结 Python简介 Python是一种解释型.面向对象.动态数据类型的高级程序设计语言.目前分为Python2.X和Python3.X两个版本,且Python ...

  4. python学习笔记(面向对象,类)

    一.类的定义 1.类的基本结构 #命名规则: 驼峰法 class Student(): # 使用class 定义类a= 1 # 变量name = '小明'def aa(self): # 函数print ...

  5. Python 学习笔记13 类 - 继承

    我们在编程的过程中,并非都是要重头开始.比如其他人已经有现成的类,我们可以使用其他找人编写的类.术语称之为: 继承. 当一个类继承例外一个类时,它可以获得这个类的所有属性和方法:原有的类称之为 父类, ...

  6. Python学习笔记:类

    本文来自:入门指南 开胃菜参考:开胃菜 使用Python解释器:使用Python解释器 本文对Python的简介:Python 简介 Python流程介绍:深入Python 流程 Python数据结构 ...

  7. 【懒懒的Python学习笔记八】

    面向对象编程是最有效的编程方法之一,在面向对象编程中,你编写表示现实世界中事物和情景的类,并基于这些类来创建对象.使用类来创建对象被称为实例化. 创建和使用类 使用类可以模拟任何东西.下面的实例编写一 ...

  8. python学习笔记:类的方法总结

    python中类的方法总结 在python中,类的方法有如下三种: (1)实例方法(即:对象方法) (2)类方法 (3)静态方法 下面,将对这三种方法进行总结. 1.实例方法(对象方法) 通常情况下, ...

  9. python学习笔记(类)

    类中一个常见的魔术方法 class pipei():def __init__(self,name,age,high): self.name = nameself.age = ageself.high ...

  10. Python 学习笔记12 类 - 使用类和实例

    当我们熟悉和掌握了怎么样创建类和实例以后,我们编程中的大多数工作都讲关注在类的简历和实例对象使用,修改和维护上. 结合实例我们来进一步的学习类和实例的使用: 我们新建一个汽车的类: #-*- codi ...

最新文章

  1. mSystems:南土所梁玉婷组-土壤pH过滤稻田土壤耐铝微生物的关联模式
  2. 实现entity、dao 、service 、serviceImpl自动生成
  3. urllib2.HTTPError: HTTP Error 403: Forbidden的解决方案
  4. JAVA中的面向对象与内存解析_2
  5. C语言文字加密程序的实现
  6. 您的第一个Lagom服务– Java Microservices入门
  7. 单自由度阻尼强迫振动通解求导及simulink验证(修正了网上常见的错误)
  8. 安装vim提示Depends: libpython3.5 (>= 3.5.0~b1) but it is not going to be installed的解决方法
  9. python数据科学课后答案_Python数据科学-技术详解与商业实践-第五讲作业
  10. 如何识别计算机病毒,怎样识别计算机病毒
  11. Java语言程序设计(第3版)沈泽刚主编第6,7,8章课后习题答案
  12. android开发利器--站在巨人肩膀上前行
  13. 论文解读(一)V-Net: Fully Convolutional Neural Networks for Volumetric Medical Image Segmentation
  14. 计算机硬盘显示容量,求解怎么增加电脑硬盘容量
  15. [UnexpectedValueException] Your github oauth token for github.com contains invalid characters
  16. 一键打开多个软件 开发工具
  17. Java微服务框架一览
  18. 单个数码管动态显示(STM32F103C8T6)
  19. PR 2020 关于驱动程序更新
  20. 《C游记》 第二章 - 初识分支句 循环助本心(壹)

热门文章

  1. 关于华为P40登录谷歌闪退的问题
  2. C语言:L1-033 出生年 (15分)(解题报告)
  3. (*长期更新)软考网络工程师学习笔记——Section 10 网络安全
  4. element表格实现树形全选_vue+element UI实现树形表格带复选框的示例代码
  5. php中的rtrim_php中ltrim()、rtrim()与trim()删除字符空格实例
  6. linux mmu的实现的讲解_Linux中的段
  7. python爬虫之美女图片爬取
  8. java和C结构体通信
  9. kalixfce不能启动_kali升级2019.4后切换xfce桌面
  10. 学python的前提_Python语言学习前提:条件语句