第八章
面向对象和正则表达式
8.1 面向对象编程(OOP)术语概述
(1)类(Class)
用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
(2)类变量
类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
(3)数据成员
类变量或者实例变量, 用于处理类及其实例对象的相关的数据。
(4)方法重写
如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
(5)局部变量
定义在方法中的变量,只作用于当前实例的类。
(6)实例变量
在类的声明中,属性是用变量来表示的。这种变量就称为实例变量,是在类声明的内部但是在类的其他成员方法之外声明的。
(7)继承
即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。
(8)实例化
创建一个类的实例,类的具体对象。
(9)方法
类中定义的函数。
(10)对象
通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
8.2 创建类
使用 class 语句来创建一个新类,class之后为类的名称并以冒号结尾:
类的帮助信息可以通过ClassName.__doc__查看,class_suite由类成员,方法,数据属性组成。
看下面类的简单例子:
empCount 变量是一个类变量,它的值将在这个类的所有实例之间共享。可在内部类或外部类使用 Employee.empCount 访问。
第一种方法__init__()方法是一种特殊的方法,被称为类的构造函数或初始化方法,当创建了这个类的实例时就会调用该方法
self 代表类的实例,self 在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数。
类的方法必须有一个额外的第一个参数名称, 这是跟普通函数的区别。
上面例子执行结果为:
从运行结果可以看出,self代表的是类的实例,代表当前对象的地址,而self._class_ 则指向类。
self 不是 python 关键字,我们换成其他参数也是可以正常执行:
以上实例执行结果为:
8.3 创建实例对象
实例化类其他编程语言中一般用关键字 new,但是在 Python 中并没有这个关键字,类的实例化类似函数调用方式。
以下使用类的名称 Employee 来实例化,并通过 __init__ 方法接收参数。
"创建 Employee 类的第一个对象"
emp1 = Employee("LiQiang ", 4000)
"创建 Employee 类的第二个对象"
emp2 = Employee("WangXing ", 6000)
8.4 访问属性
您可以使用点号 . 来访问对象的属性。使用如下类的名称访问类变量:
emp1.displayEmployee()
emp2.displayEmployee()
print "Total Employee %d" % Employee.empCount
实例
执行以上代码输出结果如下:
你可以添加,删除,修改类的属性,如下所示:
emp1.age = 7 # 添加一个 'age' 属性
emp1.age = 8 # 修改 'age' 属性
del emp1.age # 删除 'age' 属性
你也可以使用以下函数的方式来访问属性:
- getattr(obj, name[, default])
访问对象的属性。
getattr(emp1, 'age') # 返回 'age' 属性的值
- hasattr(obj,name)
检查是否存在一个属性。
hasattr(emp1, 'age') # 如果存在 'age' 属性返回 True。
- setattr(obj,name,value)
设置一个属性。如果属性不存在,会创建一个新属性。
setattr(emp1, 'age', 8) # 添加属性 'age' 值为 8
- delattr(obj, name)
删除属性。
delattr(emp1, 'age') # 删除属性 'age'
8.5 Python内置类属性
- __dict__ : 类的属性(包含一个字典,由类的数据属性组成)
- __doc__ :类的文档字符串
- __name__: 类名
- __module__: 类定义所在的模块(类的全名是'__main__.className',如果类位于一个导入模块mymod中,那么className.__module__ 等于 mymod)
- __bases__ : 类的所有父类构成元素(包含了一个由所有父类组成的元组)
Python内置类属性调用实例如下:
实例
执行以上代码输出结果如下:
8.6 python对象销毁(垃圾回收)
Python 使用了引用计数技术来跟踪和回收垃圾。在其内部记录着所有使用中的对象各有多少引用。
一个内部跟踪变量,称为一个引用计数器。
当对象被创建时, 就创建了一个引用计数, 而当这个对象不再需要时,引用计数变为0,它被垃圾回收。但是回收不是"立即"的, 由解释器在适当的时机,将垃圾对象占用的内存空间回收。
a = 40 # 创建对象 <40>
b = a # 增加引用, <40> 的计数
c = [b] # 增加引用. <40> 的计数
del a # 减少引用 <40> 的计数
b = 100 # 减少引用 <40> 的计数
c[0] = -1 # 减少引用 <40> 的计数
垃圾回收机制不仅针对引用计数为0的对象,同样也可以处理循环引用的情况。循环引用是指两个对象相互引用,但没有其他变量引用他们。在这种情况下,仅仅使用引用计数是不够的。Python 的垃圾收集器实际上是一个引用计数器和一个循环垃圾收集器。作为引用计数的补充, 垃圾收集器也会留心被分配的总量很大(即未通过引用计数销毁的那些)的对象。在这种情况下, 解释器会暂停下来,试图清理所有未引用的循环。
析构函数 __del__ ,__del__在对象销毁的时候被调用,当对象不再被使用时,__del__方法运行:
实例
以上实例运行结果如下:
注意:通常你需要在单独的文件中定义一个类,
8.7 类的继承
面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。
通过继承创建的新类称为子类或派生类,被继承的类称为基类、父类或超类。
继承语法
class 派生类名(基类名)
...
在python中继承中的一些特点:
1、如果在子类中需要父类的构造方法就需要显式的调用父类的构造方法,或者不重写父类的构造方法。
2、在调用基类的方法时,需要加上基类的类名前缀,且需要带上 self 参数变量。区别在于类中调用普通函数时并不需要带上 self 参数
3、Python 总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。(先在本类中查找调用的方法,找不到才去基类中找)。
4、如果在继承元组中列了一个以上的类,那么它就被称作"多重继承" 。
派生类的声明,与他们的父类类似,继承的基类列表跟在类名之后,如下所示:
语法:
class SubClassName (ParentClass1[, ParentClass2, ...]):
...
实例
以上代码执行结果如下:
你可以继承多个类
class A: # 定义类 A
.....
class B: # 定义类 B
.....
class C(A, B): # 继承类 A 和 B
.....
你可以使用issubclass()或者isinstance()方法来检测。
- issubclass() - 布尔函数判断一个类是另一个类的子类或者子孙类,语法:issubclass(sub,sup)
- isinstance(obj, Class) 布尔函数如果obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。
8.8 方法重写
如果你的父类方法的功能不能满足你的需求,你可以在子类重写你父类的方法:
实例:
执行以上代码输出结果如下:
8.8.1 基本重载方法
下表列出了一些通用的功能:
序号 | 方法, 描述 & 简单的调用 |
1 | __init__ ( self [,args...] ) 构造函数 调用方法: obj = className(args) |
2 | __del__( self ) 析构方法, 删除一个对象 调用方法 : del obj |
3 | __repr__( self ) 转化为供解释器读取的形式 调用方法 : repr(obj) |
4 | __str__( self ) 用于将值转化为适于人阅读的形式 调用方法 : str(obj) |
5 | __cmp__ ( self, x ) 对象比较 调用方法 : cmp(obj, x) |
8.8.2 运算符重载
Python同样支持运算符重载:
实例
以上代码执行结果如下所示:
8.9 类属性与方法
8.9.1 类的私有属性
__private_attrs:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs。
8.9.2 类的方法
在类的内部,使用 def 关键字可以为类定义一个方法,与一般函数定义不同,类方法必须包含参数 self,且为第一个参数
8.9.3 类的私有方法
__private_method:两个下划线开头,声明该方法为私有方法,不能在类的外部调用。在类的内部调用 self.__private_methods
实例
编译通过后运行上面实例如下:
运行后报异常:在程序第14行。Python不允许实例化的类访问私有数据。
但你可以使用 object._className__attrName( 对象名._类名__私有属性名 )访问属性,参考以下实例:
执行以上代码,执行结果如下:
8.10 单下划线、双下划线、头尾双下划线说明:
- __foo__: 定义的是特殊方法,一般是系统定义名字 ,类似 __init__() 之类的。
- _foo: 以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问,不能用于 from module import *
- __foo: 双下划线的表示的是私有类型(private)的变量, 只能是允许这个类本身进行访问了。
8.11 正则表达式
正则表达式是一个特殊的字符序列,它能帮助我们方便的检查一个字符串是否与某种模式匹配。这节我们主要介绍一下Python常用的正则表达式处理函数。
8.11.1 re.match函数
函数功能:尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。
基本语法:re.match(pattern, string, flags=0)
参数说明:
- pattern
匹配的正则表达式
- string
要匹配的字符串。
- flags
标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。
下面我们来看一个实例:
以上实例运行输出结果为:
re.match方法匹配成功返回一个匹配的对象,否则返回None。
我们可以使用group(num) 或 groups() 匹配对象函数来获取匹配表达式:
- group(num=0)
匹配的整个表达式的字符串,group() 可以一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组。
- groups()
返回一个包含所有小组字符串的元组,从1到所含的小组号。
下面我们看实例:
以上实例执行结果如下:
8.11.2 re.search()方法
re.search()方法用于扫描整个字符串并返回第一个成功的匹配。
基本语法:re.search(pattern, string, flags=0)
参数说明:
- pattern
匹配的正则表达式
- string
要匹配的字符串。
- flags
标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。
如果re.search()方法匹配成功,则返回一个匹配的对象,否则返回None。
实例演示:
以上实例运行输出结果如下:
实例演示:
以上实例运行输出结果如下:
8.11.3 re.match()与re.search()的区别
re.match()只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search()匹配整个字符串,直到找到一个匹配。
实例
#!/usr/bin/python
import re
line = "Cats are smarter than dogs";
matchObj = re.match( r'dogs', line, re.M|re.I)
if matchObj:
print ("match --> matchObj.group() : ", matchObj.group())
else:
print ("No match!!" )
matchObj = re.search( r'dogs', line, re.M|re.I)
if matchObj:
print ("search --> searchObj.group() : ", matchObj.group())
else:
print ("No match!!")
以上实例运行输出结果如下:
8.11.4 检索和替换
re 模块提供了re.sub()用于替换字符串中的匹配项。
基本语法:re.sub(pattern, repl, string, count=0, flags=0)
参数说明:
pattern : 正则中的模式字符串。
repl : 替换的字符串,也可为一个函数。
string : 要被查找替换的原始字符串。
count : 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。
实例
以上实例运行输出结果如下:
下面这个例子,利用自定义函数double,将字符串S中匹配到的数字乘以 2并返回:
上面实例中,字符串S的第4、5、6、8、9、12、13和14位对应的数字分别为:3、4、5、2、8、1、6和3,将数字乘以2后,得到新的字符串“abc690G56MY326”:
8.11.5 re.compile ()函数
compile() 函数用于编译正则表达式,生成一个正则表达式( Pattern )对象,供 match() 和 search() 这两个函数使用。
基本语法:re.compile(pattern[, flags])
参数说明:
pattern: 一个字符串形式的正则表达式
flags: 可选,表示匹配模式,如:忽略大小写、多行模式等,具体参数为:
re.I 忽略大小写
re.L 表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境
re.M 多行模式
re.S 即为 . 并且包括换行符在内的任意字符(. 不包括换行符)
re.U 表示特殊字符集 \w, \W, \b, \B, \d, \D, \s, \S 依赖于 Unicode 字符属性数据库
re.X 为了增加可读性,忽略空格和 # 后面的注释
具体操作我们来看下面这个例子:
在上面,当匹配成功时返回一个 Match 对象,如下图:
其中:
group([group1, …]) 方法用于获得一个或多个分组匹配的字符串,当要获得整个匹配的子串时,可直接使用 group() 或 group(0);
start([group]) 方法用于获取分组匹配的子串在整个字符串中的起始位置(子串第一个字符的索引),参数默认值为 0;
end([group]) 方法用于获取分组匹配的子串在整个字符串中的结束位置(子串最后一个字符的索引+1),参数默认值为 0;
span([group]) 方法返回 (start(group), end(group))。
我们再来看看下面这个例子:
程序运行后的结果:
8.11.6 findall
在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。
与match 和 search 不同的是,match 和 search匹配一次 ,而findall 匹配所有。
基本语法:findall(string[, pos[, endpos]])
参数说明:
- string : 待匹配的字符串。
- pos : 可选参数,指定字符串的起始位置,默认为 0。
- endpos : 可选参数,指定字符串的结束位置,默认为字符串的长度。
我们来看下面一个实例,查找字符串中的所有数字:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import re
pattern = re.compile(r'\d+') # 查找数字
result1 = pattern.findall('myphone 139 mobi 7948 tp 2032')
result2 = pattern.findall('my086phon139e7948mob2032', 0, 24)
print(result1)
print(result2)
输出结果:
['139', '7948', '2032']
['086', '139', '7948', '2032']
------------------
(program exited with code: 0)
请按任意键继续. . .
8.11.7 re.finditer
和 findall 类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回。
基本语法:re.finditer(pattern, string, flags=0)
参数说明:
- pattern:匹配的正则表达式
- string:要匹配的字符串。
- flags:标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。
实例:
输出结果:
139
7948
20
32
------------------
(program exited with code: 0)
请按任意键继续. . .
8.11.8 re.split
split方法按照能够匹配的子串将字符串分割后返回列表。
基本语法:re.split(pattern, string[, maxsplit=0, flags=0])
参数说明:
- pattern:匹配的正则表达式
- string:要匹配的字符串
- maxsplit:分隔次数,maxsplit=1 分隔一次,默认为 0,不限制次数。
- flags:标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。
实例:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import re
print(re.split('a*', 'hello world')) # 找不到匹配的字符串,split不会作出分割
print(re.split('\W+', ' hello world,python.'))
print(re.split('(\W+)', ' hello world.'))
print (re.split('\W+', 'hello world.'))
8.12 正则表达式对象
re.RegexObject
re.compile() 返回 RegexObject 对象。
re.MatchObject
group() 返回被 RE 匹配的字符串。
start() 返回匹配开始的位置
end() 返回匹配结束的位置
span() 返回一个元组包含匹配 (开始,结束) 的位置
8.13 正则表达式修饰符 - 可选标志
正则表达式可以包含一些可选标志修饰符来控制匹配的模式。修饰符被指定为一个可选的标志。多个标志可以通过按位 OR(|) 它们来指定。如 re.I | re.M 被设置成 I 和 M 标志:
修饰符 | 描述 |
re.I | 使匹配对大小写不敏感 |
re.L | 做本地化识别(locale-aware)匹配 |
re.M | 多行匹配,影响 ^ 和 $ |
re.S | 使 . 匹配包括换行在内的所有字符 |
re.U | 根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B. |
re.X | 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。 |
8.14 正则表达式模式
模式字符串使用特殊的语法来表示一个正则表达式:
- 字母和数字表示他们自身。一个正则表达式模式中的字母和数字匹配同样的字符串。
- 多数字母和数字前加一个反斜杠时会拥有不同的含义。
- 标点符号只有被转义时才匹配自身,否则它们表示特殊的含义。
- 反斜杠本身需要使用反斜杠转义。
由于正则表达式通常都包含反斜杠,所以你最好使用原始字符串来表示它们。模式元素(如 r'\t',等价于 '\\t')匹配相应的特殊字符。
下面3张表列出了正则表达式模式语法中的特殊元素。如果你使用模式的同时提供了可选的标志参数,某些模式元素的含义会改变。
表一
模式 | 描述 |
^ | 匹配字符串的开头 |
$ | 匹配字符串的末尾。 |
. | 匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符。 |
[...] | 用来表示一组字符,单独列出:[amk] 匹配 'a','m'或'k' |
[^...] | 不在[]中的字符:[^abc] 匹配除了a,b,c之外的字符。 |
re* | 匹配0个或多个的表达式。 |
re+ | 匹配1个或多个的表达式。 |
re? | 匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式 |
re{ n} | 精确匹配 n 个前面表达式。例如, o{2} 不能匹配 "Bob" 中的 "o",但是能匹配 "food" 中的两个 o。 |
re{ n,} | 匹配 n 个前面表达式。例如, o{2,} 不能匹配"Bob"中的"o",但能匹配 "foooood"中的所有 o。"o{1,}" 等价于 "o+"。"o{0,}" 则等价于 "o*"。 |
re{ n, m} | 匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式 |
a| b | 匹配a或b |
(re) | 对正则表达式分组并记住匹配的文本 |
表二
模式 | 描述 |
(?imx) | 正则表达式包含三种可选标志:i, m, 或 x 。只影响括号中的区域。 |
(?-imx) | 正则表达式关闭 i, m, 或 x 可选标志。只影响括号中的区域。 |
(?: re) | 类似 (...), 但是不表示一个组 |
(?imx: re) | 在括号中使用i, m, 或 x 可选标志 |
(?-imx: re) | 在括号中不使用i, m, 或 x 可选标志 |
(?#...) | 注释. |
(?= re) | 前向肯定界定符。如果所含正则表达式,以 ... 表示,在当前位置成功匹配时成功,否则失败。但一旦所含表达式已经尝试,匹配引擎根本没有提高;模式的剩余部分还要尝试界定符的右边。 |
(?! re) | 前向否定界定符。与肯定界定符相反;当所含表达式不能在字符串当前位置匹配时成功 |
(?> re) | 匹配的独立模式,省去回溯。 |
\w | 匹配字母数字及下划线 |
\W | 匹配非字母数字及下划线 |
\s | 匹配任意空白字符,等价于 [ \t\n\r\f]。 |
\S | 匹配任意非空字符 |
\d | 匹配任意数字,等价于 [0-9]. |
表三
模式 | 描述 |
\D | 匹配任意非数字 |
\A | 匹配字符串开始 |
\Z | 匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串。 |
\z | 匹配字符串结束 |
\G | 匹配最后匹配完成的位置。 |
\b | 匹配一个单词边界,也就是指单词和空格间的位置。例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。 |
\B | 匹配非单词边界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。 |
\n, \t, 等. | 匹配一个换行符。匹配一个制表符。等 |
\1...\9 | 匹配第n个分组的内容。 |
\10 | 匹配第n个分组的内容,如果它经匹配。否则指的是八进制字符码的表达式。 |
8.15 正则表达式实例
字符匹配
实例 | 描述 |
python | 匹配 "python". |
字符类
实例 | 描述 |
[Pp]ython | 匹配 "Python" 或 "python" |
rub[ye] | 匹配 "ruby" 或 "rube" |
[aeiou] | 匹配中括号内的任意一个字母 |
[0-9] | 匹配任何数字。类似于 [0123456789] |
[a-z] | 匹配任何小写字母 |
[A-Z] | 匹配任何大写字母 |
[a-zA-Z0-9] | 匹配任何字母及数字 |
[^aeiou] | 除了aeiou字母以外的所有字符 |
[^0-9] | 匹配除了数字外的字符 |
特殊字符类
实例 | 描述 |
. | 匹配除 "\n" 之外的任何单个字符。要匹配包括 '\n' 在内的任何字符,请使用象 '[.\n]' 的模式。 |
\d | 匹配一个数字字符。等价于 [0-9]。 |
\D | 匹配一个非数字字符。等价于 [^0-9]。 |
\s | 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。 |
\S | 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。 |
\w | 匹配包括下划线的任何单词字符。等价于'[A-Za-z0-9_]'。 |
\W | 匹配任何非单词字符。等价于 '[^A-Za-z0-9_]'。 |
下一章:第九章 Python CGI编程
感谢广大Python爱好者的关注,请收藏并关注,留言讨论相关话题!
声明:我要去上班所有作品(图文、音视频)均由用户自行上传分享,仅供网友学习交流,版权归原作者爱知自媒体人所有,原文出处。若您的权利被侵害,请联系删除。
本文标题:(pythoncgi编程详解)(《python编程》)
本文链接:https://www.51qsb.cn/article/m8ydf.html