OOP基础
基本概念
OOP:面向对象编程
实现了数据属性和行为属性的融合
类:具有相同属性的对象集合
实例/对象:类的一个具体实现
• 类(Class):用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
• 实例化:创建一个类的实例,类的具体对象。
• 方法:类中定义的函数。
• 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
创建类
• 使用 class 语句来创建一个新类,class 之后为类的名称并以冒号结尾
• 类名建议使用驼峰形式
class BearToy:
pass
• 类是蓝图,实例是根据蓝图创建出来的具体对象
tidy = BearToy()
构造器方法
• 当实例化类的对象是,构造器方法默认自动调用
• 实例本身作为第一个参数,传递给self
class BearToy:
def init(self, size, color):
self.size = size
slef.color = color
if name == ‘main’:
tidy = BearToy(‘small’, ‘orange’)
其他绑定方法
• 类中定义的方法需要绑定在具体的实例,由实例调用
• 实例方法需要明确调用
class BearToy:
def init(self, size, color):
self.size = size
self.color = color
def speak(self):
print(‘hahaha’)
if name == ‘main’:
tidy = BearToy(‘small’, ‘orange’)
tidy.speak()
class Warrior:
def __init__(self, name, weapon):
#'self不是关键字,可以是任何名字,表示实例本身'
#绑定在对象身上的属性,在类中任意位置可用
self.name = name
self.weapon = weapon
def speak(self, words):
#方法走的的参数、变量就是函数的局部变量
print('我是%s, %s' % (self.name, words))
def attack(self, target):
print('正在攻击: %s' % target)
if __name__ == '__main__':
#创建实例时,自动调用__init__方法,实例自动作为第一个参数
lb = Warrior('吕布', '方天画戟') #创建实例
print(lb.name)
print(lb.weapon)
lb.speak('马中赤兔,人中吕布')
zf = Warrior('张飞', '丈八蛇矛')
print(zf.name, zf.weapon)
zf.speak('我乃燕人张飞张翼德')
print('*' * 30)
lb.attack('董卓')
zf.attack('吕布')
(5nsd1906) [student@room9pc01 py08]$ python rose.py
吕布
方天画戟
我是吕布, 马中赤兔,人中吕布
张飞 丈八蛇矛
我是张飞, 我乃燕人张飞张翼德
******************************
正在攻击: 董卓
正在攻击: 吕布
组合和派生
组合
两个类有明显的不同,一个类的属性是另一个类的实例,用组合
组合的定义,建议使用驼峰的形式,每个单词的开头用大写
• 类被定义后,目标就是要把它当成一个模块来使用,并把这些对象嵌入到你的代码中去
• 组合就是让不同的类混合并加入到其它类中来增加功能和代码重用性
• 可以在一个大点的类中创建其它类的实例,实现一些其它属性和方法来增强对原来的类对象
组合的应用
• 一个类是另一个类的组件
class Manufacture:
def init(self, phone, email):
self.phone = phone
self.email = email
class BearToy:
def init(self, size, color, phone, email):
self.size = size
self.color = color
self.vendor = Manufacture(phone, email)
class Weapon:
def __init__(self, name, strength, type):
self.name = name
self.strength = strength
self.type = type
class Warrior:
def __init__(self, name, weapon):
self.name = name
self.weapon = weapon
if __name__ == '__main__':
ji = Weapon('方天画戟', '88', '物理伤害')
#print(ji.name, ji.strength, ji.ytpe)
lb = Warrior('吕布', ji)
print(lb.weapon.name, lb.weapon.type)
创建子类
两个类非常相似,只是有一些不同
• 当类之间有显著的不同,并且较小的类是较大的类所需要的组件时组合表现得很好;但当设计“相同的类但有一些不同的功能”时,派生就是一个更加合理的选择了
• OOP 的更强大方面之一是能够使用一个已经定义好的类,扩展它或者对其进行修改,而不会影响系统中使用现存类的其它代码片段
• OOD(面向对象设计)允许类特征在子孙类或子类中进行继承
• 创建子类只需要在圆括号中写明从哪个父类继承即可
class BearToy:
def init(self, size, color):
self.size = size
self.color = color
… …
class NewBearToy(BearToy):
pass
继承
• 继承描述了基类的属性如何“遗传”给派生类
• 子类可以继承它的基类的任何属性,不管是数据属性还是方法
class BearToy:
def init(self, size, color):
self.size = size
self.color = color
… …
class NewBearToy(BearToy):
pass
if name == ‘main’:
tidy = NewBearToy(‘small’, ‘orange’)
tidy.speak()
class Role:
def __init__(self, name, weapon):
self.name = name
self.weapon = weapon
def speak(self, words):
print('我是%s, %s' % (self.name, words))
def attack(self, target):
print('正在攻击: %s' % target)
class Warrior(Role):
pass
# 子类可以继承父类(基类)的所有方法,没有pass会报错
class Mage(Role):
pass
if __name__ == '__main__':
lb = Warrior('吕布', '方天画戟')
lj = Mage('李靖', '宝塔')
lb.speak('马中赤兔,人中吕布')
lj.speak('宝塔镇河妖')
myclass.py
class A:
def __init__(self, a, b, c ,d):
self.a = a
self.b = b
self.c = c
self.d = d
class B(A):
def __init__(self, a, b, c, d, e):
#调用父类
# A.__init__(self, a, b, c, d) #与下面写法等价
super(B, self).__init__(a, b, c, d)
self.e = e
if __name__ == '__main__':
b1 = B(10, 20, 30, 40, 50)
print(b1.a, b1.e)
通过继承覆盖方法
• 如果子类中有和父类同名的方法,父类方法将被覆盖
• 如果需要访问父类的方法,则要调用一个未绑定的父类方法,明确给出子类的实例
class BearToy:
def init(self, size, color, phone, email):
self.size = size
self.color = color
self.vendor = Manufacture(phone, email)
… …
class NewBearToy(BearToy):
def init(self, size, color, phone, email, date):
super(NewBearToy, self).init(size, color, phone, email)
self.date = date
多重继承
子类可以有多个父类,子类自动继承所有父类的方法,查找为自下向上,自左向右
• python允许多重继承,即一个类可以是多个父类的子类,子类可以拥有所有父类的属性
class A:
… def foo(self):
… print('foo method’)
class B:
… def bar(self):
… print('bar method’)
class C(A, B):
… pass
c = C()
c.foo()
foo method
c.bar()
bar method
class A:
def func1(self):
print('A func')
class B:
def func2(self):
print('B func')
def func4(self):
print('BBBBBBBBB func4')
class C(B, A): #将C(B, A)换为C(A, B),可以查看到多重继承的自左向右,自下向上继承
def func3(self):
print('B func')
def func4(self):
print('CCCCCCCC func4')
if __name__ == '__main__':
c1 = C()
c1.func1()
c1.func2()
c1.func3()
c1.func4()
特殊方法
__init__方法
• 实例化类实例时默认会调用的方法
class BearToy:
init(self, size, color):
self.size = size
slef.color = color
if name == ‘main’:
tidy = BearToy(‘small’, ‘orange’)
__str__方法
• 打印/显示实例时调用方法
• 返回字符串
class BearToy:
def init(self, size, color):
self.size = size
slef.color = color
def str(self):
return ‘<Bear: %s %s>’ % (self.size, self.color)
if name == ‘main’:
tidy = BearToy(‘small’, ‘orange’)
print(tidy)
__call__方法
• 用于创建可调用的实例
class BearToy:
def init(self, size, color):
self.size = size
slef.color = color
def call(self):
print(‘I am a %s bear’ % self.size)
if name == ‘main’:
tidy = BearToy(‘small’, ‘orange’)
print(tidy)
class Book:
def __init__(self, title, author):
self.title = title
self.author = author
def __str__(self):
return '<<%s>>' % self.title
def __call__(self):
print('<<%s>>是%s编写的' % (self.title, self.author))
if __name__ == '__main__':
pybook = Book('Python基础教程', 'Magnus') #调用__init__
print(pybook) #调用__str__
pybook() #调用__call__
re模块
正则表达式
匹配单个字符
记号 | 说明 |
---|---|
. | 匹配任意字符(换行符除外) |
[…x-y…] | 匹配字符组里的任意字符 |
[^…x-y…] | 匹配不在字符组里的任意字符 |
\d | 匹配任意数字,与[0-9]同义 |
\w | 匹配任意数字字母字符,与[0-9a-zA-Z_]同义 |
\s | 匹配空白字符,与[ \r\v\f\t\n]同义 |
匹配一组字符
记号 | 说明 |
---|---|
literal | 匹配字符串的值,匹配多个需要转义,(/tom|tam|t2m) |
re1/re2 | 匹配正则表达式re1或re2 |
* | 匹配前面出现的正则表达式零次或多次 |
+ | 匹配前面出现的正则表达式一次或多次,匹配之前要转义(/t+m) |
? | 匹配前面出现的正则表达式零次或一次,匹配之前需要转义(/t?m) |
{M,N} | 匹配前面出现的正则表达式至少M次最多N次,匹配之前,需要对{}进行转义(/t{3,5}m) |
>>> import re #在food的开头匹配f..,匹配到返回匹配对象,否则返回None(了解)
>>> re.match('f..', 'food')
<_sre.SRE_Match object; span=(0, 3), match='foo'>
>>> print(re.match('f..', 'seafood'))
None
>>> re.search('f..','seafood') #在字符串中匹配f..
<_sre.SRE_Match object; span=(3, 6), match='foo'>
>>> m = re.search('f..', 'seafood')
>>> m.group() #匹配到之后,用group方法返回匹配结果
'foo'
>>> re.findall('f..', 'seafood is food') #findter返回的是由匹配对象构成的生成器
['foo', 'foo']
>>> list(re.finditer('f..', 'seafood is food'))
[<_sre.SRE_Match object; span=(3, 6), match='foo'>, <_sre.SRE_Match object; span=(11, 14), match='foo'>]
>>> for m in re.finditer('f..', 'seafood is food'):
... m.group()
...
'foo'
'foo'
其他元字符
记号 | 说明 |
---|---|
$ | 匹配字符串的结尾 |
\b | 匹配单词的边界 |
() | 对正则表达式分组 |
^ | 匹配字符串的开始 |
\nn | 匹配已保存的子组 |
例:为mac地址加:分隔
192.168.1.1 00:0C:59:12:3E:25
192.168.1.2 26:FA:51:5D:45:15
~
~
~
~
:%s/\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)$/\1:\2:\3:\4:\5:\6/
核心函数和方法
match函数
• 尝试用正则表达式模式从字符串的开头匹配,如果匹配成功,则返回一个匹配对象;否则返回None
import re
m = re.match(‘foo’, ‘food’) #成功匹配
print(m)
<_sre.SRE_Match object; span=(0, 3), match=‘foo’>
m = re.match(‘foo’, ‘seafood’) #未能匹配
print(m)
None
search函数
• 在字符串中查找正则表达式模式的第一次出现,如果匹配成功,则返回一个匹配对象;否则返回None
import re
m = re.search(‘foo’, ‘food’)
print(m)
<_sre.SRE_Match object; span=(0, 3), match=‘foo’>
m = re.search(‘foo’, ‘seafood’) #可以匹配在字符中间的模式
print(m)
<_sre.SRE_Match object; span=(3, 6), match=‘foo’>
group方法
• 使用match或search匹配成功后,返回的匹配对象可以通过group方法获得匹配内容
import re
m = re.match(‘foo’, ‘food’)
print(m.group())
foo
m = re.search(‘foo’, ‘seafood’)
m.group()
‘foo’
findall函数
• 在字符串中查找正则表达式模式的所有(非重复)出现;返回一个匹配对象的列表
import re
m = re.search(‘foo’, ‘seafood is food’)
print(m.group()) #search只匹配模式的第一次出现
foo
m = re.findall(‘foo’, ‘seafood is food’) #获得全部的匹配项
print(m)
[‘foo’, ‘foo’]
finditer函数
• 和findall()函数有相同的功能,但返回的不是列表而是迭代器;对于每个匹配,该迭代器返回一个匹配对象
import re
m = re.finditer(‘foo’, ‘seafood is food’)
for item in m:
… print(item.group())
…
foo
foo
compile函数
• 对正则表达式模式进行编译,返回一个正则表达式对
• 不是必须要用这种方式,但是在大量匹配的情况下,
可以提升效率
import re
patt = re.compile(‘foo’)
m = patt.match(‘food’)
print(m.group())
foo
split方法
• 根据正则表达式中的分隔符把字符分割为一个列表,并返回成功匹配的列表
• 字符串也有类似的方法,但是正则表达式更加灵活
import re #使用 . 和 - 作为字符串的分隔符
mylist = re.split(’.|-’, ‘hello-world.data’)
print(mylist)
[‘hello’, ‘world’, ‘data’]
>>> re.split('-|\.', 'hello-world.tar.gz') #以.和-作为分隔符切割字符串
['hello', 'world', 'tar', 'gz']
>>> re.sub('x', 'tedu', 'Hello x. Hi x') #把x替换成tedu
'Hello tedu. Hi tedu'
# 为了提升效率,建议将正则表达式的模式先编译
>>> patt = re.compile('f..') #先编译
>>> m = patt.search('seafood') #在匹配
>>> m.group() #取出
'foo'
>>> patt.findall('seafood is food')
['foo', 'foo']
sub方法
• 把字符串中所有匹配正则表达式的地方替换成新的字符串
import re
m = re.sub(‘X’, ‘Mr. Smith’, ‘attn: X\nDear X’)
print(m)
attn: Mr. Smith
Dear Mr. Smith
例:分析apache访问日志
• 编写一个apche日志分析脚本
- 统计每个客户端访问apache服务器的次数
- 将统计信息通过字典的方式显示出来
- 分别统计客户端是Firefox和MSIE的访问次数
- 分别使用函数式编程和面向对象编程的方式实现
import re #函数式编程实现
def count_patt(fname, patt):
result = {} #保存结果
cpatt = re.compile(patt) #编译模式,提高效率
with open(fname) as fobj:
for line in fobj:
m = cpatt.search(line)
if m: #如果匹配到了
key = m.group()
result[key] = result.get(key, 0) + 1
return result
if __name__ == '__main__':
fname = 'access_log'
ip = '^(\d+.){3}\d+' #匹配三次数字.之后匹配一次数字
br = 'Firefox|MSIE|Chrome' #匹配浏览器名称字符
result1 = count_patt(fname, ip)
result2 = count_patt(fname, br)
print(result1)
print(result2)
面向对象编程模式:
import re
class CountPatt:
def __init__(self, fname):
self.fname = fname
def count_patt(self, patt):
result = {} #保存结果
cpatt = re.compile(patt) #编译模式,提高效率
with open(self.fname) as fobj:
for line in fobj:
m = cpatt.search(line)
if m: #如果匹配到了
key = m.group()
result[key] = result.get(key, 0) + 1
return result
if __name__ == '__main__':
fname = 'access_log'
ip = '^(\d+.){3}\d+' #匹配三次数字.之后匹配一次数字
br = 'Firefox|MSIE|Chrome' #匹配浏览器名称字符
cp1 = CountPatt(fname)
result1 = cp1.count_patt(ip)
result2 = cp1.count_patt(br)
print(result1)
print(result2)
print('*' * 30)
cp2 = CountPatt('/etc/passwd')
result3 = cp2.count_patt('nologin$|bash$')
print(result3)import re
class CountPatt:
def __init__(self, fname):
self.fname = fname
def count_patt(self, patt):
result = {} #保存结果
cpatt = re.compile(patt) #编译模式,提高效率
with open(self.fname) as fobj:
for line in fobj:
m = cpatt.search(line)
if m: #如果匹配到了
key = m.group()
result[key] = result.get(key, 0) + 1
return result
if __name__ == '__main__':
fname = 'access_log'
ip = '^(\d+.){3}\d+' #匹配三次数字.之后匹配一次数字
br = 'Firefox|MSIE|Chrome' #匹配浏览器名称字符
cp1 = CountPatt(fname)
result1 = cp1.count_patt(ip)
result2 = cp1.count_patt(br)
print(result1)
print(result2)
print('*' * 30)
cp2 = CountPatt('/etc/passwd')
result3 = cp2.count_patt('nologin$|bash$')
print(result3)
字典排序: 字典本身没有顺序,不能排序,需要将字典转化为其他序列类型,才能排序
复杂列表排序:列表的sort方法,支持一个名为key的参数,key应该是一个函数,该函数处理列表的每一项,处理结果作为排序依据
>>> result = {'172.40.58.150': 10, '172.40.58.124': 6, '172.40.58.101': 10, '127.0.0.1': 121, '192.168.4.254': 103, '192.168.2.254': 110, '201.1.1.254': 173, '201.1.2.254': 119, '172.40.0.54': 391, '172.40.50.116': 244}
>>> alist = list(result.items()
... )
>>> alist
[('172.40.58.150', 10), ('172.40.58.124', 6), ('172.40.58.101', 10), ('127.0.0.1', 121), ('192.168.4.254', 103), ('192.168.2.254', 110), ('201.1.1.254', 173), ('201.1.2.254', 119), ('172.40.0.54', 391), ('172.40.50.116', 244)]
>>> def get_second(seq):
... return seq[-1]
...
>>> alist.sort(key=get_second)
>>> alist
[('172.40.58.124', 6), ('172.40.58.150', 10), ('172.40.58.101', 10), ('192.168.4.254', 103), ('192.168.2.254', 110), ('201.1.2.254', 119), ('127.0.0.1', 121), ('201.1.1.254', 173), ('172.40.50.116', 244), ('172.40.0.54', 391)] #上面写法,可以用下一行解决,并实现降序排列
>>> alist.sort(key=lambda seq: seq[-1], reverse=True)
>>> alist
[('172.40.0.54', 391), ('172.40.50.116', 244), ('201.1.1.254', 173), ('127.0.0.1', 121), ('201.1.2.254', 119), ('192.168.2.254', 110), ('192.168.4.254', 103), ('172.40.58.150', 10), ('172.40.58.101', 10), ('172.40.58.124', 6)]
>>>