Python的类提供了面向对象编程的所有标准特性:类继承机制允许有多个基类,一个派生类可以覆盖基类中的任何方法,一个方法可以使用相同的名字调用基类中的方法。

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

名称空间是名字和对象之间的映射。多数名称空间使用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程序的一个名称空间可以直接访问的一个文本范围,“直接访问”在这里的意思时当对一个名字的访问没有前缀时,会尝试在名称空间内查找这个名字。 在执行的任意时刻,至少有三个嵌套域,它们有名称空间可以直接访问。

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

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

2. 类

2.1 类的定义

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

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

class people:   #定义一个类peopledef _init_(self,name,age,sex):self.Name=nameself.Age=ageself.Sex=sexdef speak(self): #定义一个方法speakprint "my name"+self.Name

2.2 对象

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

>>> class MyClass:
...     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)

2.3 实例化对象

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

2.4 方法对象

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

x.f()

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

>>> class MyClass:
...     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'

数据属性如果和方法属性名称相同,前者会覆盖后者。所以为了避免名称冲突,最好养成一些习惯,比如方法名称大写,数据属性名称前加一个短小,唯一的前缀。或者数据属性用名词,方法属性用动词。
数据属性可以被方法引用,也可以被对象的使用者引用。换句话说,类不能实现为纯抽象数据类型。

函数定义没有必要非在类里面,例如:

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

3. self的含义

self代表类的实例,而非类。
举例说明:

class Test:def prt(self):print(self)print(self.__class__)t = Test()
t.prt()

输出结果为:

<__main__.Test object at 0x000000000284E080>
<class '__main__.Test'>

从上面的例子中可以很明显的看出,self代表的是类的实例。而self.class则指向类。

3.1 self不必非写成self

class Test:def prt(this):print(this)print(this.__class__)t = Test()
t.prt()

改成this后,运行结果完全一样。当然,最好还是尊重约定俗成的习惯,使用self。

3.2 总结

  • self在定义时需要定义,但是在调用时会自动传入。
  • self的名字并不是规定死的,但是最好还是按照约定是用self。
  • self总是指调用时的类的实例。

4. 派生类

派生类的形式如下:

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

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

class DerivedClassName(modname.BaseClassName)

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

BaseClassName.methodname(self, arguments)

5. 多重继承

Python 支持有限的多重继承:

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

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

6. 私有变量和类局部引用

实例的私有变量只能在对象内部使用,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。

7. 结构体

有时候我们可能需要像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

8. 异常类

用户定义的异常也可以用类来表示,使用这种机制可以创建出可扩展,层次化的异常。 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的基类.

9. 迭代器

现在你可能已经注意到了多数容器对象都可以使用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

10. 生成器(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:类与结构体相关推荐

  1. python中定义结构体

    2019独角兽企业重金招聘Python工程师标准>>> python中定义结构体通过继承_ctypes中的Structure类,标准写法如下: c: struct beer_reci ...

  2. C++_类和结构体所占内存大小,静态成员问题

    C++_类和结构体所占内存大小,静态成员问题 静态成员未初始化或者未调用(当然静态成员必须类外初始化才能调用)是不占内存的.静态成员不占类或结构体的内存 静态成员函数同样只声明未定义同样不占内存 指针 ...

  3. [Swift]枚举、类与结构体的对比

    为什么80%的码农都做不了架构师?>>>    ###枚举.类与结构体的对比### ####枚举与其他两者的关系#### 首先说枚举,相对比较好区分,因为我们知道,枚举与其他两者最大 ...

  4. swift4.0-11 类和结构体

    代码学习swift4.0, 类和结构体 // // main.swift // SwiftLearn11-类和结构体 // // Created by iOS on 2018/5/7. // Copy ...

  5. Swift之深入解析类和结构体的本质

    一.类和结构体的异同 Swift中,类和结构体有许多相似之处,但也有不同.内存分配可以分为堆区(Heap)和栈区(Stack),由于栈区内存是连续的,内存的分配和销毁是通过入栈和出栈操作进行的,速度要 ...

  6. c#类和结构体的关系

    原文地址:http://www.dnbcw.com/biancheng/c/fvhc81798.html 简介:这是c#类和结构体的关系的详细页面,介绍了和c/c++,有关的知识,谢谢大家的观看!要查 ...

  7. Swift傻傻分不清楚系列(十一)类和结构体

    本页包含内容: 类和结构体对比 结构体和枚举是值类型 类是引用类型 类和结构体的选择 字符串(String).数组(Array).和字典(Dictionary)类型的赋值与复制行为 类和结构体是人们构 ...

  8. Swift -- 7.3 类和结构体

    swift的类和结构体高度相似,无论是定义语法还是用法上,swift的类和结构体都具有高度的相似性 区别是:结构体不支持继承(自然也不支持类型转换了.....这个,还不太理解) 结构体不支持定义析构器 ...

  9. Swift基础语法: 25 - Swift的类和结构体

    在我们实际开发的过程中, 类和结构体是人们创建代码时所用的一种通用且灵活构造体, 为了在类和结构体中实现各种功能,我们必须要严格按照对于常量,变量以及函数所规定的语法规则来定义属性和添加方法. 但是在 ...

  10. (07)System Verilog 类与结构体区别

    (07)System Verilog 类与结构体区别 1.1 目录 1)目录 2)FPGA简介 3)System Verilog简介 4)System Verilog 类与结构体区别 5)结语 1.2 ...

最新文章

  1. 在YII2框架中使用UEditor编辑器发布文章
  2. simantaneous AJAX call triggered for Kyma plugin
  3. c++语言中如果调用函数时,需要改变实参或者返回多个值,应该采取,2013年计算机二级C++模拟试题十一及答案...
  4. Dataphin产品核心功能大图(六)发布中心:生产和开发隔离模式下的保护伞
  5. python激活虚拟环境source不是内部或外部_激活虚拟环境的问题
  6. java 中加法递归_java 简单的加法 递归 从A加到B
  7. Perl结合飞信发送免费的天气预报信息
  8. rust哪里油桶多_我们在说Filecoin的时候为什么要说rust语言
  9. win10计算器计算反三角函数
  10. 给站长推荐几本IT书
  11. win10 pro 永久激活
  12. Win7系统一直提示盗版怎么办【系统天地】
  13. Xcelsius 使用XML做为数据源 ----利用ASP与数据库进行交互,即时刷新
  14. 写个简单的chrome插件-京东商品历史价格查询
  15. python3 函数类型限制登录可解封_如何解决python反爬虫限制访问?
  16. 如何把图片转换成PDF?4种简单快捷的方法,一键将图片转PDF
  17. 移动互联网时代,不会“网络预约”的老年人应该怎么办
  18. 蓝牙 韦东山_韦东山生活实例演绎法讲解蓝牙
  19. DOSBox0.74的显示大小问题(调整分辨率)
  20. 如何在鼠标右键菜单中添加自定义菜单?工效率提升一倍

热门文章

  1. python Scrapy Selenium PhantomJS 爬取微博图片
  2. 源于生活的设计之一:职责问题
  3. 老旧小区改造,智慧社区解决方案应用其中
  4. qq空间里每条说说的访客查询接口
  5. Nagiosxi的一个RCE漏洞利用脚本
  6. kb981833补丁蓝屏报错 0x0000006b
  7. [单片机框架][drivers层][cw2015/ADC] fuelgauge 硬件电量计和软件电量计(一)
  8. Android音视频开发(二)SurfaceView
  9. Google云计算三大论文中英文版
  10. 腾讯云cos html,Docsify+腾讯云对象存储 COS,一键搭建云上静态博客