面向过程编程最易被初学者接受,其每每用一长段代码来实现指定功能,开发过程当中最多见的操做就是粘贴复制,即:将以前实现的代码块复制到现需功能处。python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
while
True
:
if
cpu利用率 >
90
%
:
#发送邮件提醒
链接邮箱服务器
发送邮件
关闭链接
if
硬盘使用空间 >
90
%
:
#发送邮件提醒
链接邮箱服务器
发送邮件
关闭链接
if
内存占用 >
80
%
:
#发送邮件提醒
链接邮箱服务器
发送邮件
关闭链接
|
随着时间的推移,开始使用了函数式编程,加强代码的重用性和可读性,就变成了这样:编程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
def
发送邮件(内容)
#发送邮件提醒
链接邮箱服务器
发送邮件
关闭链接
while
True
:
if
cpu利用率 >
90
%
:
发送邮件(
'CPU报警'
)
if
硬盘使用空间 >
90
%
:
发送邮件(
'硬盘报警'
)
if
内存占用 >
80
%
:
发送邮件(
'内存报警'
)
|
今天咱们来学习一种新的编程方式:面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)
注:Java和C#来讲只支持面向对象编程,而python比较灵活即支持面向对象编程也支持函数式编程服务器
面向对象编程是一种编程方式,此编程方式的落地须要使用 “类” 和 “对象” 来实现,因此,面向对象编程其实就是对 “类” 和 “对象” 的使用。python2.7
类就是一个模板,模板里能够包含多个函数,函数里实现一些功能ide
对象则是根据模板建立的实例,经过实例对象能够执行类中的函数函数式编程
ps:类中的函数第一个参数必须是self(详细见:类的三大特性之封装)
类中定义的函数叫作 “方法”函数
#!usr/bin/env python # -*- coding:utf-8 -*- #建立类 class Foo: def Bar(self): print('Bar') def Hello(self,name): print('hello %s' %name) #根据类Foo建立对象 foo = Foo() foo.Bar() #执行Bar方法 foo.Hello('SUNXIAO') #执行Hello方法
内部实际传参数的逻辑: foo.Hello('SUNXIAO') --> Hello(self = foo,name = 'SUNXIAO')学习
诶,你在这里是否是有疑问了?使用函数式编程和面向对象编程方式来执行一个“方法”时函数要比面向对象简便this
观察上述对比答案则是确定的,而后并不是绝对,场景的不一样适合其的编程方式也不一样。spa
总结:
1)函数式的应用场景 --> 各个函数之间是独立且无共用的数据
2)面向对象的使用场景:
A、同一类型的方法具备相同的参数时,直接封装到对象中便可
B、把类当作模板,建立多个对象,对象内封装的数据能够不同
self详解
#!usr/bin/env python # -*- coding:utf-8 -*- #建立类 class Foo: def Bar(self): print('Bar',self) #self打印:<__main__.Foo object at 0x000001EC4601ADA0> def Hello(self,name): print('hello %s' %name) #根据类Foo建立对象 foo1 = Foo() print(foo1) foo1.Bar() #执行Bar方法,打印出:Bar <__main__.Foo object at 0x000001EC4601ADA0> #由上面的代码结果能够看出,在执行Bar方法时,python自动将对象foo1传递了Bar方法做为第一个参数
面向对象的三大特性是指:封装、继承和多态。
1、封装
封装,顾名思义就是将内容封装到某个地方,之后再去调用被封装在某处的内容。
因此,在使用面向对象的封装特性时,须要:
第一步:将内容封装到某处
__init__(self)为构造方法,当建立对象时,首先执行的就是构造方法
__del__() 析构方法,当解释器销毁对象时自动调用该方法
self 是一个形式参数,当执行 obj1 = Foo('wupeiqi', 18 ) 时,self 等于 obj1
当执行 obj2 = Foo('alex', 78 ) 时,self 等于 obj2
因此,内容其实被封装到了对象 obj1 和 obj2 中,每一个对象中都有 name 和 age 属性,在内存里相似于下图来保存。
第二步:从某处调用被封装的内容
调用被封装的内容时,有两种状况:
一、经过对象直接调用被封装的内容
上图展现了对象 obj1 和 obj2 在内存中保存的方式,根据保存格式能够如此调用被封装的内容:对象.属性名
class Foo: def __init__(self,name,age): self.name = name self.age = age obj1 = Foo('sunxiao',28) print(obj1.name) #sunxiao print(obj1.age) #28 obj2 = Foo('sunyu','23') print(obj2.name) #sunyu print(obj2.age) #23
二、经过self间接调用被封装的内容
执行类中的方法时,须要经过self间接调用被封装的内容
class Foo: def __init__(self,name,age): self.name = name self.age = age def detail(self): print(self.name) print(self.age) obj1 = Foo('sunxiao',28) obj1.detail() # Python默认会将obj1传给self参数,即:obj1.detail(obj1),因此,此时方法内部的 self = obj1,即:self.name 是 wupeiqi ;self.age 是 18 obj2 = Foo('sunyu','23') obj2.detail() # Python默认会将obj2传给self参数,即:obj1.detail(obj2),因此,此时方法内部的 self = obj2,即:self.name 是 alex ; self.age 是 78
综上所述,对于面向对象的封装来讲,其实就是使用构造方法将内容封装到 对象 中,而后经过对象直接或者self间接获取被封装的内容。
练习一:在终端输出以下信息
- 小明,10岁,男,上山去砍柴
- 小明,10岁,男,开车去东北
- 小明,10岁,男,最爱大保健
- 老李,90岁,男,上山去砍柴
- 老李,90岁,男,开车去东北
- 老李,90岁,男,最爱大保健
- 老张...
函数式编程def kanchai(name,age,gender): print('%s,%s岁,%s,上山去砍柴' %(name,age,gender)) def qudongbei(name,age,gender): print('%s,%s岁,%s,去东北' %(name,age,gender)) def dabaojian(name,age,gender): print('%s,%s岁,%s,大保健' %(name,age,gender)) kanchai('小敏',20,'男') qudongbei('小明',18,'男') dabaojian('老张',65,'男')
面向对象编程class Person: def __init__(self,name,age,gender): self.name = name self.age = age self.gender = gender def kanchai(self): print('%s,%s岁,%s,上山去砍柴' %(self.name,self.age,self.gender)) def qudongbei(self): print('%s,%s岁,%s,开车去东北' %(self.name,self.age,self.gender)) def dabaojian(self): print('%s,%s岁,%s,最爱大保健' %(self.name,self.age,self.gender)) p1 = Person('xiaoming','18','男') p1.kanchai() p1.qudongbei() p2 = Person('老张',65,'男') p2.dabaojian()上述对比能够看出,若是使用函数式编程,须要在每次执行函数时传入相同的参数,若是参数多的话,又须要粘贴复制了... ;而对于面向对象只须要在建立对象时,将全部须要的参数封装到当前对象中,以后再次使用时,经过self间接去当前对象中取值便可。
练习二:游戏人生程序
一、建立三个游戏人物,分别是:
- 梅超风,女,18,初始战斗力1000
- 张三丰,男,20,初始战斗力1800
- 黄蓉,女,19,初始战斗力2500
二、游戏场景,分别:
- 草丛战斗,消耗200战斗力
- 自我修炼,增加100战斗力
- 多人游戏,消耗500战斗力
游戏人生#!usr/bin/env python #-*- coding:utf-8 -*- class Person: def __init__(self,name,gender,age,fight): self.name = name self.gender = gender self.age = age self.fight = fight def grassland(self): """注释:草丛战斗,消耗200战斗力""" self.fight -= 200 def practice(self): """注释:自我修炼,增加100战斗力""" self.fight += 100 def commandfight(self): """多人游戏:消耗500战斗力""" self.fight -= 500 def detail(self): temp = '姓名%s;性别%s;年龄%s;战斗力%s' %(self.name,self.gender,self.age,self.fight) print(temp) p1 = Person('sun','男',100,10000) p1.detail() p1.commandfight() p1.detail()
2、继承
继承,面向对象中的继承和现实生活中的继承相同,即:子能够继承父的内容。
例如:
猫能够:喵喵叫、吃、喝、拉、撒
狗能够:汪汪叫、吃、喝、拉、撒
若是咱们要分别为猫和狗建立一个类,那么就须要为 猫 和 狗 实现他们全部的功能,以下所示:
上述代码不难看出,吃、喝、拉、撒是猫和狗都具备的功能,而咱们却分别的猫和狗的类中编写了两次。若是使用 继承 的思想,以下实现:
动物:吃、喝、拉、撒
猫:喵喵叫(猫继承动物的功能)
狗:汪汪叫(狗继承动物的功能)
#!usr/bin/env python # -*- coding:utf-8 -*- class Animal: def eat(self): print('%s吃' %self.name) def drink(self): print('%s喝' %self.name) def shit(self): print('%s拉' %self.name) def pee(self): print('%撒' %self.name) class Cat(Animal): def __init__(self,name): self.name = name def cry(self): print('喵喵叫') class Dog(Animal): def __init__(self,name): self.name = name def cry(self): print('汪汪叫') dog1 = Dog('老李家的小狗') dog1.eat() dog1.cry() cat1 = Cat('小明家的小猫') cat1.drink() cat1.cry()
因此,对于面向对象的继承来讲,其实就是将多个类共有的方法提取到父类中,子类仅需继承父类而没必要一一实现每一个方法。
注:除了子类和父类的称谓,你可能看到过 派生类 和 基类 ,他们与子类和父类只是叫法不一样而已。
若是子类和父类都存在相同名称的方法,子类对象优先调用子类中的方法,父类对象优先调用父类的方法
那么问题又来了,多继承呢?
一、Python的类能够继承多个类,Java和C#中则只能继承一个类
二、Python2.7中的类若是继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先和广度优先
Python中经典类和新式类的区别:
区别主要体如今继承上:
Python的类能够继承多个类,Java和C#中则只能继承一个类
Python的类若是继承了多个类,那么其寻找方法的方式有两种
当类是经典类时,多继承状况下,会按照深度优先方式查找
当类是新式类时,多继承状况下,会按照广度优先方式查找
简单点说就是:经典类是纵向查找,新式类是横向查找
以下例:
#!usr/bin/env python # -*- coding:utf-8 -*- class Life(): def daily(self): print "繁衍" class Animal(Life): def daily(self): print "吃" print "喝" print "拉" print "撒" class Puru(Life): def __init__(self): pass class cat(Puru,Animal): def __init__(self): print "喵喵" a = cat() a.daily() # 执行结果是: # 喵喵 # 繁衍
#!usr/bin/env python # -*- coding:utf-8 -*- class Life(object): def daily(self): print "繁衍" class Animal(Life): def daily(self): print "吃" print "喝" print "拉" print "撒" class Puru(Life): def __init__(self): pass class cat(Puru,Animal): def __init__(self): print "喵喵" a = cat() a.daily() # 执行结果是: # 喵喵 # 吃 # 喝 # 拉 # 撒
经典类和新式类,从字面上能够看出一个老一个新,新的必然包含了不少的功能,也是以后推荐的写法,从写法上区分的话,若是 当前类或者父类继承了object类,那么该类即是新式类,不然即是经典类。
三、python3中再也不区分经典类和新式类,只有一种查找方式
#!usr/bin/env python # -*- coding:utf-8 -*- class A: def f1(self): print("A") def Bar(self): self.f1() class B(A): def f1(self): print("B") class C: def f1(self): print("C") class D(B,C): pass d = D() d.Bar() #B,在执行self.f1()时,由于是D建立的对象,因此也是按照D B A C的顺序查找的
子类执行父类构造方法的两种方式:
#!usr/bin/env python # -*- coding:utf-8 -*- class Animal: def __init__(self): print('A构造方法') self.ty = "动物" def aaa(self): print("aaa") class Cat(Animal): def __init__(self): print('B构造方法') self.n = '猫' #执行父类的构造方法,这样系统会按照上图的规则去父类找init构造方法,因此建议此种方法 super(Cat,self).__init__() #或者用下面方法执行,此种方法随意性比较强,容易混乱 #Animal.__init__(self) b = Cat() # B构造方法 # A构造方法
补充:
子类调用父类的构造方法__init__()的两种方法:
#!/ufr/bin/env python # -*- coding:utf-8 -*- class Animal: def __init__(self): print('A构造方法') self.ty = '动物' class Cat(Animal): def __init__(self): print('B构造方法') self.n = "猫" super(Cat, self).__init__() #第一种调用父类的构造方法,推荐这种使用方法,按照python的规则(顺序)去调用 #Animal.__init__(self) #第二种调用父类的构造方法 c = Cat() print(c.__dict__) #结果: # B构造方法 # A构造方法 #{'n': '猫', 'ty': '动物'}
利用反射导入模块、查找类、建立对象、查找字段
3、多态
Pyhon不支持Java和C#这一类强类型语言中多态的写法,由于python的变量是不区分类型的,因此python原生支持多态,以下例:
#!usr/bin/env python # -*- coding:utf-8 -*- class C1: def f1(self): print('C1') class C2: def f1(self): print('C2') def f2(arg): arg.f1() c1 = C1() c2 = C2() f2(c1) #C1 f2(c2) #C2
练习实例:
#!usr/bin/env python # -*- coding:utf-8 -*- class Weapon: def __init__(self,name,limit_level,attack,duration): self.name = name #武器名字 self.limit_level = limit_level #等级限制 self.attack = attack #攻击 self.duration = duration #持久 耐久 class Role: #角色 def __init__(self,name,blood,base_attack = 100): self.name = name #角色名字 self.blood = blood #血 self.base_attack = base_attack #基础攻击 class Magic(Role): #魔法师 def magic_attack(self,target,weapon): """魔法攻击,target为攻击目标,需传入Role的子类对象,weapon,需传入Weapon对象""" target.blood -= (weapon.attack + self.base_attack) #目标血量下降=攻击方的基础攻击+攻击方的武器攻击 magic1 = Magic('笑熬浆糊',3000) scarecrow1 = Role('稻草人',500,50) mofazhang = Weapon('魔法杖',10,50,100) magic1.magic_attack(scarecrow1,mofazhang) #传入被攻击对象和使用武器 print(scarecrow1.blood) magic1.magic_attack(scarecrow1,mofazhang) print(scarecrow1.blood)
扩展:
重载:类中的方法,方法名相同,方法的个数不一样,python是不支持重载的,C# JAVA语言中支持重载
重写:子类集成父类能够重写方法,子类对象调用方法的时候优先调用的是子类的方法
4、静态字段、静态方法、类方法、特性(属性)
一、静态字段存在的意义:当类中具备相同字段和值的时候,若是在对象中设置须要每一个对象都须要赋值,例如:
静态字段尽可能用类去调用,类的方法用对象去调用
#!/ufr/bin/env python # -*- coding:utf-8 -*- class Province: country = "China" #建立静态字段,静态字段存储在类中 def __init__(self,name): self.name = name #建立普通字段,普通字段存储在对象中 def print_name(self): print('%s' %(self.name)) shandong = Province('山东省') hebei = Province("河北省") shandong.print_name() print(hebei.country) #调用静态字段的第1中方法 print(Province.country) #调用静态字段的第2种方法,推荐使用这种,原则是谁的成员谁调用 Province.print_name(shandong) #调用类的方法的第1种方法 shandong.print_name() #调用类的方法的第2种方法,推荐使用这种,原则类的方法由对象调用
静态字段、静态方法、类方法实例:
#!usr/bin/env python # -*- coding:utf-8 -*- class Province: country = "China" #建立静态字段,静态字段存储在类中 def __init__(self,name): self.name = name #建立普通字段,普通字段存储在对象中 def print_name(self): print('%s' %(self.name)) @staticmethod #静态方法,静态方法至关于普通函数 def static_method(args): #注意静态方法没有self print(args) @classmethod #类方法 def class_method(cls): #类方法必须有cls参数,代指类 cls.static_method(cls.country) #能够直接调用类的静态方法,把静态字段传入 shandong = Province('山东省') hebei = Province("河北省") #调用类的普通方法的两种方式 shandong.print_name() Province.print_name(hebei) #能够用这种方式调用方法 #调用类的静态方法的两种方式 Province.static_method('类调用静态方法') shandong.static_method('shandong对象调用静态方法') #调用静态字段的两种方法 print(Province.country) print(shandong.country) #类的方法有两种调用方式 Province.class_method() shandong.class_method() #总结:虽然有两种方式调用,但潜规则是: #一、对象调用:类中的方法、普通字段 #二、由类调用:静态字段、静态方法、类方法
二、特性(属性)
#!usr/bin/env python # -*- coding:utf-8 -*- class Province: def __init__(self,name): self.name = name @property #特性(属性),只能传递self参数;调用时不用加括号,即以字段的形式调用 def county(self): if self.name == "山东省": return ["五莲县","莒县","东港区"] else: return["未知"] shandong = Province("山东省") re = shandong.county #特性(属性)的调用方法 print(re)
#!usr/bin/env python # -*- coding:utf-8 -*- class Province: def __init__(self,name): self.name = name if self.name == "山东省": self.li = ["五莲县","莒县","东港区"] else: self.li = ["未知"] @property #特性(属性),只能传递self参数;调用时不用加括号,即以字段的形式调用 def county(self): return self.li @county.setter #当设置特性(属性)时调用此函数 def county(self,value): self.li = value shandong = Province("山东省") re1 = shandong.county #特性(属性)的调用方法 print(re1) re2 = shandong.county = ["日照市","济南市"] #['日照市', '济南市'] print(re2)
成员总结,类的成员有:
对象中: 通常字段、通常方法
类中:静态字段、静态方法、类方法、特性(属性)
其实就是三大类:字段、方法、特性
那么用类仍是对象调用呢:只记住一句话,传self的用对象调用,其余的用类调用
三、面向对象之成员修饰符
成员的前面加两个下划线(__)表示私有的,只能在类的内部调用,外部不能调用,只能在成员所属类的内部能访问,其子类和对象都是不能访问的,字段和方法都是适用的
1)静态字段的私有修饰符
#!usr/bin/env python # -*- coding:utf-8 -*- class Province: __country = 'China' def __init__(self): pass def print_country(self): return Province.__country p = Province() #print(Province.__country) #此句报错由于__country是私有静态字段 print(p.print_country())
成员修饰符:全部成员加两个下划线(__)都可变为私有的,变为私有的后只能在本类中被访问,其继承类中也不能访问。
2)若是想调用私有字段或方法怎么办呢?能够在字符或方法名前面加(_类名.)的方法进行调用,但通常不建议这么作,例如:
#!usr/bin/env python # -*- coding:utf-8 -*- class Province: __country = 'China' def __init__(self,name): self.__name = name def print_country(self): return Province.__country p = Province("山东") print(p._Province__name) #山东
四、类的特殊成员
1)__init__()
2)__del__()
3)__call__(self),类中的call的特殊方法,能够用对象加括号执行。(对象())
#!usr/bin/env python # -*- coding:utf-8 -*- class Foo: def __init__(self): print('__init__') def __call__(self,*args,**kwargs): print('__call__') foo = Foo() foo() #输出:'__call__'。对象加括号,实际是执行的__call__()函数。两句合起来:Foo()()
4)getitem setitm delitem
foo["key"] 调用 getitem ; foo[1:3] 调用getitem ; python2.x中是调用的getslice
foo["key"] = value 调用setitem ; foot[1:3] = [11,22,33]调用的是setitem;python2.x中调用的是setslice
del foo["key"] 调用delitem; del foor[1:3] 调用的是delitem ; python2.x中调用的是delslice
例如:
#!usr/bin/env python # -*- coding:utf-8 -*- class Foo: def __init__(self): pass def __getitem__(self,item): #对应的是:foo["key"] print(item) def __setitem__(self, key, value): #对应的是:foo["key"] = "value" print("%s:%s" %(key,value)) def __delitem__(self, key): #对应的是:del foo["key"] print('delte this' + key) foo = Foo() foo['key'] foo["key"] = "value" del foo["key"]
#!usr/bin/env python # -*- coding:utf-8 -*- class Foo: def __init__(self): pass def __getitem__(self,item): #对应的是:foo[1:20:2] print(item.indices(100)) #最大值为100,将slice对象转为元组(1, 20, 2) def __setitem__(self, key, value): #对应的是:foo[1:3] = [11,22] emp = key.indices(100) print(emp,value) def __delitem__(self, key): #对应的是:del foo[1:3] print(key.indices(100)) foo = Foo() foo[1:20:2] #打印:(1, 20, 2) foo[1:3] = [11,22] #打印输出:(1, 3, 1) [11, 22] del foo[1:3] #打印输出:(1, 3, 1) #slice.indices()
5)类的特殊成员之__dict__ ,查看对象或类中的成员
#!usr/bin/env python # -*- coding:utf-8 -*- class Foo: def __init__(self): self.name = 'sunshuhai' def hello(self): pass foo = Foo() print(foo.__dict__) #查看对象中的成员 print(Foo.__dict__) #查看类中的成员
6)特殊成员之__iter__(self)方法
当for循环类的对象时,实际执行的就是类中的__iter__(self)方法
#!usr/bin/env python # -*- coding:utf-8 -*- class Foo: def __iter__(self): yield 1 yield 2 yield 3 foo = Foo() for i in foo: #当循环对象时,实际执行的是特殊方法__iter__(self) print(i)
7)类的特殊成员之__str__(self)
在用print打印对象,或将对象转为字符串时,会自动执行__str__(self)方法
#!usr/bin/env python # -*- coding:utf-8 -*- class Foo: def __init__(self,ef): self.ef = ef def __str__(self): return self.ef exception = Foo('出错了。。。。') print(exception) #出错了。。。。,实际调用的是__str__(self)方法
8)了解:类的特殊成员之new和metaclass
类是由对象Type建立的