OOP介绍
一、什么是OOP
1、编程的发展已经从简单控制流中按步的指令序列进入到更有组织的方式中
2、结构化的或过程性编程可以让我们把程序组织成逻辑块,以便重复或重用
3、面向对象编增强了结构化编程,实现了数据与动作的融合
4、数据层和逻辑层由一个可用以创建这些对象的简单抽象层来描述
二、常用术语
1、抽象/实现:抽象指对现实世界问题和实体的本质表现,行为和特征建模,建立一个相关的子集,可以用于描绘程序结构,从而实现这种模型
2、封装/接口:封装描述了对数据/信息进行隐藏的观念,它对数据属性提供接口和访问函数;客户端根本就不
需要知道在封装之后,数据属性是如何组织的,这就需要在设计时,对数据提供相应的接口
3、合成:合成扩充了对类的描述,使得多个不同的类合成为一个大的类,来解决现实问题
4、派生/继承:派生描述了子类的创建,新类保留已存类类型中所有需要的数据和行为,但允许修改或者其它的自定义操作,都不会修改原类的定义
5、多态:指出了对象如何通过他们共同的属性和动作来
操作及访问,而不需考虑他们具体的类
6、泛化/特化:泛化表示所有子类与其父类及祖先类有一样的特点;特化描述所有子类的自定义,也就是什么属性让它与其祖先类不同
7、自省/反射:自省表示给予程序员某种能力来进行像“手工类型检查”的工作,它也被称为反射。这个性质展示了某对象是如何在运行期取得自身信息的
self和__init__的含义
self:自己、本身
1、实例本身
2、此处的self是对象,是当前类的实例
python中规定:函数的第一个参数必须是实例本身,并且建议把改参数的名字写成self
类
一、创建类
1、类是一种数据结构,我们可以用它来定义对象,对象把数据值和行为特性融合在一起
2、python使用class关键字来创建类
3、通常类名的第一个字母大写
class ClassName(bases):
'class documentation string' #'类文档字符串'
class_suite #类体
二、类的数据属性
1、数据属性仅仅是所定义的类的变量
2、这种属性已是静态变量,或者是静态数据。它们表示这些数据是与它们所属的类对象绑定的,不依赖于任何类实例
3、静态成员通常仅用来跟踪与类相关的值
>>> class C(object):
... foo = 100
...
>>> print C.foo
100
>>> C.foo += 1
>>> print C.foo
101
三、特殊的类属性
实例
一、创建实例
1、如果说类是一种数据结构定义类型,那么实例则声明了一个这种类型的变量
2、类被实例化得到实例,该实例的类型就是这个被实例化的类
3、创建实例与调用函数类似,调用一个类就创建了它的一个实例
>>> class C(object):
... foo = 100
>>> c = C()
>>> print c
<__main__.C object at 0x7f4f012c1ad0>
二、实例属性
1、实例仅拥有数据属性,数据属性只是与某个类的实例相关联的数据值,并且可通过句点属性标识法来访问
2、设置实例的属性可以在实例创建后任意时间进行,也可以在能够访问实例的代码中进行
>>> class C(object):
... pass
...
>>> c = C()
>>> c.hi = 'hello'
>>> print c.hi
hello
三、特殊的实例属性
>>> class C(object):
... pass
>>> c = C()
>>> c.hi = 'hello'
>>> c.__dict__
{'hi': 'hello'}
>>> print c.__class__
<class '__main__.C'>
四、类与实例属性对比
1、类属性仅是与类相关的数据值,类属性和实例无关
2、静态成员(类的数据属性)不会因为实例而改变它们的值,除非实例中显式改变它
3、类和实例都是名字空间,各不相同
>>> class C(object):
... version = 1.0
>>> c = C()
>>> C.version += 0.1
>>> print 'in C version=%s, in c version=%s' % (C.version, c.version)
in C version=1.1, in c version=1.1
>>> c.version += 0.1
>>> print 'in C version=%s, in c version=%s' % (C.version, c.version)
in C version=1.1, in c version=1.2
组合和派生
方法
一、__init__方法
1、__init__()是实例创建后第一个被调用的方法
2、设置实例的属性可以在实例创建后任意时间进行,但是通常情况下优先在__init__方法中实现
3、__init__也叫构造方法,它在创建一个对象时被自动调用
#!/usr/bin/env python
class AddrBook(object):
def __init__(self, nm, ph):
self.name = nm
self.phone = ph
>>> import AddrBook
>>> bob = AddrBook.AddrBook('bob green', '13322334455')
二、绑定方法
1、方法仅仅是类内部定义的函数,方法只有在其所属的类拥有实例时,才能被调用
2、任何一个方法定义中的第一个参数都是变量self,它表示调用此方法的实例对象
#!/usr/bin/env python
class AddrBook(object):
def getPhone(self):
return self.phone
>>> AddrBook.AddrBook.getPhone() #不能直接调用类的方法
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method getPhone() must be called with
AddrBook instance as first argument (got nothing instead)
三、非绑定方法
1、调用非绑定方法并不经常用到
2、需要调用一个没有实例的类中的方法的一个主要场景是你在派生一个子类,而且要覆盖父类的方法
#!/usr/bin/env python
class AddrBook(object):
def __init__(self, nm, ph):
self.name = nm
self.phone = ph
def getPhone(self):
return self.phone
class EmplAddrBook(AddrBook):
def __init__(self, nm, ph, id, em):
AddrBook.__init__(self, nm, ph)
self.emid = id
self.email = em
组合
一、什么是组合
1、类被定义后,目标就是要把它当成一个模块来使用,并把这些对象嵌入到你的代码中去
2、组合就是让不同的类混合并加入到其它类中来增加功能和代码重用性
3、可以在一个大点的类中创建其它类的实例,实现一些其它属性和方法来增强对原来的类对象
二、实现组合
1、创建复合对象、应用组合可以实现附加的功能
2、例如,通过组合实现上述地址薄功能的增强
#!/usr/bin/env python
class Info(object):
def __init__(self, ph, em, qq):
self.phone = ph
self.email = em
self.qq = qq
def updatePhone(self, newph):
self.phone = newph
class Contact(object):
def __init__(self, name, ph, em, qq):
self.name = name
self.info = Info(ph, em, qq)
子类和派生
一、创建子类
1、当类之间有显著的不同,并且较小的类是较大的类所需要的组件时组合表现得很好;但当设计“相同的类
但有一些不同的功能”时,派生就是一个更加合理的选择了
2、OOP 的更强大方面之一是能够使用一个已经定义好的类,扩展它或者对其进行修改,而不会影响系统中使用现存类的其它代码片段
3、OOD(面向对象设计)允许类特征在子孙类或子类中进行继承
4、创建子类只需要在圆括号中写明从哪个父类继承即可
>>> class A(object):
... def printStar(self):
... print '*' * 20
...
>>> class B(A):
... pass
...
二、继承
1、继承描述了基类的属性如何“遗传”给派生类
2、子类可以继承它的基类的任何属性,不管是数据属性还是方法
>>> class A(object):
... def printStar(self):
... print '*' * 20
...
>>> class B(A):
... pass #类B并没有定义任何方法
...
>>> b = B() #b是B的实例
>>> b.printStar() #B继承了类A的方法,实例b也就具备了该功能
*******************
三、通过继承覆盖方法
1、如果子类中有和父类同名的方法,父类方法将被覆盖
2、如果需要访问父类的方法,则要调用一个未绑定的父类方法,明确给出子类的实例
>>> class P(object):
... def foo(self):
... print 'in P-foo'
>>> class C(P):
... def foo(self):
... print 'in C-foo'
>>> c = C()
>>> c.foo()
in C-foo
>>> P.foo(c)
in P-foo
四、多重继承
1、python允许多重继承,即一个类可以是多个父类的子类,子类可以拥有所有父类的属性
>>> class A(object):
... def foo(self):
... print 'foo method'
>>> class B(object):
... def bar(self):
... print 'bar method'
>>> class C(A, B):
... pass
>>> c = C()
>>> c.foo()
foo method
>>> c.bar()
bar method
五、类和实例的内建函数
六、私有化
1、python为类元素(属性和方法)的私有性提供初步的形式,由双下划线开始的属性在运行时被“混淆”,所以直接访问是不允许的
>>> class C(object):
... def __init__(self, nm):
... self.__name = nm
... def getName(self):
... return self.__name
>>> c = C('bob')
>>> c.getName()
'bob'
>>> c.__name #私有化的数据不能在外部直接使用
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'C' object has no attribute '__name'
练习:
>>> class MyClass(object):
def pstar(self):
print '*' * 20
>>>
>>> a = MyClass()
>>> a.pstar
<bound method MyClass.pstar of <__main__.MyClass object at 0x02DE24F0>>
>>> a.pstar()
********************
>>>
addrbook1.py内容:
#!/usr/bin/env python
#coding:utf8
class AddrBook(object):
def __init__(self,nm,ph):
self.name = nm
self.phone = ph
if __name__ == "__main__":
bob = AddrBook("Bob Green","15011223344")
alice = AddrBook("Alice Smith","13355667788")
print "%s: %s" % (bob.name,bob.phone)
print "%s: %s" % (alice.name,alice.phone)
执行结果:
Bob Green: 15011223344
Alice Smith: 13355667788
addrbook2.py内容:
#!/usr/bin/env python
#coding:utf8
class AddrBook(object):
def __init__(self,nm,ph):
self.name = nm
self.phone = ph
def get_name(self):
return self.name
def get_phone(self):
return self.phone
def update_phone(self,newph):
self.phone = newph
print "Now ,%s phone number is : %s" %(self.name,self.phone)
if __name__ == "__main__":
bob = AddrBook("Bob Green","15011223344")
alice = AddrBook("Alice Smith","13355667788")
print "%s: %s" % (bob.get_name(),bob.get_phone())
print "%s: %s" % (alice.get_name(),alice.get_phone())
bob.update_phone("18612345678")
print "%s: %s" % (bob.get_name(),bob.get_phone())
执行结果:
Bob Green: 15011223344
Alice Smith: 13355667788
Now ,Bob Green phone number is : 18612345678
Bob Green: 18612345678
addrbook3.py内容:
#!/usr/bin/env python
#coding:utf8
class Info(object):
def __init__(self,ph,em,qq):
self.phone = ph
self.email = em
self.qq = qq
def get_phone(self):
return self.phone
def update_phone(self,newph):
self.phone =newph
class AddrBook(object):
def __init__(self,nm,ph,em,qq):
self.name = nm
self.info = Info(ph,em,qq)
if __name__ == "__main__":
bob = AddrBook("Bob Green","15011223344","bob@tedu.cn","12423234")
print bob.info.get_phone()
执行结果:
15011223344
addrbook4.py内容:
#!/usr/bin/env python
#coding:utf8
class AddrBook(object):
def __init__(self,nm,ph):
self.name = nm
self.phone = ph
self.city = "Beijing"
def get_name(self):
return self.name
def get_phone(self):
return self.phone
def update_phone(self,newph):
self.phone = newph
print "Now ,%s phone number is : %s" %(self.name,self.phone)
class EmplAddrBook(AddrBook):
pass
if __name__ == "__main__":
bob = EmplAddrBook("Bob Green","13344556677")
print bob.get_phone()
执行结果:
13344556677
addrbook5.py内容:
#!/usr/bin/env python
#coding:utf8
class AddrBook(object):
def __init__(self,nm,ph):
self.name = nm
self.phone = ph
def get_name(self):
return self.name
def get_phone(self):
return self.phone
def update_phone(self,newph):
self.phone = newph
print "Now ,%s phone number is : %s" %(self.name,self.phone)
class EmplAddrBook(AddrBook):
def __init__(self,nm,ph,em,eid):
AddrBook.__init__(self, nm, ph)
self.email =em
self.eid = eid
def get_email(self):
return self.email
if __name__ == "__main__":
bob = EmplAddrBook("Bob Green","13344556677","bob@tedu.cn","1023")
print bob.get_phone()
print bob.get_email()
执行结果:
13344556677
bob@tedu.cn
addrbook6.py内容:
#!/usr/bin/env python
#coding:utf8
class AddrBook(object):
def __init__(self,nm,ph):
self.name = nm
self.phone = ph
def get_name(self):
return self.name
def get_phone(self):
return self.phone
def update_phone(self,newph):
self.phone = newph
print "Now ,%s phone number is : %s" %(self.name,self.phone)
class EmplAddrBook(AddrBook):
company = "Tarena"
def __init__(self,nm,ph,em,eid):
AddrBook.__init__(self, nm, ph)
self.email =em
self.eid = eid
def get_email(self):
return self.email
if __name__ == "__main__":
bob = EmplAddrBook("Bob Green","13344556677","bob@tedu.cn","1023")
print bob.get_phone()
print bob.get_email()
print EmplAddrBook.company
print bob.company
执行结果:
13344556677
bob@tedu.cn
Tarena
Tarena
books.py内容:
#!/usr/bin/env python
#coding:utf8
class Book(object):
def __init__(self,title,author):
self.title = title
self.author = author
def __str__(self):
return self.title
def __call__(self):
print "%s is written by %s" % (self.title,self.author)
if __name__ == "__main__":
pybook = Book("Core Python","Wesley")
print pybook #因为类中定义了__str__方法,此处打印出的内容是__str__的返回值
pybook() #调用pybook就是执行__call__方法中的代码
执行结果:
Core Python
Core Python is written by Wesley
number.py内容:
#!/usr/bin/env python
#coding:utf8
class MyNumber(object):
def __init__(self,num):
self.number= num
def __add__(self,other):
return self.number + other
def __radd__(self,other):
return self.number + other
def __sub__(self,other):
return self.number - other
def __rsub__(self,other):
return other - self.number
if __name__ == "__main__":
digit = MyNumber(10)
print digit + 10
print 10 + digit
print digit - 100
print 100 - digit
执行结果:
#!/usr/bin/env python
#coding:utf8
class MyNumber(object):
def __init__(self,num):
self.number= num
def __add__(self,other):
return self.number + other
def __radd__(self,other):
return self.number + other
def __sub__(self,other):
return self.number - other
def __rsub__(self,other):
return other - self.number
if __name__ == "__main__":
digit = MyNumber(10)
print digit + 10
print 10 + digit
print digit - 100
print 100 - digit
执行结果:
20
20
-90
90
mylist.py内容:
#!/usr/bin/env python
#coding:utf8
class MyList(list):
def __getitem__(self,offset):
return list.__getitem__(self,offset-1)
if __name__ == "__main__":
alist = MyList([10,20])
print alist[1]
alist.append(30)
print alist
print alist[0]
执行结果:
10
[10, 20, 30]
30
编写酒店类
编写 hotel.py 脚本,主要要求如下:
1、用于计算住宿开销
2、酒店有会员卡可以打九折
3、每天早餐 15 元
4、根据住宿天数返加总费用
方案
用于计算酒店消费的程序,可以创建一个酒店的类。不同酒店的住宿费、餐费和折扣不
一样,可以将这些参数作为酒店的属性绑定到特定的酒店对象上。
为了方便计算消费者的费用,可以编写一个方法,根据住宿的时间来计算其总费用。
#!/usr/bin/env python
#coding:utf8
class hotel(object):
def __init__(self, rm = 200, fd = 15, cf = 1): #设置基础费用
self.room = rm
self.food = fd
self.cutoff = cf
def costCalc(self, days = 1): #默认计算一天的消费
return (self.room * self.cutoff + self.food) * days
def test():
bj = hotel(cf = 0.9) #假设在北京住宿,折扣为 9 折
print bj.costCalc(3)
sh = hotel() #假设在上海住宿,没有折扣
print sh.costCalc(5)
if __name__ == '__main__':
test()
执行结果:
585.0
1075
分析 apache 访问日志
写用于分析 apache 日志的脚本,主要要求如下:
1、统计每个客户端访问 apache 服务器的次数
2、将统计信息通过字典的方式显示出来
3、分别统计客户端是 Firefox 和 MSIE 的访问次数
4、分别使用函数式编程和面向对象编程的方式实现
方案
涉及到文本处理时,正则表达式将是一个非常强大的工具。匹配客户端的 IP 地址,可以使用正则表达式的元字符,匹配字符串可以直接使用字符的表面含义。
入门级程序员的写法,使用顺序的结构,直接编写。这种方法虽然可以得出结果,但是代码难以重用。参考步骤一。
进阶的写法可以采用函数式编程,方便日后再次使用。参考步骤二。
最后,还可以使用 OOP 的编程方法,先定义一个统计类,该类将正则表达式作为它的数据属性。再定义一个方法,从指定的文件中搜索正则表达式出现的次数,并将其存入到一
个字典中。参考步骤三
步骤
实现此案例需要按照如下步骤进行。
步骤一:简单实现
#!/usr/bin/env python
#coding:utf-8
import re
logfile = '/var/log/httpd/access_log'
cDict = {}
patt_ip = '^\d+\.\d+\.\d+\.\d+' #定义匹配 IP 地址的正则表达式
with open(logfile) as f:
for eachLine in f:
m = re.search(patt_ip, eachLine)
if m is not None:
ipaddr = m.group()
#如果 IP 地址已在字典中,将其值加 1,否则初始值设置为 1
cDict[ipaddr] = cDict.get(ipaddr, 0) + 1
print cDict
执行结果:需要客户端访问
root@host-192-168-3-6 test]# python countweb.py
{'61.140.232.156': 11}
步骤二:使用函数式编程实现
#!/usr/bin/env python
#coding:utf-8
import re
def countPatt(patt, fname): #定义可以在指定文件中搜索指定字符串的函数
cDict = {}
with open(fname) as f:
for eachLine in f:
m = re.search(patt, eachLine)
if m is not None:
k = m.group()
cDict[k] = cDict.get(k, 0) + 1
return cDict
def test():
logfile = '/var/log/httpd/access_log'
patt_ip = '^\d+\.\d+\.\d+\.\d+'
print countPatt(patt_ip, logfile)
patt_br = 'Firefox|MSIE'
print countPatt(patt_br, logfile)
if __name__ == '__main__':
test()
执行结果:
[root@host-192-168-3-6 test]# python countweb.py
{'61.140.232.156': 11}
{'Firefox': 11}
步骤三:使用 OOP 方式实现
#!/usr/bin/env python
#coding:utf-8
import re
class MyCount(object): #定义类,将正则表达式作为其数据属性
def __init__(self, patt):
self.patt = patt
def countPatt(self, fname): #定义统计正则表达式的方法
cDict = {}
with open(fname) as f:
for eachLine in f:
m = re.search(self.patt, eachLine)
if m is not None:
k = m.group()
cDict[k] = cDict.get(k, 0) + 1
return cDict
def test():
patt_ip = '\d+\.\d+\.\d+\.\d+'
logfile = '/var/log/httpd/access_log'
testip = MyCount(patt_ip)
print testip.countPatt(logfile)
patt_br = 'Firefox|MSIE'
testbr = MyCount(patt_br)
print testbr.countPatt(logfile)
if __name__ == '__main__':
test()
执行结果:
[root@host-192-168-3-6 test]# python countweb.py
{'61.140.232.156': 11}
{'Firefox': 11}
方法1:
step1:
>>> adict= {}
>>> print adict.get('a',0)
0
>>> adict['a'] = adict.get('a',0)
>>> adict
{'a': 0}
>>> adict['b'] = adict.get('b',0) + 1
>>> adict
{'a': 0, 'b': 1}
>>> adict['b'] = adict.get('b',0) + 1
>>> adict
{'a': 0, 'b': 2}
>>>
[root@host-192-168-3-6 ~]# yum -y install httpd
[root@host-192-168-3-6 ~]# systemctl start httpd
[root@host-192-168-3-6 ~]# systemctl status httpd
[root@host-192-168-3-6 ~]# tail -f /var/log/httpd/access_log
count_patt1.py内容:
#!/usr/bin/env python
#coding:utf8
import re
def count_patt(fname,patt):
patt_dict = {}
cpatt = re.compile(patt)
with open(fname) as fobj:
for line in fobj:
m = cpatt.search(line)
if m:
key = m.group()
#如果key不在字典中,设置值为1,否则加1
patt_dict[key] = patt_dict.get(key,0) + 1
'''
if key not in patt_dict:
patt_dict[key] = 1
else:
patt_dict[key] += 1
'''
return patt_dict
if __name__ == "__main__":
log_file = "/var/log/httpd/access_log"
ip_patt = "^(\d+\.){3}\d+"
br_patt = "Firefox|MSIE|Chrome|QQBrowser"
print count_patt(log_file, ip_patt)
print count_patt(log_file, br_patt)
执行结果:
[root@host-192-168-3-6 ~]# python count_patt.py
{'113.67.74.155': 250}
{'Chrome': 95, 'Firefox': 24, 'QQBrowser': 22}
count_patt2.py内容:
#!/usr/bin/env python
#coding:utf8
import re
def count_patt(fname,patt):
patt_dict = {}
cpatt = re.compile(patt)
with open(fname) as fobj:
for line in fobj:
m = cpatt.search(line)
if m:
key = m.group()
#如果key不在字典中,设置值为1,否则加1
patt_dict[key] = patt_dict.get(key,0) + 1
'''
if key not in patt_dict:
patt_dict[key] = 1
else:
patt_dict[key] += 1
'''
return patt_dict
def sort(adict):
alist = []
patt_list = adict.items()
for i in range(len(patt_list)):
greater = patt_list[0]
for j in range(len(patt_list[1:])):
greater = greater if greater[1] >=patt_list[j+1][1] else patt_list[j +1]
alist.append(greater)
patt_list.remove(greater)
return alist
if __name__ == "__main__":
log_file = "/var/log/httpd/access_log"
ip_patt = "^(\d+\.){3}\d+"
br_patt = "Firefox|MSIE|Chrome|QQBrowser"
print count_patt(log_file, br_patt)
ip_count = count_patt(log_file, ip_patt)
print ip_count
print sort(ip_count)
执行结果:
[root@host-192-168-3-6 ~]# python count_patt.py
{'113.67.74.155': 264}
{'Chrome': 100, 'Firefox': 33, 'QQBrowser': 22}
[root@host-192-168-3-6 ~]# python count_patt.py
{'Chrome': 100, 'Firefox': 33, 'QQBrowser': 22}
{'113.67.74.155': 268}
[('113.67.74.155', 268)]
count_patt3.py内容:
#!/usr/bin/env python
#coding:utf8
import re
import collections
class CountPatt(object):
def __init__(self,patt):
self.cpatt = re.compile(patt)
def count_patt(self,fname):
c = collections.Counter()
with open(fname) as fobj:
for line in fobj:
m = self.cpatt.search(line)
if m:
c.update([m.group()])
return c
if __name__ == "__main__":
log_file = "/var/log/httpd/access_log"
ip_patt = "^(\d+\.){3}\d+"
br_patt = "Firefox|MSIE|Chrome|QQBrowser"
c1 = CountPatt(ip_patt)
print c1.count_patt(log_file)
print c1.count_patt(log_file).most_common(2)
c2 = CountPatt(br_patt)
print c2.count_patt(log_file)
print c2.count_patt(log_file).most_common(2)
执行结果:
[root@host-192-168-3-6 ~]# python count_patt2.py
Counter({'113.67.74.155': 311, '47.88.191.116': 1, '139.162.88.63': 1})
[('113.67.74.155', 311), ('47.88.191.116', 1)]
Counter({'Chrome': 130, 'Firefox': 39, 'QQBrowser': 26})
[('Chrome', 130), ('Firefox', 39)]
[root@host-192-168-3-6 ~]#