开发的思想-面向过程
? 面向过程:根据业务逻辑从上到下写垒代码
? 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可
? 面向对象:对函数进行分类和封装,让开发更快更好更强...
面向过程编程最易被初学者接受,其往往用一长段代码来实现指定功能,开发过程中最常见的操作就是粘贴复制,即:将之前实现的代码块复制到现需功能处。
while True:
if cpu利用率 > 90%:
#发送邮件提醒
连接邮箱服务器
发送邮件
关闭连接
if 硬盘使用空间 > 90%:
#发送邮件提醒
连接邮箱服务器
发送邮件
关闭连接
if 内存占用 > 80%:
#发送邮件提醒
连接邮箱服务器
发送邮件
关闭连接
开发的思想-函数式编程
随着时间的推移,开始使用了函数式编程,增强代码的重用性和可读性,就变成了这样:
def 发送邮件(内容)
#发送邮件提醒
连接邮箱服务器
发送邮件
关闭连接
while True:
if cpu利用率 > 90%:
发送邮件(‘CPU报警‘)
if 硬盘使用空间 > 90%:
发送邮件(‘硬盘报警‘)
if 内存占用 > 80%:
发送邮件(‘内存报警‘)
面向对象技术简介
Python从设计之初就已经是一门面相对象的语言,正因为如此,在Python中创建一个类和对象是很容易的。本章就让我们来学习Python的面向对象编程。 如果你以前没有接触过面向对象的编程语言,那你可能需要先了解一些面向对象语言的一些基本特征,在头脑中形成一个基本的面相对象的概念,这样有助于你更容易学习Python的面向对象编程。
?类(Class):用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
?类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
?数据成员:类变量或者实例变量用于处理类及其实例对象的相关的数据。
?方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
?实例变量:定义在方法中的变量,只作用于当前实例的类。
?继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。
?实例化:创建一个类的实例,类的具体对象。
?方法:类中定义的函数。
?对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
对象
对象是面向对象方法中的核心概念,也是理解面向对象技术的关键。 对象是描述其属性的数据以及对这些数据施加的一组操作封装在一起构成的统一体。
程序中所有东西都是对象。可以将对象想象成一种新类型的变量,它保存着数据,对外提供服务,对自己的数据进行操作。
程序是一系列对象的组合。对象之间通过消息传递机制组合起来,相互调用彼此的方法,实现程序的复杂功能。
每个对象都有自己的存储空间,可以容纳其他对象。利用封装机制,可以以现有对象为基础构造出新的对象。因此,虽然对象的概念很简单,但程序中可以实现任意复杂的对象。
每个对象都有一种类型。每个对象都是某个类的一个实例,其中类是类型type的同义词。类最主要的特征是对外接口。
同一类型的所有对象都能够接收相同的消息。子类与父类具有“同一类型”。
现实世界的对象拥有两个特征:状态和行为。而面向对象程序设计语言中的对象是以现实世界中对象为模型构造的,所以也具有状态与行为,其中状态保存在一组变量中,行为则通过方法实现。 对象的方法一方面把对象的内部变量包裹、保护起来,使得只有对象自己的方法才能操作这些内部变量;另一方面,对象的方法还是对象与外部环境和其他对象交互、通信的接口,外界对象通过这些接口驱动对象执行指定的行为,提供相应的服务。
类
现实世界中所有对象都归属于某种对象类型。同一类型的对象具有共同的特征与行为。对象类型就是同种类型对象的集合与抽象。如我们开的小汽车,就是“车”这类交通工具中的一个实例。
对象类型在英语中称为Class of Objects,所以class(类)这个关键词来进行对象类型的定义。使用class定义的类在程序中称为一种抽象数据类型,它是面向对象程序设计语言的基本概念。一个类定义好后,我们就可以像使用其他数据类型一样,声明该类的变量并创建该变量所指向的对象,然后通过该变量调用对象的方法实现对对象的操作。
将类实例化就得到了现实世界实体的面向对象的映射——对象。
类描述了同一类对象都具有的数据和行为。
创建类
面向对象编程是一种编程方式,此编程方式的落地需要使用 “类” 和 “对象” 来实现,所以,面向对象编程其实就是对 “类” 和 “对象” 的使用。
? 类就是一个模板,模板里可以包含多个函数,函数里实现一些功能
? 对象则是根据模板创建的实例,通过实例对象可以执行类中的函数
?class是关键字,表示类
?创建对象,类名称后加括号即可
#coding=utf-8
class Foo:
def Bar(self):
print ‘Bar‘
def Hello(self,name):
print ‘i am %s‘ %name
#根据类Foo创建对象obj
obj=Foo()
obj.Bar()#执行Bar方法
obj.Hello(‘xiaxiaoxu‘)#执行Hello方法
c:\Python27\Scripts>python task_test.py
Bar
i am xiaxiaoxu
使用class语句来创建一个新类,class之后为类的名称,类名后有一括号,括号中为基类(可以有多个基类)然后以冒号结尾。
classClassName(BaseClass1,BaseClass2,…):
‘optional class documentation string‘#类的帮助信息
class_suite #类体
类的帮助信息可以通过ClassName.__doc__查看。
Class_suite由类成员、方法、数据属性组成。
#coding=utf-8
class Employee(object):
#所有员工基类
empCount=0
def __init__(self,name,salary):
#类的构造函数
self.name=name
self.salary=salary
Employee.empCount +=1
def displayCount(self):
#类方法
print "total employee",Employee.empCount
def displayEmployee(self):
print "name:",self.name,",salary:",self.salary
empCount变量是一个类变量(也叫静态变量),它的值将在这个类的所有实例之间共享。你可以在内部类或外部类使用Employee.empCount访问。
类中第一个方法__init__()是一种特殊方法,被称做类的构造函数或初始化方法,只要创建类的实例,就会调用这个方法。如果没显示定义这个方法,默认会给一个空的构造方法。
类方法中参数中的self,代表实例本身,相当于java中的this指针。并且类中所有的方法中都必须有self,并且写在第一个参数位置。
所有类都是继承至基类object。
访问类的实例方法或属性
类通过函数(在class语句内由def语句编写的)为实例提供行为。因为这种嵌套的def会在类中对变量名进行赋值,实际效果就是把属性添加到类对象之中,从而可以由所有实例和子类继承。 这种def语句块出现在类内部时,通常就叫方法,而且会自动接收第一个特殊参数(通常为self),这个参数提供了被处理的实例的参照值,并且一定是明确写出来的。 访问对象的属性及方法,需要使用(.)来访问。如果访问类变量,既可以直接使用类名称访问,也可以用类的实例名称访问。 注意: 在实例化类时,不需要再显示写self参数了,会默认带入,表示指向实例本身。
emp1=Employee("sam",10000)
emp2=Employee("Steve",2000)
emp1.displayEmployee()
emp2.displayEmployee()
print "Total Employee %d" %Employee.empCount
c:\Python27\Scripts>python task_test.py
name: sam ,salary: 10000
name: Steve ,salary: 2000
Total Employee 2
创建实例对象
要创建一个类的实例,可以使用类的名称,并通过__init__()方法来接受参数。
创建一个类的具体对象。每次从类产生实例时,Python都会自动调用名为__init__的方法,也就是类的构造方法,进行数据初始化。新实例会如往常那样传入__init__的self参数。该方法也会在类中被继承。
给Employee类创建实例:
#This would create first object of Employee class
emp1 = Employee("joy", 10000)
"This would create second object of Employee class"
emp2 = Employee("sr", 15000)
函数式编程
练习:在终端下输出如下信息
小明,10岁,男,上山去砍柴
小明,10岁,男,开车去东北
小明,10岁,男,最爱大保健
老李,90岁,男,上山去砍柴
老李,90岁,男,开车去东北
老李,90岁,男,最爱大保健
老张...
#encoding=utf-8
def kanchai(name, age, gender):
print u"%s,%s岁,%s,上山去砍柴" %(name, age, gender)
def qudongbei(name, age, gender):
print u"%s,%s岁,%s,开车去东北" %(name, age, gender)
def dabaojian(name, age, gender):
print u"%s,%s岁,%s,最爱大保健" %(name, age, gender)
kanchai(u‘小明‘, 10, u‘男‘)
qudongbei(u‘小明‘, 10, u‘男‘)
dabaojian(u‘小明‘, 10, u‘男‘)
kanchai(u‘老李‘, 90, u‘男‘)
qudongbei(u‘老李‘, 90, u‘男‘)
面向对象的例子1
#encoding=utf-8
class Foo:
def __init__(self, name, age ,gender):
self.name = name
self.age = age
self.gender = gender
def kanchai(self):
print u"%s,%s岁,%s,上山去砍柴" %(self.name, self.age, self.gender)
def qudongbei(self):
print u"%s,%s岁,%s,开车去东北" %(self.name, self.age, self.gender)
def dabaojian(self):
print u"%s,%s岁,%s,最爱大保健" %(self.name, self.age, self.gender)
xiaoming = Foo(u‘小明‘, 10, u‘男‘)
xiaoming.kanchai()
xiaoming.qudongbei()
xiaoming.dabaojian()
laoli = Foo(u‘老李‘, 90, u‘男‘)
laoli.kanchai()
laoli.qudongbei()
面向对象例子2
练习二:游戏人生程序
1、创建三个游戏人物,分别是:
苍井井,女,18,初始战斗力1000
东尼木木,男,20,初始战斗力1800
波多多,女,19,初始战斗力2500
2、游戏场景,分别:
草丛战斗,消耗200战斗力
自我修炼,增长100战斗力
多人游戏,消耗500战斗力
#-*-coding:utf-8-*-
###################### 定义实现功能的类 #####################
class Person:
def __init__(self, na, gen, age, fig):
self.name = na
self.gender = gen
self.age = age
self.fight =fig
def grassland(self):
"""注释:草丛战斗,消耗200战斗力"""
self.fight = self.fight - 200
def practice(self):
"""注释:自我修炼,增长100战斗力"""
self.fight = self.fight + 200
def incest(self):
"""注释:多人游戏,消耗500战斗力"""
self.fight = self.fight - 500
def detail(self):
"""注释:当前对象的详细情况"""
temp = u"姓名:%s ; 性别:%s ; 年龄:%s ; 战斗力:%s" % (self.name, self.gender, self.age, self.fight)
print temp
###################### 开始游戏 #####################
cang = Person(u‘苍井井‘, u‘女‘, 18, 1000) # 创建苍井井角色
dong = Person(u‘东尼木木‘, u‘男‘, 20, 1800) # 创建东尼木木角色
bo = Person(u‘波多多‘, u‘女‘, 19, 2500) # 创建波多多角色
cang.incest() #苍井空参加一次多人游戏
dong.practice()#东尼木木自我修炼了一次
bo.grassland() #波多多参加一次草丛战斗
#输出当前所有人的详细情况
cang.detail()
dong.detail()
bo.detail()
cang.incest() #苍井空又参加一次多人游戏
dong.incest() #东尼木木也参加了一个多人游戏
bo.practice() #波多多自我修炼了一次
#输出当前所有人的详细情况
cang.detail()
dong.detail()
bo.detail()
类和对象在内存中是如何保存?
类以及类中的方法在内存中只有一份,而根据类创建的每一个对象都在内存中需要存一份
如上图所示,根据类创建对象时,对象中除了封装 name 和 age 的值之外,还会保存一个类对象指针,该值指向当前对象的类。
当通过 obj1 执行 【方法一】 时,过程如下:
根据当前对象中的 类对象指针 找到类中的方法
将对象 obj1 当作参数传给 方法的第一个参数 self
疑问:
使用函数式编程和面向对象编程方式来执行一个“方法”时函数要比面向对象简便
?面向对象:【创建对象】--》【通过对象执行方法】
?函数编程:【执行函数】
观察上述对比答案则是肯定的,然后并非绝对,场景的不同适合其的编程方式也不同。
总结:
?函数式的应用场景 --> 各个函数之间是独立且无共用的数据
?面向对象编程的应用场景à各个函数公用一组数据
函数式编程 和 面向对象 如何选择?
须知:对于 C# 和 Java 程序员来说不存在这个问题,因为该两门语言只支持面向对象编程(不支持函数式编程)。而对于 Python 和 PHP 等语言却同时支持两种编程方式,且函数式编程能完成的操作,面向对象都可以实现;而面向对象的能完成的操作,函数式编程不行(函数式编程无法实现面向对象的封装功能)。 所以,一般在Python开发中,全部使用面向对象或 面向对象和函数式混合使用
1.多函数需使用共同的值,如:数据库的增、删、改、查操作都需要连接数据库字符串、主机名、用户名和密码
2.需要创建多个事物,每个事物属性个数相同,但是值的需求 如:张三、李四、杨五,他们都有姓名、年龄、血型,但其都是不相同。即:属性个数相同,但值不相同
封装
封装,顾名思义就是将内容封装到某个地方,以后再去调用被封装在某处的内容。
在使用面向对象的封装特性时,需要:
1.将内容封装到某处
2.从某处调用被封装的内容
?self 是一个形式参数,当执行 obj1 = Foo(‘wupeiqi‘, 18 ) 时,self 等于 obj1
?当执行 obj2 = Foo(‘alex‘, 78 ) 时,self 等于 obj2
?所以,内容其实被封装到了对象 obj1 和 obj2 中,每个对象中都有 name 和 age 属性,在内存里类似于下图来保存。
调用
调用被封装的内容时,有两种情况:
?通过对象直接调用
?通过 self 间接调用 1、通过对象直接调用被封装的内容 上图展示了对象 obj1 和 obj2 在内存中保存的方式,根据保存格式可以如此调用被封装的内容:对象.属性名 2、通过self间接调用被封装的内容 执行类中的方法时,需要通过self间接调用被封装的内容
方法1:
#encoding=utf-8
class Foo:
def __init__(self,name,age):
self.name=name
self.age=age
obj1=Foo(‘wupeiqi‘,18)
print obj1.name #直接调用obj1对象的name属性
print obj1.age #直接调用obj1对象的age属性
obj2=Foo(‘alex‘,73)
print obj2.name #直接调用obj2对象的name属性
print obj2.age#直接调用obj2对象的age属性
c:\Python27\Scripts>python task_test.py
wupeiqi
18
alex
73
方法2:
#encoding=utf-8
class Foo:
def __init__(self,name,age):
self.name=name
self.age=age
def detail(self):
print self.name
print self.age
obj1=Foo(‘wupeiqi‘,18)
obj1.detail()
#python默认会将ogj1传给self参数,即:obj1.detail(obj1)
#所以,此时方法内部的self=obj1,即:self.name是
#wupeiqi;self.age是18
obj2=Foo(‘alex‘,73)
obj2.detail()
#Python默认胡江obj2传给self参数,即:obj1.detail(obj2)
#所以,此时方法内部的self=obj2,即:self.name是
#alex;self.age是78
c:\Python27\Scripts>python task_test.py
wupeiqi
18
alex
73
类成员
字段
字段包括:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同
普通字段属于对象
静态字段属于类
#encoding=utf-8
class Province:
#静态字段
country="China"
def __init__(self,name):
#普通字段
self.name=name
#直接访问普通字段
obj=Province("Hebei Province")
print obj.name
#直接访问静态字段
print Province.country
c:\Python27\Scripts>python task_test.py
Hebei Province
China
?静态字段在内存中只保存一份
?普通字段在每个对象中都要保存一份
方法
方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。
?普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self;
?类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类复制给cls;
?静态方法:由类调用;无默认参数
方法示例
#encoding=utf-8
class Foo:
def __init__(self,name):
self.name=name
def ord_func(self):
"""定义普通方法,至少一个self参数 """
#print self.name
print "normal method"
@classmethod
def class_func(cls):
"""定义类方法,至少有一个cls参数 """
print "class method"
@staticmethod
def static_func():
"""定义静态方法,无默认参数 """
print "static method"
#调用普通方法
f=Foo("gloryroad")
f.ord_func()
#调用静态方法
Foo.static_func()
c:\Python27\Scripts>python task_test.py
normal method
static method
相同点:对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份。
不同点:方法调用者不同、调用方法时自动传入的参数不同。
经典类的属性
Python中的属性其实是普通方法的变种。
对于属性,有以下三个知识点:
?属性的基本使用
?属性的两种定义方式
1、属性的基本使用
由属性的定义和调用要注意一下几点:
?定义时,在普通方法的基础上添加 @property 装饰器;
?定义时,属性仅有一个self参数
调用时,无需括号方法:foo_obj.func() 属性:foo_obj.prop
注意:属性存在意义是:访问属性时可以制造出和访问字段完全相同的假象
属性由方法变种而来,如果Python中没有属性,方法完全可以代替其功能。
#encoding=utf-8
class Foo:
def func(self):
print "func!"
#定义属性
@property
def prop(self):
return "gloryroad"
############调用###################
foo_obj=Foo()
foo_obj.func()
print foo_obj.prop#调用属性
c:\Python27\Scripts>python task_test.py
func!
gloryroad
新式类的属性(不太理解下面的例子)
#encoding=utf-8
class Goods(object):
@property
def price(self):
print "@property"
@price.setter
def price(self,value):
print "@price.setter"
@price.deleter
def price(self):
print "@price.deleter"
#########调用#######
obj=Goods()
obj.price #自动执行@property修饰的方法,并返回方法的返回值
obj.price=123#自动执行@price.setter修饰的price方法,
#并将123赋值飞方法的参数
del obj.price#自动执行@price.deleter修饰的price方法
c:\Python27\Scripts>python task_test.py
@property
@price.setter
@price.deleter
新式类中的属性有三种访问方式,并分别对应了三个被@property、@方法名.setter、@方法名.deleter修饰的方法
动态添加、删除、修改类的属性
1. 直接添加、删除、修改类属性
代码示例:
#coding=utf-8
class Employee(object):
#所有员工基类
empCount=0
def __init__(self,name,salary):
#类的构造函数
self.name=name
self.salary=salary
Employee.empCount+=1
def displayCount(self):
#类方法
print "total employee",Employee.empCount
def displayEmployee(self):
print "name:",self.name,",salary:",self.salary
#创建Employee类的实例对象
emp1=Employee("SR",10000)
emp1.displayCount()
emp1.displayEmployee()
emp1.salary=2000#修改属性salary
print emp1.salary
emp1.age=25#添加属性age
print emp1.age
del emp1.age#删除age属性
c:\Python27\Scripts>python task_test.py
total employee 1
name: SR ,salary: 10000
2000
25
使用函数添加、删除、修改、访问类属性
可以使用以下函数来操作对象的属性:
1、getattr(obj, name[, default]) : 访问对象的属性,如果存在返回对象属性的值,否则抛出AttributeError异常。
2、hasattr(obj,name) : 检查是否存在某个属性,存在返回True,否则返回False。
3、setattr(obj,name,value) : 设置一个属性。如果属性不存在,会创建一个新属性,该函数无返回值。若存在则更新这个值
4、delattr(obj, name) : 删除属性,如果属性不存在则抛出AttributeError异常,该函数也无返回值。
>>> class P():
... a=100
... def __init__(self):
... self.x=1
... self.y=2
...
>>> p=P()
>>> getattr(p,"a")
100
>>> getattr(p,"x")
1
>>> getattr(p,"y")
2
>>> getattr(p,"z")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: P instance has no attribute ‘z‘
>>> getattr(p,"z","not exist")
‘not exist‘
>>> hasattr(p,‘x‘)
True
>>> hasattr(p,‘y‘)
True
>>> hasattr(p,‘z‘)
False
>>> setattr(p,‘z‘,3)
>>> getattr(p,z)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name ‘z‘ is not defined
>>> getattr(p,‘z‘)
3
>>> hasattr(p,‘z‘)
True
Python有如下内置的类属性:__dict__,__doc__,__name__,__module__,__bases__
__dict__ : 类的属性(包含一个字典,由类的数据属性组成)
__doc__ :类的文档字符串,也就是类的帮助信息。
__name__: 类名
__module__: 类定义所在的模块(类的全名是’__main__.className’,如果类位于一个导入模块mymod中,那么className.__module__ 等于 mymod)
__bases__ : 类的所有父类(包含了所有父类组成的元组)
#coding=utf-8
class Employee(object):
"base class of all classes"
empCount=0
def __init__(self,name,salary):
#类的构造函数
self.name=name
self.salary=salary
Employee.empCount +=1
def displayCount(self):
#类方法
print "total employee",Employee.empCount
def displayEmployee(self):
print "name:",self.name,",salary:",self.salary
print "Employee.__doc__:",Employee.__doc__
print "Employee.__name__:",Employee.__name__
print "Employee.__module__:",Employee.__module__
print "Employee.__bases__:",Employee.__bases__
print "Employee.__dict__",Employee.__dict__
c:\Python27\Scripts>python task_test.py
Employee.__doc__: base class of all classes
Employee.__name__: Employee
Employee.__module__: __main__
Employee.__bases__: (<type ‘object‘>,)
Employee.__dict__ {‘displayEmployee‘: <function displayEmployee at 0x028279B0>, ‘__module__‘: ‘__main__‘, ‘displayCount‘: <function displayCount at 0x028279F0>, ‘__dict__‘: <attribute ‘__dict__‘ of ‘Employee‘ objects>, ‘empCount‘: 0, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Employee‘ objects>, ‘__doc__‘: ‘base class of all classes‘, ‘__init__‘: <function __init__ at 0x02827A30>}
类变量
Python中类变量也叫类的静态成员变量或类属性,类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体外。类变量通常不作为实例变量使用,它的值在这个类的所有实例之间共享。
类变量紧接在类名后面定义,相当于java和c++的static变量。
实例变量在__init__()函数里定义,相当于java和c++的普通变量。
#coding=utf-8
class Parent(object):#define parent class
parentAttr=100#类变量
def __init__(self):
self.name="zhangsan"
print "Calling parent constructor"
#parentAttr变量就是一个类变量,name就是一个实例变量
#coding=utf-8
class f(object) :
count = 12#类变量
t = f()
print u"t.count内存地址 =", id(t.count), "t.count =", t.count
print u"f.count内存地址 =", id(f.count), "f.count =", f.count
t.count = 10
print u"通过实例对象修改count的值后"
print u"t.count内存地址 =", id(t.count), "t.count =", t.count
print u"f.count内存地址 =", id(f.count), "f.count =", f.count
从上例前两句类变量访问来看,类变量既可以通过类变名直接调用,也可以通过实例对象名调用。但是当我们执行如下语句后:t.count = 10
再次打印t.count的值发现已经改变成10,而f.count的值仍然是12,并且他们的内存地址也不一致了,why?
类名.属性名 = value这种方式添加的,那这个属性就是类的类属性,其所有的实例对象都可以访问。但是如果是通过类的某个实例对象去添加的,就像 实例名.属性名 = value,这种添加方式,那这个属性只属于这个实例对象所有,并且如果所添加的属性名与类中类属性名相同的时,类属性在这个实例对象中就会被屏蔽掉,也就是说实例引用时,同名的实例属性会屏蔽同名的类属性,就类似于全局变量与局部变量的关系。
所以当我们执行t.count = 10语句后,就等于给实例对象t添加了一个同名的类属性count,其值等于10,但父类的该类属性的值并未被改变,所以此时再打印t.count,就是引用的是t实例中的count属性(除非删除该实例变量)。这就是为什么上例执行t.count = 10以后,t.count的值变成了10的原因。
>>> class P:
... x=100
...
>>> a=P()
>>> a.x
100
>>> a.x=1000
>>> a.x
1000
>>> P.x
100
>>> del a.x
>>> a.x
100
>>> P.x
100
>>> a.m=22
>>> a.m
22
>>> P.m
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: class P has no attribute ‘m‘
>>>
上例抛出AttributeError异常是因为我们是用类名f直接去访问类f的实例对象t中的属性address时抛出的。
由此可以说明:
实例对象可以直接访问类变量,而类名却不能直接访问实例对象的属性。
实例变量
实例变量是在__init__函数里定义的,相当于java和c++的普通变量,只作用于当前类的实例。实例变量前面需要加上self关键字,表示执行实例本身。
#coding=utf-8
class Person(object):
id=12 #类静态成员在这儿定义,注意缩进
def __init__(self,name):
self.name = name#类的实例变量
p=Person(‘linda‘)
print p.name #访问对象的实例变量
c:\Python27\Scripts>python task_test.py
linda
类变量和实例变量区别
加self的是实例变量
#coding=utf-8
class P:
a=0
def __init__(self,b):
self.b=b#self.b 叫做实例变量
P.a+=1
print P.a
p=P(5)
print P.a
a=P(1)
print P.a
print p.a
p.a=200
print P.a#当实例变量和类变量实名同名的时候,在实例中使用的实例变量
print p.a
c:\Python27\Scripts>python task_test.py
0
1
2
2
2
200
类变量用类名调用,实例变量用self调用
>>> class P:
... a=0
... def __init__(self,b):
... self.b=b
... P.a+=1
局部变量
在类的实例方法中定义的变量,叫局部变量,其作用域仅限于其所在的函数内部,执行到该函数,局部变量生成,函数退出后,局部变量被消亡。
#coding=utf-8
class Person(object):
id=12 #类静态成员在这儿定义,注意缩进
def __init__(self,name):
self.name = name#类的实例变量
self.inName=‘ads‘#类的实例变量
def getName(self):
self.name=‘Lucy‘#修改实例变量的值
name=‘xiaxiaoxu‘#局部变量
return self.name, name
p=Person(‘linda‘)
print p.name #访问对象的实例变量
print p.inName#访问对象的实例变量
#通过函数访问实例变量和局部变量
res=p.getName()
print res
c:\Python27\Scripts>python task_test.py
linda
ads
(‘Lucy‘, ‘xiaxiaoxu‘)
类方法
类方法是类对象所拥有的方法,在类的内部使用关键字def定义的方法,但与一般方法不同的是,需要用修饰器”@classmethod”来标识其为类方法,并且在定义类方法时需要把类本身的实例(cls,指向该类本身)作为参数传进入。
类方法既可以直接使用类名去调用,还可以使用类的实例去调。
#coding=utf-8
class Person(object):
id=12 #类静态成员在这儿定义,注意缩进
def __init__(self,name):
self.name = name#类的实例变量
self.inName=‘ads‘#类的实例变量
@classmethod#类方法定义,用注解方式说明
def up(cls,a):#这儿是cls,不是self
print cls,cls.__name__
return a+1
#创建类的实例
p=Person("xiaxiaoxu")
#调用类方法
print Person.up(20)#类名直接调用
print p.up(12)#通过实例对象调用
c:\Python27\Scripts>python task_test.py
<class ‘__main__.Person‘> Person
21
<class ‘__main__.Person‘> Person
13
cls是建议的,可以不一样,指向类在内存中的地址,存类变量和类方法,指向类在内存中的地址,存类变量和类方法
#coding=utf-8
class Person(object):
id=12 #类静态成员在这儿定义,注意缩进
def __init__(self,name):
self.name = name#类的实例变量
self.inName=‘ads‘#类的实例变量
@classmethod#类方法定义,用注解方式说明
def up(xxx,a):#这儿是cls,不是self
print xxx,xxx.__name__
return a+1
#创建类的实例
p=Person("xiaxiaoxu")
#调用类方法
print Person.up(20)#类名直接调用
print p.up(12)#通过实例对象调
c:\Python27\Scripts>python task_test.py
<class ‘__main__.Person‘> Person
21
<class ‘__main__.Person‘> Person
13
类方法
#coding=utf-8
class P:
a=0
def __init__(self,b):
self.b=b#self.b 叫做实例变量
P.a+=1
x=1
@staticmethod
def print_var(x):
print P.a#类方法中可以使用实例变量么?不能!
return x
print P.print_var(123)
a=P(4)
print a.print_var(123)
c:\Python27\Scripts>python task_test.py
0
123
1
123
self指向实例在内存中的地址,加self才能找到实例在内存中的地址,才能调用实例变量
类方法里不能使用实例变量,因为类方法不能实例化
实例化:p=P(5)
调用类方法
类名.方法()
实例可以调用类方法,值是类方法里不能使用实例变量而已
修改类属性
修改类属性的值,有三种方法,分别为:
1、通过类方法修改
2、通过实例方法修改
3、直接通过类对象修改
#coding=utf-8
class employee(object):
city=‘BJ‘#类属性
def __init__(self,name):
self.name = name#实例变量
#定义类方法
@classmethod
def getCity(cls):
return cls.city
#定义类方法
@classmethod
def setCity(cls,city):
cls.city=city
#实例方法
def set_City(self,city):
employee.city=city
emp=employee(‘joy‘)#创建类的实例
print emp.getCity()#通过实例对象引用类方法
print employee.getCity()#通过类对象引用类方法
emp.setCity(‘TJ‘)#实例对象调用类方法改变类属性的值
print emp.getCity()
print employee.getCity()
employee.setCity(‘CD‘)#类对象调用类方法改变类属性的值
print emp.getCity()
print employee.getCity()
emp.set_City(‘SH‘)#调用实例方法改变类属性的值
print emp.getCity()
print employee.getCity()
employee.city=20#直接修改类属性的值
print emp.getCity()
print employee.getCity()
c:\Python27\Scripts>python task_test.py
BJ
BJ
TJ
TJ
CD
CD
SH
SH
20
20
静态方法
静态方法是在类的内部使用关键字def定义的方法,但与其他方法不同的是,需要用修饰器”@staticmethod”来标识其为静态方法,并且在定义静态方法时不需要传入任何隐式参数。
静态方法既可以直接使用类名去调用,还可以使用类的实例去调。
#coding=utf-8
class Person(object):
id=12#类静态成员在这儿定义,注意缩进
def __init__(self,name):
self.name=name
self.__inName=‘ads‘
@staticmethod#类静态方法定义,用注解方式说明
def down(b):#静态方法不需要self,cls变量
id=13
print "id=",id
return b-1
#创建类的实例
p=Person(‘xiaxiaoxu‘)
#调用静态方法
print Person.down(15)#类名直接调用
print p.down(19)#通过实例对象调用
c:\Python27\Scripts>python task_test.py
id= 13
14
id= 13
18
例子
#coding=utf-8
class P:
a=0 #类变量
def __init__(self,b):
self.b = b #self.b 叫做实例变量
P.a+=1
x=1
@staticmethod
def print_var(x):
print P.a #类方法中可以使用实例变量么?不能!
return x
print P.print_var(123)
a=P(2)
print a.print_var(124)
c:\Python27\Scripts>python task_test.py
0
123
1
124
实例方法
实例方法则是类中定义的普通方法,定义时需要传入隐式参数self,且为第一个参数,调用时则不需要,并且只能通过实例调用。
#coding=utf-8
class Person(object):
id=12#类静态成员在这儿定义,注意缩进
def __init__(self,name):
self.name=name
self.__inName=‘ads‘
def getName(self):#实例方法,需要传入self参数
return self.name
#创建类的实例
p=Person(‘xiaxiaoxu‘)
#通过实例对象调用,无法通过类对象调用
print p.getName()
c:\Python27\Scripts>python task_test.py
xiaxiaoxu
小结几种方法的区别,类方法、静态方法、实例方法
类方法可以被类对象或实例调用,实例方法只能被实例调用,它只属于类的单个实例拥有,静态方法两者都能调用,它在类和实例间共享。Python实现了一定的灵活性使得类方法和静态方法,都能够被实例和类二者调用。
所以三者主要区别在于参数传递上的区别,实例方法隐含传的参数是类实例self,而类方法隐含传递的参数是类本身cls,而静态方法无隐含参数,主要为了类实例也可以直接调用静态方法。
练习:
写一个密码生成器的类:
要求有个类变量,统计一下一共生成过多少个密码。
要求有4个方法,1:构造方法 2 实例方法 3 类方法 4 静态方法
生成指定长度的随机数字密码
生成指定长度的随机字母密码
生成指定长度的随机数字和字母的混合
#coding=utf-8
import random
import string
class Gen_passwd():
gen_nbr=0
def __init__(self,length):
self.length=length
pass
def Gen_digital_passwd(self):
Gen_passwd.gen_nbr+=1
self.passwd=‘‘
for i in range(self.length):
self.passwd+=random.choice(string.digits)
return self.passwd
@classmethod
def Gen_letter_passwd(cls,length):
Gen_passwd.gen_nbr+=1
passwd=‘‘
for i in range(length):
passwd+=random.choice(string.letters)
return passwd
@staticmethod
def Gen_mix_passwd(length):
Gen_passwd.gen_nbr+=1
passwd_digit=‘‘
passwd_letter=‘‘
digit_nbr = random.randint(1,length-1)
print "(","digit_nbr:",digit_nbr,"letter_nbr:",length-digit_nbr,")"
for i in range(digit_nbr):
passwd_digit+=random.choice(string.digits)
for i in range(length-digit_nbr):
passwd_letter+=random.choice(string.letters)
return passwd_digit+passwd_letter
@staticmethod
def Get_gen_nbr():
pass
return Gen_passwd.gen_nbr
if __name__ ==‘__main__‘:
GenPasswd=Gen_passwd(10)
print GenPasswd.Gen_digital_passwd()
print GenPasswd.Gen_letter_passwd(14)
print GenPasswd.Gen_mix_passwd(13)
print GenPasswd.Get_gen_nbr()
c:\Python27\Scripts>python task_test.py
6295463513
xqkAmgAqjqAonW
( digit_nbr: 2 letter_nbr: 11 )
88zIgBoWarNUo
3
修改版本:
#coding=utf-8
import random
import string
class Gen_passwd():
gen_nbr=0
def __init__(self):#沒有初始化length
print "Gen passwd is running now!"
def Gen_digital_passwd(self,length):#在實例方法中傳入長度,不是實例傳進來的
Gen_passwd.gen_nbr+=1
self.passwd=‘‘
for i in range(length):
self.passwd+=random.choice(string.digits)
return self.passwd
@classmethod
def Gen_letter_passwd(cls,length):
Gen_passwd.gen_nbr+=1
passwd=‘‘
for i in range(length):
passwd+=random.choice(string.letters)
return passwd
@staticmethod
def Gen_mix_passwd(length):
Gen_passwd.gen_nbr+=1
passwd_digit=‘‘
passwd_letter=‘‘
digit_nbr = random.randint(1,length-1)
print "(","digit_nbr:",digit_nbr,"letter_nbr:",length-digit_nbr,")"
for i in range(digit_nbr):
passwd_digit+=random.choice(string.digits)
for i in range(length-digit_nbr):
passwd_letter+=random.choice(string.letters)
return passwd_digit+passwd_letter
@staticmethod
def Get_gen_nbr():
pass
return Gen_passwd.gen_nbr
if __name__ ==‘__main__‘:
GenPasswd=Gen_passwd()
print GenPasswd.Gen_digital_passwd(10)
print Gen_passwd.Gen_letter_passwd(14)#使用類名調用類函數
print Gen_passwd.Gen_mix_passwd(13) #使用類名調用類函數
print Gen_passwd.Get_gen_nbr()#使用類名調用類函數
Python类中隐藏数据
Python中经常会看到类中定义的方法名前后有下划线的情况,那这些都表示什么意思呢?
类中的方法名前后都有两个下划线,表示是Python的内置方法,如__init__()
类中的方法名前面有两个下划线,表示是Python中的私有方法,类似于java中的private,不能在类外部调用(如__setId())。
类中的属性前有两个下划线表示声明该属性为私有属性,不能在类的外部被使用或直接访问,如__name。
类的私有属性
__private_attrs:两个下划线开头属性,声明该属性为私有属性,不能在类的外部被使用或直接访问,只能在类内部访问。在类内部的方法中使用时的语法:self.__private_attrs
#coding=utf-8
class Person(object):
__secretCount=0#私有属性
def __init__(self,name):
self.name=name#实例属性
self.__inName=‘ads‘#私有属性
def visit_private_attribute(self):
self.__secretCount +=1
print "__secretCount:",self.__secretCount
print "__inName:",self.__inName
p=Person(‘prel‘)
p.visit_private_attribute()
print "\n"
print "access private attribute out of class:"
print "\n"
print p.__inName
c:\Python27\Scripts>python task_test.py
__secretCount: 1
__inName: ads
access private attribute out of class:
Traceback (most recent call last):
File "task_test.py", line 18, in <module>
print p.__inName
AttributeError: ‘Person‘ object has no attribute ‘__inName‘
虽然Python不允许实例化的类从外部访问私有数据,但我们可以使用 object._className__attrName 访问私有属性
#coding=utf-8
class Person(object):
__secretCount=0#私有属性
def __init__(self,name):
self.name=name#实例属性
self.__inName=‘ads‘#私有属性
def visit_private_attribute(self):
self.__secretCount +=1
print "__secretCount:",self.__secretCount
print "__inName:",self.__inName
p=Person(‘prel‘)
p.visit_private_attribute()
print "access private attribute from out of class"
print p._Person__secretCount
print p._Person__inName
c:\Python27\Scripts>python task_test.py
__secretCount: 1
__inName: ads
access private attribute from out of class
1
ads
私有方法
类中方法名前面有两个下划线,表示是Python中的私有方法,类似于java中的private,不能在类外部调用(如__setId()),在类内部调用语法: self.__private_methods。
#coding=utf-8
class Person(object):
id=12 #类静态成员在这儿定义,注意缩进
def __init__(self,name):
self.name=name
self.__inName=‘ads‘
def __setId(self,id):#私有方法
Person.id=id*2
def getId(self):
self.__setId(18)#类内部调用私有方法
return Person.id
p=Person(‘prel‘)
print p.getId()
print "access private attribute from out of class"
print p.__setId(10)
c:\Python27\Scripts>python task_test.py
36
access private attribute from out of class
Traceback (most recent call last):
File "task_test.py", line 21, in <module>
print p.__setId(10)
AttributeError: ‘Person‘ object has no attribute ‘__setId‘
单继承
面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。继承完全可以理解成类之间的类型和子类型关系。
即一个派生类(derived class)继承基类(bass class)字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。
例如,有这样一个设计,一个Cat类型的对象派生自Animal类,这是模拟”是一个(is-a)”关系(例如,Cat是一个Animal)。
继承基本语法:
class 派生类名(基类名1 [, 基类名2....]):
‘Optional class documentation string‘
class_suite
基类名写在括号里,基本类是在类定义的时候,在元组之中指明的
继承的特点:
在继承中基类的构造(__init__()方法)不会被自动调用,它需要在其派生类的构造方法中专门调用,即子类不会继承基类的构造方法。
?在调用基类的方法时,需要加上基类的类名前缀,且需要带上self参数变量。区别于在类中调用普通函数时并不需要带上self参数。
?Python总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。(先在本类中查找需要调用的方法,找不到才去基类中找)。
?子类只继承父类所有公有的属性和方法,并且也可以在子类中通过父类名来调用,而对于私有的属性和方法,子类是不进行继承的,因此在子类中是无法通过父类名来访问的。
继承的本质:
对于面向对象的继承来说,其实就是将多个类共有的方法提取到父类中,子类仅需继承父类而不必一一实现每个方法。
注:除了子类和父类的称谓,你可能看到过 派生类 和 基类 ,他们与子类和父类只是叫法不同而已。
#coding=utf-8
class vehicle:
def __init__(self,name):
self.name=name
def get_vehicle_name(self):
return self.name
def set_volume(self,number):
self.volume=number
def get_volume(self):
return self.volume
class car(vehicle):
def set_car_colour(self,colour):
self.colour=colour
def get_car_colour(self):
return self.colour
if __name__ == "__main__":
v=vehicle("land_engine")
v.set_volume(5)
print v.get_volume()
c=car("car")#没有构造方法,会自动调用父类的构造方法
c.set_car_colour("red")
print c.get_car_colour()
print c.get_vehicle_name()
c:\Python27\Scripts>python task_test.py
5
red
car
单继承
#coding=utf-8
class Parent(object): # define parent class
parentAttr = 100
def __init__(self):
"父类构造方法,用于初始化工作"
print "Calling parent constructor"
def parentMethod(self):
print ‘Calling parent method‘
def setAttr(self, attr):
Parent.parentAttr = attr
def getAttr(self):
print "Parent attribute :", Parent.parentAttr
class Child1(Parent): # define child1 class
def __init__(self):
"子类构造方法,用于初始化子类本身数据的工作"
print "Calling child1 constructor"
def childMethod(self):
print ‘Calling child1 method‘
Parent.parentMethod(self) #调用基类的方法,所以要加上参数self
class Child2(Parent): # define child2 class
#没有实现__init__方法,则会调用基类的__init__方法
def childMethod(self):
print ‘Calling child2 method‘
self.parentMethod() #子类调用自己从父类那继承过来的父类的方法
c1 = Child1() # 实例化子类 1
c2 = Child2() # 实例化子类 2
c1.childMethod() # 调用子类1的方法
c2.childMethod() # 调用子类2的方法
c1.parentMethod() # 子类实例对象调用父类方法
c1.setAttr(200) # 再次调用父类的
c:\Python27\Scripts>python task_test.py
Calling child1 constructor
Calling parent constructor
Calling child1 method
Calling parent method
Calling child2 method
Calling parent method
Calling parent method
c1 = Child1() # 实例化子类 1 c2 = Child2() # 实例化子类 2 这两句是创建Child1类和Child2类的实例对象,创建实例对象时,就会自动去调类的构造方法__init__进行初始化工作,如果子类中没有实现构造方法__init__,就会直接调用父类的构造方法,所以打印结果为:
但如果基类构造方法需要传入参数时,创建子类的实例对象时,就必须传入对应的参数,否则子类不实现构造方法的话,调用基类的构造方法,会报参数数量不匹配的错。
子类调用基类方法
子类调用父类的方法有两种方式:
1、Parent.parentMethod(self)
2、self.parentMethod()
第一种是直接用父类的类名.方法名去调用父类的方法,但是需要注意的是,这种调用方法必须将self作为参数传进去并且作为第一个参数,表示指向这个类的实例本身。这种方法多用于方法重写时。
第二种是直接用self去调用父类的方法,为什么可以这样调用呢?因为一旦子类继承了父类,那么子类就拥有父类所有的公有方法和属性,所以此时父类的方法和属性就相当于子类自己了,所以可以直接用self去直接调用实例的方法,而不用再传入self参数了。
子类实例调用父类方法:在子类方法中调用父类方法,传self
调用父类的__init__方法
我们知道,子类继承基类后,在子类定义了自己的构造方法后,基类的构造方法不会被主动继承,所以基类构造函数中初始化的一些属性在子类中是不具有的,但是我们又想用这些属性,怎么办?
这个时候有两种方法,一是在子类构造方法中再写一次这些属性,二是直接调用父类的构造方法完成。当然我们一般采用第二种发法。
子类调用基类的构造函数有两种方法,基本语法如下:
1、super(subclassName, self).__init__( [parameter1[,parameter2....]])
2、superclassName.__init__(self, [parameter1[,parameter2....]])
注意:
两次参数列表中的self参数都是必不可少的,如果基类的构造方法中有除了self参数外别的参数,调用时,也必须传入同等数量、同等类型的参数。
当子类不定义自己的构造方法时,默认会自动调用父类的构造方法。
Python中super函数只能在构造方法中使用。
#coding=utf-8
class A(object):
name=‘‘
def __init__(self):
self.name=‘fosterwu‘
def getName(self):
return ‘A ‘ + self.name
class C(A):
def __init__(self):
super(C,self).__init__()#调用基类构造方法
if __name__==‘__main__‘:
c=C()
print c.getName()
c:\Python27\Scripts>python task_test.py
A fosterwu
#coding=utf-8
class UniversityMember(object):
def __init__(self,name,age):
self.name=name
self.age=age
def getName(self):
return self.name
def getAge(self):
return self.age
class Student(UniversityMember):
def __init__(self,name,age,sno,Department):
#注意要显示调用父类构造方法,并传递参数self
UniversityMember.__init__(self,name,age)
self.sno = sno
self.Department = Department
def getSno(self):
return self.sno
def getDepartment(self):
return self.Department
s=Student("xiaxiaoxu",‘18‘,‘CS‘,18)
print s.name,s.age
s.name=‘superman‘
print s.name
print s.getName()
print s.getAge()
c:\Python27\Scripts>python task_test.py
xiaxiaoxu 18
superman
superman
18
类间的关系判断
使用issubclass()或者isinstance()方法来检测类之间的关系。
#coding=utf-8
class UniversityMember(object):
pass
class Student(UniversityMember):
pass
if issubclass(Student,UniversityMember):
print "class Student is subclass of UniversityMember"
else:
print "class Student is not subclass of UniversityMember"
c:\Python27\Scripts>python task_test.py
class Student is subclass of UniversityMember
Isinstance()判断一个对象是否是一个类或子类的实例对象
#coding=utf-8
class UniversityMember(object):
pass
class Student(UniversityMember):
pass
vm = UniversityMember() #创建父类的实例
s = Student() #创建子类的实例
if isinstance(vm, UniversityMember) :
print u"vm是UniversityMember类或其子类的实例对象"
else :
print u"vm不是UniversityMember类或其子类的实例对象"
if isinstance(s, UniversityMember) :
print u"s是UniversityMember类或其子类的实例对象"
else :
print u"s不是UniversityMember类或其子类的实例对象"
c:\Python27\Scripts>python task_test.py
vm是UniversityMember类或其子类的实例对象
s是UniversityMember类或其子类的实例对象
多重继承
Python支持多重继承,也就是一个子类可以有多个父类,定义子类时,括号中写多个父类,并且父类间用逗号隔开。 在多重继承中,子类有那么多的父类,那子类实例化时,构造方法是怎样调用的呢? 这里需要记住几点:
?多重继承中,子类在没有定义自己的构造方法时,以第一个父类为中心。
?如果子类重新定义了自己的构造方法,就不会调用父类的构造方法。但如果仍想调用父类的构造方法,这个时候调用哪个父类的构造方法,由你自己确定,并且多重继承时,调用具体哪个父类的构造方法时只能使用如下方法: superclassName.__init__(self, [parameter1[,parameter2....]] )
?如果父类中有同名的方法时,通过子类的实例对象去调用的该方法也是第一个父类中的方法。同样你可以用我们上边说的?子类调用基类方法?中的的方法去调用具体哪个父类中的方法。Parent.parentMethod(self)
如果继承的多个类每个类中都定了相同的函数,那么那一个会被使用呢?
1、Python的类可以继承多个类,Java和C#中则只能继承一个类
2、Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先和广度优先
?当类是经典类时,多继承情况下,会按照深度优先方式查找
?当类是新式类时,多继承情况下,会按照广度优先方式查找 经典类和新式类,从字面上可以看出一个老一个新,新的必然包含了跟多的功能,也是之后推荐的写法,从写法上区分的话,如果当前类或者父类继承了object类,那么该类便是新式类,否则便是经典类
例子:写一个房子的类,一个车的类,一个房车的类并继承与类:房子和车
#coding=utf-8
class House(object):
def __init__(self,area):
print "a house is build!"
self.area=area
def set_room_num(self,room_num):
self.room_num=room_num
def get_room_num(self):
return self.room_num
class Car(object):
def __init__(self,name):
print "a car is running!"
def set_name(self,name):
self.name=name
def get_name(self):
return self.name
class House_Car(House,Car):
def __init__(self,area,name):
#参数从这里传,把需要调用的构造方法需要的参数都传进来
House.__init__(self,area)
Car.__init__(self,name)
hc=House_Car("50 sm","gloryroad car")
hc.set_room_num(5)
print hc.get_room_num()
hc.set_name("gr car")
print hc.get_name()
c:\Python27\Scripts>python task_test.py
a house is build!
a car is running!
5
gr car
封装
将对象的数据与操作数据的方法相结合,通过方法将对象的数据与实现细节保护起来,就称为封装。外界只能通过对象的方法访问对象,因此封装同时也实现了对象的数据隐藏。通过封装和数据隐藏机制,将一个对象相关的变量和方法封装为一个独立的软件体,单独进行实现和维护,并使对象能够在系统内方便地进行传递,另外也保证了对象数据的一致性并使程序易于维护,封装可以让调用者不用关心对象是如何构建的而直接进行使用。
在Python中,所有的特性都是公开可用的,但是程序员应该在直接访问对象数据与操作数据时谨慎行事,因为他们可能无意中使得这些特性在某些方面不一致。
如下面的例子:
Property用法
通过property方法可以把类对象变量(self.Name)的获取、修改和删除方法自动影射到类变量(bob.name)的三种行为
#coding=utf-8
import os
class Person(object):
def __init__(self,name):
self.Name=name
def getName(self):
print "getting..."
return self.Name
def setName(self,name):
print "setting..."
self.Name=name
def delName(self):
print "remove..."
del self.Name
name = property(getName,setName,delName,‘name property docs‘)
#name=property(getName,setName)
bob=Person(‘Bob Smith‘)
print bob.name
bob.name=‘Robert Smith‘
print bob.name
del bob.name
c:\Python27\Scripts>python task_test.py
getting...
Bob Smith
setting...
getting...
Robert Smith
remove...
Property防止非法值得出现
#coding=utf-8
class Student():
def __init__(self,name,age,score):
self.name=name
self.age=age
self.score=score
s=Student(‘tom‘,19,90)
s.name=‘lily‘
s.score=9999
print s.name, s.age,s.score
很显然,将score改成9999,很不符合逻辑。为了限制score的范围,可以通过一个set_score()方法来设置成绩,再通过一个get_score()来获取成绩,这样,在set_score()方法里,就可以检查参数
#coding=utf-8
class Student():
def __init__(self,name,age,score):
self.name=name
self.age=age
self.__score=score
def set_score(self,nbr):
if not isinstance(nbr,int):
raise ValueError("score must be a interger!")
if nbr >=100 or nbr<=0:
raise ValueError("score must be between 0 - 100!")
self.__score = nbr
def get_score(self):
return self.__score
s=Student(‘tom‘,19,90)
print "score is ", s.get_score()
try:
s.set_score(999)
except Exception,e:
print e
c:\Python27\Scripts>python task_test.py
score is 90
score must be between 0 - 100!
改成property,property只能在新式类用
#coding=utf-8
import os
class Student(object):
def __init__(self, name, age, score):
self.name = name
self.age = age
self.__score = score
def set_score(self, score):
if isinstance(score, int):
raise ValueError("score must be an interger!")
if score < 0 or score > 100 :
raise ValueError("score must be between 0 - 100!")
self.__score = score
def get_score(self):
return self.__score
score = property(get_score,set_score)
s = Student(‘tom‘, 19, 90)
print s.score
try:
s.score = 1000
except Exception,e:
print "score error!"
else:
print s.score
c:\Python27\Scripts>python task_test.py
90
score error!
Property这儿不太懂
多态
同一个方法传不同的实例,执行不同的方法,有不同的结果。
定义一个方法,参数用得基类类型,animal类得cry方法
叫多态,传基类类型的参数
一个方法传多个类型的实例参数,会执行不同的方法
多态:所谓多态是指同一种行为在不同的对象上
有不同的表现形式。
#不同的实例传入相同的方法,展现不同的执行效果:python的多态。
同一个方法传不同参数,有不同的结果
python多态里不需要指定基类
#coding=utf-8
class F1:
pass
class S1(F1):
def show(self):
print "S1.show"
class S2(F1):
def show(self):
print "S2.show"
def Func(obj):
"""""Func函数需要接受一个F1类型或F2类型的"""
obj.show()
s1_obj=S1()
Func(s1_obj)
#在Func函数中传入S1类的对象#s1_obj,执行S1的show方法,结果时S1.show
s2_obj=S2()
Func(s2_obj)
#在Func函数中传入S2类的对象#s2_obj,执行S2的show方法,结果时S1.show
c:\Python27\Scripts>python task_test.py
S1.show
S2.show
多态意味着可以对不同的对象使用同样的操作,但它们可能会以多种形态呈现出结果。在Python中,任何不知道对象到底是什么类型,但又需要对象做点什么的时候,都会用到多态。
#coding=utf-8
class calculator:
def count(self,args):
return 1
calc=calculator()
#自定义类型
from random import choice
#obj是随机返回的类型不确定
obj=choice([‘hello,world‘,[1,2,3],calc])
print obj
print type(obj)
print obj.count(‘a‘) #方法多态
#obj取到calc时,等价于执行calc.count(“a”)
c:\Python27\Scripts>python task_test.py
<__main__.calculator instance at 0x02761440>
<type ‘instance‘>
1
c:\Python27\Scripts>python task_test.py
[1, 2, 3]
<type ‘list‘>
0
c:\Python27\Scripts>python task_test.py
[1, 2, 3]
<type ‘list‘>
0
c:\Python27\Scripts>python task_test.py
hello, world
<type ‘str‘>
0
多态—”鸭子类型“
#coding=utf-8
_metaclass_=type # 确定使用新式类
class Duck(object):
def quack(self):
print "Quaaaaaack!"
def feathers(self):
print "The duck has white and gray feathers."
class Person(object):
def quack(self):
print "The person imitates a duck."
def feathers(self):
print "The person takes a feather from the ground and shows it."
def in_the_forest(duck):
duck.quack()
duck.feathers()
def game():
donald = Duck()
john = Person()
in_the_forest(donald)
in_the_forest(john)
game()
c:\Python27\Scripts>python task_test.py
Quaaaaaack!
The duck has white and gray feathers.
The person imitates a duck.
The person takes a feather from the ground and shows it.
多态—运算符多态
#coding=utf-8
def add(x,y):
return x+y
print add(1,2) #输出3
print add("hello,","world") #输出hello,world
print add(1,"abc") #将抛出异常 TypeError,不同类型不能相加‘
c:\Python27\Scripts>python task_test.py
3
hello,world
Traceback (most recent call last):
File "task_test.py", line 9, in <module>
print add(1,"abc")
File "task_test.py", line 4, in add
return x+y
TypeError: unsupported operand type(s) for +: ‘int‘ and ‘str‘
重载
方法的重载(overloading)是允许在一个类的定义中,多个方法使用相同的方法名。 方法的重载是面向对象编程语言多态性的一种形式,方法重载大大提高了代码重用率和程 序员开发效率。 重载方法的名称是相同的,但在方法的声明中一定要有彼此不相同的成份,以使编译器能够区分这些方法。重载的方法必须遵循下列原则:
?方法的参数表必须不同,包括参数的类型或个数,以此区分不同方法体;
?方法的返回类型、修饰符可以相同,也可以不同。 如果你学过C++、JAVA或者是其他面向对象编程语言,你会发现Python中的重载与其他面向对象编程语言在使用上是有区别,其实可以这么说Python在形式上是不支持重载。
方法重写
在类层次结构中,当子类的成员变量与父类的成员变量同名时,子类的成员变量会隐藏父类的成员变量;当子类的方法与父类具有相同的名字、参数列表、返回值类型时,子类的方法重写(overriding)了父类的方法,在父类定义的方法就被隐藏。“隐藏”的含义是,通过子类对象调用子类中与父类同名的变量和方法时,操作的是这些变量和方法是在子类中的定义。子类通过成员变量的隐藏和方法的重写可以把父类的状态和行为改成为自身的状态和行为。
重写是指子类重写父类的成员方法。子类可以改变父类方法所实现的功能,但子类中重写的方法必须与父类中对应的方法具有相同的方法名。也就是说要实现重写,就必须存在继承。
简单来讲,就是如果父类的方法不能满足子类的需求,子类就可以重写从父类那继承过来的方法。
重写父类的成员方法
#coding=utf-8
class Parent(object):#定义父类
def myMethod(self):
print "call Parent"
def printName(self):
print "my name is Lily"
class Child(Parent):#定义子类
def myMethod(self):#子类重写父类myMethod方法
print "call child"
c=Child()#子类实例
c.myMethod()#子类调用重写方法
c.printName()#子类调用从父类继承过来但未被重写的方法
c:\Python27\Scripts>python task_test.py
call child
my name is Lily
运算符重写
在Python中,运算符重写的方式很简单,每个类都会有默认内置一些运算符方法,只要重写这个方法,就可以实现针对该运算符的重写。
#coding=utf-8
class Vector(object):
def __init__(self,a,b):
self.a=a
self.b=b
def __str__(self):
return ‘Vector (%d,%d)‘% (self.a,self.b)
def __add__(self,other):
return Vector(self.a+other.a,self.b+other.b)
x=Vector(3,7)
y=Vector(1,-10)
print x+y
print str(x)
c:\Python27\Scripts>python task_test.py
Vector (4,-3)
Vector (3,7)
可以被重写的内置方法
#coding=utf-8
class P:
def __del__(self):
print "bye"
p=P()
del p
c:\Python27\Scripts>python task_test.py
bye
内存泄漏:一般是静态变量和队列,插入了后没有删,慢慢的就爆了
重写str,repr
在命令行界面,不用print打印,用repr方法
用print打印,用str方法
自定义一个类,有__str__()方法和__repr__()方法
直接调用调用repr方法,print调用调用str方法
>>> class P:
... def __repr__(self):
... return "hi repr method"
... def __str__(self):
... return "hi str method"
...
>>> p=P()
>>> p
hi repr method
>>> print p
hi str method
>>>
重写cmp
python中cmp的方法返回的1,0,-1会默认转换为True,False,python中的cmp的方法默认就是返回数字
#coding=utf-8
class P:
def __init__(self,value):
self.value=value
def __cmp__(self,other):
if self.value>other.value:
return 1
elif self.value== other.value:
return 0
else:
return -1
p1=P(10)
p2=P(10)
print p1==p2
print p1>p2
print p1<p2
c:\Python27\Scripts>python task_test.py
True
False
False
类的特殊成员__doc__ 表示类的描述信息
#coding=utf-8
class Foo:
u"""it is a amazing film"""
def func(self):
pass
print Foo.__doc__
c:\Python27\Scripts>python task_test.py
it is a amazing film
文档字符串也会在help()里显示
#coding=utf-8
class Foo:
u"""it is a amazing film"""
def func(self):
pass
print Foo.__doc__
help(Foo)
c:\Python27\Scripts>python task_test.py
it is a amazing film
Help on class Foo in module __main__:
class Foo
| it is a amazing film
|
| Methods defined here:
|
| func(self)
c:\Python27\Scripts>
重写call
__call__
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
>>> class P:
... def __call__(self):
... return 100
...
>>> p=P()
>>> p
<__main__.P instance at 0x0000000001E6DBC
>>> p()
100
实例后边加():调用实例,就是调用call方法,把实例当成函数来调用
类的特殊成员__dict__
类或对象中的所有成员
上文中我们知道:类的普通字段属于对象;类中的静态字段和方法等属于类,即:
#coding=utf-8
class Province:
country=‘China‘
def __init__(self,name,count):
self.name = name
self.count=count
def func(self,*args,**kwargs):
print ‘func‘
#获取类的成员,即:静态字段、方法、
print Province.__dict__
obj1=Province(‘HeBei‘,1000)
print obj1.__dict__
#获取对象obj1的成员
#输出:{‘count‘:1000,‘name‘:‘HeBei‘}
obj2=Province(‘HeNan‘,38888)
print obj2.__dict__
#获取对象obj2的成员
#输出:{‘count‘:3888,‘name‘:‘Henan‘}
c:\Python27\Scripts>python task_test.py
{‘country‘: ‘China‘, ‘__module__‘: ‘__main__‘, ‘func‘: <function func at 0x027579B0>, ‘__init__‘: <function __init__ at 0x027579F0>, ‘__doc__‘: None}
{‘count‘: 1000, ‘name‘: ‘HeBei‘}
{‘count‘: 38888, ‘name‘: ‘HeNan‘}
*可变参数,代表元组,**代表字典
类的特殊成员__getitem__、__setitem__、__delitem__用于索引操作,如字典。以上分别表示获取、设置、删除数据
#coding=utf-8
class Foo(object):
def __getitem__(self,key):
print ‘__getitem__‘,key
def __setitem__(self,key,value):
print ‘__setitem__‘,key,value
def __delitem__(self,key):
print ‘__delitem__‘,key
obj=Foo()
result=obj[‘k1‘] #自动触发执行__getitem__
obj[‘k2‘]=‘sdfsdf‘ # 自动触发执行 __setitem__
del obj[‘k1‘] #自动触发执行 __delitem__
c:\Python27\Scripts>python task_test.py
__getitem__ k1
__setitem__ k2 sdfsdf
__delitem__ k1
把实例当作字典来操作
#coding=utf-8
class Foo(object):
dict_value={}
def __getitem__(self,key):
print "__getitem__",key
if Foo.dict_value.has_key(key):
return Foo.dict_value[key]
else:
raise ValueError
def __setitem__(self,key,value):
print "__setitem__",key,value
Foo.dict_value[key]=value
def __delitem__(self,key):
print "__delitem__",key
del Foo.dict_value[key]
obj=Foo()
obj[‘k5‘]=‘xufengchai‘
result=obj[‘k5‘]
obj[‘k2‘]=‘xiaxiaoxu‘ #自动触发执行__setitem__
result = obj[‘k2‘] # 自动触发执行 __getitem__
del obj[‘k2‘] #自动触发执行 __delitem__
c:\Python27\Scripts>python task_test.py
__setitem__ k5 xufengchai
__getitem__ k5
__setitem__ k2 xiaxiaoxu
__getitem__ k2
__delitem__ k2
把实例搞得像序列__getslice__、__setslice__、__delslice__
该三个方法用于分片操作,如:列表
#coding=utf-8
class Foo(object):
def __getslice__(self,i,j):
print ‘__getslice__‘,i,j
def __setslice__(self,i,j,sequence):
print ‘__setslice__‘,i,j
def __delslice__(self,i,j):
print ‘__delslice__‘,i,j
obj=Foo()
obj[1:2] #自动触发执行__setitem__
obj[0:4]=[11,22,33,44] # 自动触发执行 __getitem__
del obj[0:2] #自动触发执行 __delitem__
c:\Python27\Scripts>python task_test.py
__getslice__ 1 2
__setslice__ 0 4
__delslice__ 0 2
把实例当做序列操作
#coding=utf-8
class Foo(object):
def __init__(self):
self.list_value=[]
def __getslice__(self,i,j):
print ‘__getslice__‘,i,j
try:
return self.list_value[i:j]
except:
raise ValueError
def __setslice__(self,i,j,sequence):
print ‘__setslice__‘,i,j
try:
self.list_value[i:j]=sequence
except:
raise ValueError
def __delslice__(self,i,j):
print ‘__delslice__‘,i,j
try:
del self.list_value[i:j]
except:
raise DelValueError
obj=Foo()
obj[0:5]= [22,33,44,55,66] #自动触发执行__setitem__
print obj[0:5]
obj[0:4]=[11,22,33,44] # 自动触发执行 __getitem__
print obj[0:5]
del obj[0:2] #自动触发执行 __delitem__
c:\Python27\Scripts>python task_test.py
__setslice__ 0 5
__getslice__ 0 5
[22, 33, 44, 55, 66]
__setslice__ 0 4
__getslice__ 0 5
[11, 22, 33, 44, 66]
__delslice__ 0 2
类的特殊成员 __iter__用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了 __iter__