1.1面向对象
面向对象(object-oriented ;简称: OO)至今还没有统一的概念 我这里把它定义为:按人们 认识客观世界的系统思维方式,采用基于对象(实体)的概念建立模型,模拟客观世界分析、设 计、实现软件的办法。
面向对象编程(Object Oriented Programming-OOP)是一种解决软件复用的设计和编程方法。 这种方法把软件系统中相近相似的操作逻辑和操作 应用数据、状态,以类的型式描述出来,以对象实例的形式在软件系统中复用,以达到提高软件开发效率的作用。
面向对象的理解:
面向对象是一种设计思想
1.符合人们的思考习惯
2.把执行者变成指挥者
3.简化功能,把复杂的事情简单化
面向对象有三大特征:
1.封装
2.继承
3.多态
1.2类和对象
面向对象编程的2个非常重要的概念:类和对象
对象是面向对象编程的核心,在使用对象的过程中,为了将具有共同特征和行为的一组对象抽象定义,提出了另外一个新的概念——类
类就相当于制造飞机时的图纸,用它来进行创建的飞机就相当于对象
类是对事务的描述,是抽象的。
对象是类的具体体现。
1.2.1 类
人以类聚物以群分。
具有相似内部状态和运动规律的实体的集合(或统称为抽象)。
具有相同属性和行为事物的统称
类是抽象的,在使用的时候通常会找到这个类的一个具体的存在,使用这个具体的存在。一个类可以找到多个对象
1.2.2 对象
某一个具体事物的存在,在现实世界中可以是看得见摸得着的。
可以是直接使用的
1.2.3 类和对象的关系
类>=对象
1.2.4 练习区分类和对象
奔驰汽车类
奔驰smart类
张三的那辆奔驰smart对象
狗类
大黄狗类
李四家那只大黄狗对象
水果类
苹果类
红苹果类红富士苹果类
我嘴里吃了一半的苹果对象
1.2.5 类的构成
类(Class)由3个部分构成
·类的名称:类名
·类的属性:一组数据成员变量
·类的方法:允许对进行操作的方法(行为)成员方法
举例:
1)人类设计,只关心3样东西:
·事物名称(类名):人(Person)
·属性:身高(height)、年龄(age)
·方法(行为/功能):跑(run)、打架(fight)
2)狗类的设计
·类名:狗(Dog)
·属性:品种 、毛色、性别、名字、 腿儿的数量
·方法(行为/功能):叫 、跑、咬人、吃、摇尾巴
1.2.6 类的抽象
如何把日常生活中的事物抽象成程序中的类?
拥有相同(或者类似)属性和行为的对象都可以抽像出一个类
方法:一般名词都是类(名词提炼法)
<1>坦克发射3颗炮弹轰掉了2架飞机
·坦克--》可以抽象成 类
·炮弹--》可以抽象成类
·飞机-》可以抽象成类
<2>小明在公车上牵着一条叼着热狗的狗
·小明--》 人类
·公车--》 交通工具类
·热狗--》 食物类
·狗--》 狗类
1.3 定义类
定义一个类,格式如下:
class类名:
方法列表
demo:定义一个Car类
#定义类
classCar:
#方法
defgetCarInfo(self):
print('车轮子个数:%d,颜色%s'%(self.wheelNum, self.color))
defmove(self):
print("车正在移动...")
说明:
·定义类时有2种:新式类和经典类,上面的Car为经典类,如果是Car(object)则为新式类
·类名的命名规则按照"大驼峰"
1.4 创建对象
通过上一节课程,定义了一个Car类;就好比有车一个张图纸,那么接下来就应该把图纸交给生成工人们去生成了
python中,可以根据已经定义的类去创建出一个个对象
创建对象的格式为:
对象名=类名()
创建对象demo:
#定义类
classCar:
#移动
defmove(self):
print('车在奔跑...')
#鸣笛
deftoot(self):
print("车在鸣笛...嘟嘟..")
#创建一个对象,并用变量BMW来保存它的引用
BMW = Car()
BMW.color ='黑色'
BMW.wheelNum =4#轮子数量
BMW.move()
BMW.toot()
print(BMW.color)
print(BMW.wheelNum)
总结:
·BMW = Car(),这样就产生了一个Car的实例对象,此时也可以通过实例对象BMW来访问属性或者方法
·第一次使用BMW.color = '黑色'表示给BMW这个对象添加属性,如果后面再次出现BMW.color = xxx表示对属性进行修改
·BMW是一个对象,它拥有属性(数据)和方法(函数)
·当创建一个对象时,就是用一个模子,来制造一个实物
1.5 __init__()方法
在上一小节的demo中,我们已经给BMW这个对象添加了2个属性,wheelNum(车的轮胎数量)以及color(车的颜色),试想如果再次创建一个对象的话,肯定也需要进行添加属性,显然这样做很费事,那么有没有办法能够在创建对象的时候,就顺便把车这个对象的属性给设置呢?
答:__init__()方法
在java里叫构造方法
1.5.1 使用方法
def类名:
#初始化函数,用来完成一些默认的设定
def__init__():
pass
1.5.2 __init__()方法的调用
#定义汽车类
classCar:
def__init__(self):
self.wheelNum =4
self.color ='蓝色'
defmove(self):
print('车在跑,目标:夏威夷')
#创建对象
BMW = Car()
print('车的颜色为:%s'%BMW.color)
print('车轮胎数量为:%d'%BMW.wheelNum)
1.5.3 总结
当创建Car对象后,在没有调用__init__()方法的前提下,BMW就默认拥有了2个属性wheelNum和color,原因是__init__()方法是在创建对象后,就立刻被默认调用了
想一想
既然在创建完对象后__init__()方法已经被默认的执行了,那么能否让对象在调用__init__()方法的时候传递一些参数呢?如果可以,那怎样传递呢?
#定义汽车类
classCar:
def__init__(self, newWheelNum, newColor):
self.wheelNum = newWheelNum
self.color = newColor
defmove(self):
print('车在跑,目标:夏威夷')
#创建对象
BMW = Car(4,'green')
print('车的颜色为:%s'%BMW.color)
print('车轮子数量为:%d'%BMW.wheelNum)
1.5.4 总结2
·__init__()方法,在创建一个对象时默认被调用,不需要手动调用
·__init__(self)中,默认有1个参数名字为self,如果在创建对象时传递了2个实参,那么__init__(self)中出了self作为第一个形参外还需要2个形参,例如__init__(self,x,y)
·__init__(self)中的self参数,不需要开发者传递,python解释器会自动把当前的对象引用传递进去
1.6 应用:创建多个对象
·根据上两节创建一个Car类
·创建出多个汽车对象,比如BMW、AUDI等
1.7 ‘魔法方法’
1.7.1 定义__str__()的方法
classCar:
def__init__(self, newWheelNum, newColor):
self.wheelNum = newWheelNum
self.color = newColor
def__str__(self):
msg ="嘿。。。我的颜色是"+ self.color +"我有"+ int(self.wheelNum) +"个轮胎..."
returnmsg
defmove(self):
print('车在跑,目标:夏威夷')
BMW = Car(4,"白色")
print(BMW)
1.7.2 总结
·在python中方法名如果是__xxxx__()的,那么就有特殊的功能,因此叫做“魔法”方法
·当使用print输出对象的时候,只要自己定义了__str__(self)方法,那么就会打印从在这个方法中return的数据
1.8.1 理解self
看如下示例:
#定义一个类
classAnimal:
#方法
def__init__(self, name):
self.name = name
defprintName(self):
print('名字为:%s'%self.name)
#定义一个函数
defmyPrint(animal):
animal.printName()
dog1 = Animal('西西')
myPrint(dog1)
dog2 = Animal('北北')
myPrint(dog2)
1.8.2 总结
·所谓的self,可以理解为自己
·可以把self当做C++中类里面的this指针一样理解,就是对象自身的意思
·某个对象调用其方法时,python解释器会把这个对象作为第一个参数传递给self,所以开发者只需要传递后面的参数即可
1.9 应用:烤地瓜
1.9.1 分析烤地瓜的属性和方法
示例属性如下:
·cookedLevel :这是数字;0~3表示还是生的,超过3表示半生不熟,超过5表示已经烤好了,超过8表示已经烤成木炭了!我们的地瓜开始时时生的
·cookedString :这是字符串;描述地瓜的生熟程度
·condiments :这是地瓜的配料列表,比如番茄酱、芥末酱等
示例方法如下:
·cook():把地瓜烤一段时间
·addCondiments():给地瓜添加配料
·__init__():设置默认的属性
·__str__():让print的结果看起来更好一些
1.9.2定义类,并且定义__init__()方法
#定义`地瓜`类
classSweetPotato:
'这是烤地瓜的类'
#定义初始化方法
def__init__(self):
self.cookedLevel =0
self.cookedString ="生的"
self.condiments = []
1.9.3 添加‘烤地瓜’的方法
#烤地瓜方法
defcook(self, time):
self.cookedLevel += time
ifself.cookedLevel >8:
self.cookedString ="烤成灰了"
elifself.cookedLevel >5:
self.cookedString ="烤好了"
elifself.cookedLevel >3:
self.cookedString ="半生不熟"
else:
self.cookedString ="生的"
1.9.4 测试
把上面2块代码合并为一个程序后,在代码的下面添加以下代码进行测试
mySweetPotato = SweetPotato()
print(mySweetPotato.cookedLevel)
print(mySweetPotato.cookedString)
print(mySweetPotato.condiments)
完整的代码为:
classSweetPotato:
'这是烤地瓜的类'
#定义初始化方法
def__init__(self):
self.cookedLevel =0
self.cookedString ="生的"
self.condiments = []
#烤地瓜方法
defcook(self, time):
self.cookedLevel += time
ifself.cookedLevel >8:
self.cookedString ="烤成灰了"
elifself.cookedLevel >5:
self.cookedString ="烤好了"
elifself.cookedLevel >3:
self.cookedString ="半生不熟"
else:
self.cookedString ="生的"
#用来进行测试
mySweetPotato = SweetPotato()
print(mySweetPotato.cookedLevel)
print(mySweetPotato.cookedString)
print(mySweetPotato.condiments)
1.9.5 测试cook的方法是否好用
在上面的代码最后面添加如下代码:
print("------接下来要进行烤地瓜了-----")
mySweetPotato.cook(4)#烤4分钟
print(mySweetPotato.cookedLevel)
print(mySweetPotato.cookedString)
1.9.6 定义addCondiments()方法和__str__()方法
def__str__(self):
msg = self.cookedString +"地瓜"
iflen(self.condiments) >0:
msg = msg +"("
fortempinself.condiments:
msg = msg + temp +", "
msg = msg.strip(", ")
msg = msg +")"
returnmsg
defaddCondiments(self, condiments):
self.condiments.append(condiments)
1.9.7 再次测试
完整的代码如下:
classSweetPotato:
"这是烤地瓜的类"
#定义初始化方法
def__init__(self):
self.cookedLevel =0
self.cookedString ="生的"
self.condiments = []
#定制print时的显示内容
def__str__(self):
msg = self.cookedString +"地瓜"
iflen(self.condiments) >0:
msg = msg +"("
fortempinself.condiments:
msg = msg + temp +", "
msg = msg.strip(", ")
msg = msg +")"
returnmsg
#烤地瓜方法
defcook(self, time):
self.cookedLevel += time
ifself.cookedLevel >8:
self.cookedString ="烤成灰了"
elifself.cookedLevel >5:
self.cookedString ="烤好了"
elifself.cookedLevel >3:
self.cookedString ="半生不熟"
else:
self.cookedString ="生的"
#添加配料
defaddCondiments(self, condiments):
self.condiments.append(condiments)
#用来进行测试
mySweetPotato = SweetPotato()
print("------有了一个地瓜,还没有烤-----")
print(mySweetPotato.cookedLevel)
print(mySweetPotato.cookedString)
print(mySweetPotato.condiments)
print("------接下来要进行烤地瓜了-----")
print("------地瓜经烤了4分钟-----")
mySweetPotato.cook(4)#烤4分钟
print(mySweetPotato)
print("------地瓜又经烤了3分钟-----")
mySweetPotato.cook(3)#又烤了3分钟
print(mySweetPotato)
print("------接下来要添加配料-番茄酱------")
mySweetPotato.addCondiments("番茄酱")
print(mySweetPotato)
print("------地瓜又经烤了5分钟-----")
mySweetPotato.cook(5)#又烤了5分钟
print(mySweetPotato)
print("------接下来要添加配料-芥末酱------")
mySweetPotato.addCondiments("芥末酱")
print(mySweetPotato)
1.10 应用:存放家具
#定义一个home类
classHome:
def__init__(self, area):
self.area = area#房间剩余的可用面积
#self.light = 'on' #灯默认是亮的
self.containsItem = []
def__str__(self):
msg ="当前房间可用面积为:"+ str(self.area)
iflen(self.containsItem) >0:
msg = msg +"容纳的物品有: "
fortempinself.containsItem:
msg = msg + temp.getName() +", "
msg = msg.strip(", ")
returnmsg
#容纳物品
defaccommodateItem(self,item):
#如果可用面积大于物品的占用面积
needArea = item.getUsedArea()
ifself.area > needArea:
self.containsItem.append(item)
self.area -= needArea
print("ok:已经存放到房间中")
else:
print("err:房间可用面积为:%d,但是当前要存放的物品需要的面积为%d"%(self.area, needArea))
#定义bed类
classBed:
def__init__(self,area,name ='床'):
self.name = name
self.area = area
def__str__(self):
msg ='床的面积为:'+ str(self.area)
returnmsg
#获取床的占用面积
defgetUsedArea(self):
returnself.area
defgetName(self):
returnself.name
#创建一个新家对象
newHome = Home(100)#100平米
print(newHome)
#创建一个床对象
newBed = Bed(20)
print(newBed)
#把床安放到家里
newHome.accommodateItem(newBed)
print(newHome)
#创建一个床对象
newBed2 = Bed(30,'席梦思')
print(newBed2)
#把床安放到家里
newHome.accommodateItem(newBed2)
print(newHome)
总结:
·如果一个对象与另外一个对象有一定的关系,那么一个对象可用是另外一个对象的属性
思维升华:
·添加“开、关”灯,让房间、床一起亮、灭
1.11 保护对象的属性
如果有一个对象,当需要对其进行修改属性时,有2种方法
·对象名.属性名=数据---->直接修改
·对象名.方法名() ---->间接修改
为了更好的保存属性安全,即不能随意修改,一般的处理方式为
·将属性定义为私有属性
·添加一个可以调用的方法,供调用
classPeople(object):
def__init__(self, name):
self.__name = name
defgetName(self):
returnself.__name
defsetName(self, newName):
iflen(newName) >=5:
self.__name = newName
else:
print("error:名字长度需要大于或者等于5")
xiaoming = People("xx")
print(xiaoming.__name)
classPeople(object):
def__init__(self, name):
self.__name = name
defgetName(self):
returnself.__name
defsetName(self, newName):
iflen(newName) >=5:
self.__name = newName
else:
print("error:名字长度需要大于或者等于5")
xiaoming = People("xx")
xiaoming.setName("yy")
print(xiaoming.getName())
xiaoming.setName("lisi")
print(xiaoming.getName())
总结
·Python中没有像C++中public和private这些关键字来区别公有属性和私有属性
·它是以属性命名方式来区分,如果在属性名前面加了2个下划线'__',则表明该属性是私有属性,否则为公有属性(方法也是一样,方法名前面加了2个下划线的话表示该方法是私有的,否则为公有的)。
1.12 __del__()方法
创建对象后,python解释器默认调用__init__()方法;
当删除一个对象时,python解释器也会默认调用一个方法,这个方法为__del__()方法
importtime
classAnimal(object):
#初始化方法
#创建完对象后会自动被调用
def__init__(self, name):
print('__init__方法被调用')
self.__name = name
#析构方法
#当对象被删除时,会自动被调用
def__del__(self):
print("__del__方法被调用")
print("%s对象马上被干掉了..."%self.__name)
#创建对象
dog = Animal("哈皮狗")
#删除对象
deldog
cat = Animal("波斯猫")
cat2 = cat
cat3 = cat
print("---马上 删除cat对象")
delcat
print("---马上 删除cat2对象")
delcat2
print("---马上 删除cat3对象")
delcat3
print("程序2秒钟后结束")
time.sleep(2)
总结
·当有1个变量保存了对象的引用时,此对象的引用计数就会加1
·当使用del删除变量指向的对象时,如果对象的引用计数不会1,比如3,那么此时只会让这个引用计数减1,即变为2,当再次调用del时,变为1,如果再调用1次del,此时会真的把对象进行删除,__del__会被调用
1.13继承
将共性的内容放在父类中,子类只需要关注自己特有的内容。在程序中,继承描述的是事物之间的所属关系,例如猫和狗都属于动物,程序中便可以描述为猫和狗继承自动物;同理,波斯猫和巴厘猫都继承自猫,而沙皮狗和斑点狗都继承自狗。
1.131 继承的实例
lassCat(object):
def__init__(self, name, color="白色"):
self.name = name
self.color = color
defrun(self):
print("%s--在跑"%self.name)
#定义一个子类,继承Cat类如下:
classBosi(Cat):
defsetNewName(self, newName):
self.name = newName
defeat(self):
print("%s--在吃"%self.name)
bs = Bosi("印度猫")
print('bs的名字为:%s'%bs.name)
print('bs的颜色为:%s'%bs.color)
bs.eat()
bs.setNewName('波斯')
bs.run()
说明:
·虽然子类没有定义__init__方法,但是父类有,所以在子类继承父类的时候这个方法就被继承了,所以只要创建Bosi的对象,就默认执行了那个继承过来的__init__方法
总结
·子类在继承的时候,在定义类时,小括号()中为父类的名字
·父类的属性、方法,会被继承给子类
1.132注意点
classAnimal(object):
def__init__(self, name='动物', color='白色'):
self.__name = name
self.color = color
def__test(self):
print(self.__name)
print(self.color)
deftest(self):
print(self.__name)
print(self.color)
classDog(Animal):
defdogTest1(self):
#print(self.__name) #不能访问到父类的私有属性
print(self.color)
defdogTest2(self):
#self.__test() #不能访问父类中的私有方法
self.test()
A = Animal()
#print(A.__name) #程序出现异常,不能访问私有属性
print(A.color)
#A.__test() #程序出现异常,不能访问私有方法
A.test()
print("------分割线-----")
D = Dog(name ="小花狗", color ="黄色")
D.dogTest1()
D.dogTest2()
总结
·私有的属性,不能通过对象直接访问,但是可以通过方法访问
·私有的方法,不能通过对象直接访问
·私有的属性、方法,不会被子类继承,也不能被访问
·一般情况下,私有的属性、方法都是不对外公布的,往往用来做内部的事情,起到安全的作用
1.14多继承
Python中多继承的格式如下:
#定义一个父类
classA:
defprintA(self):
print('----A----')
#定义一个父类
classB:
defprintB(self):
print('----B----')
#定义一个子类,继承自A、B
classC(A,B):
defprintC(self):
print('----C----')
obj_C = C()
obj_C.printA()
obj_C.printB()
运行结果:
----A----
----B----
说明
·python中是可以多继承的,在java中叫接口
·父类中的方法、属性,子类会继承
注意点
·想一想:
如果在上面的多继承例子中,如果父类A和父类B中,有一个同名的方法,那么通过子类去调用的时候,调用哪个?
#coding=utf-8
classbase(object):
deftest(self):
print('----base test----')
classA(base):
deftest(self):
print('----A test----')
#定义一个父类
classB(base):
deftest(self):
print('----B test----')
#定义一个子类,继承自A、B
classC(A,B):
pass
obj_C = C()
obj_C.test()
print(C.__mro__)#可以查看C类的对象搜索方法时的先后顺序
1.15重写父类与调用父类方法
1.151重写父类的方法
所谓重写,就是子类中,有一个和父类相同名字的方法,在子类中的方法会覆盖掉父类中同名的方法
#coding=utf-8
classCat(object):
defsayHello(self):
print("halou-----1")
classBosi(Cat):
defsayHello(self):
print("halou-----2")
bosi = Bosi()
bosi.sayHello()
1.15.2调用父类的方法
#coding=utf-8
classCat(object):
def__init__(self,name):
self.name = name
self.color ='yellow'
classBosi(Cat):
def__init__(self,name):
#调用父类的__init__方法1(python2)
#Cat.__init__(self,name)
#调用父类的__init__方法2
#super(Bosi,self).__init__(name)
#调用父类的__init__方法3
super().__init__(name)
defgetName(self):
returnself.name
bosi = Bosi('xiaohua')
print(bosi.name)
print(bosi.color)
1.6多态
多态的概念是应用于Java和C#这一类强类型语言中,而Python崇尚“鸭子类型”。
首先Python不支持多态,也不用支持多态,python是一种多态语言,崇尚鸭子类型。以下是维基百科中对鸭子类型得论述:
在程序设计中,鸭子类型(英语:duck typing)是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定。这个概念的名字来源于由James Whitcomb Riley提出的鸭子测试,“鸭子测试”可以这样表述:
“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”
在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。例如,在不使用鸭子类型的语言中,我们可以编写一个函数,它接受一个类型为鸭的对象,并调用它的走和叫方法。在使用鸭子类型的语言中,这样的一个函数可以接受一个任意类型的对象,并调用它的走和叫方法。如果这些需要被调用的方法不存在,那么将引发一个运行时错误。任何拥有这样的正确的走和叫方法的对象都可被函数接受的这种行为引出了以上表述,这种决定类型的方式因此得名。
鸭子类型通常得益于不测试方法和函数中参数的类型,而是依赖文档、清晰的代码和测试来确保正确使用。从静态类型语言转向动态类型语言的用户通常试图添加一些静态的(在运行之前的)类型检查,从而影响了鸭子类型的益处和可伸缩性,并约束了语言的动态特性。
所谓多态:定义时的类型和运行时的类型不一样,此时就成为多态
·Python伪代码实现Java或C#的多态
classF1(object):
defshow(self):
print'F1.show'
classS1(F1):
defshow(self):
print'S1.show'
classS2(F1):
defshow(self):
print'S2.show'
#由于在Java或C#中定义函数参数时,必须指定参数的类型
#为了让Func函数既可以执行S1对象的show方法,又可以执行S2对象的show方法,所以,定义了一个S1和S2类的父类
#而实际传入的参数是:S1对象和S2对象
defFunc(F1 obj):
"""Func函数需要接收一个F1类型或者F1子类的类型"""
printobj.show()
s1_obj = S1()
Func(s1_obj)#在Func函数中传入S1类的对象s1_obj,执行S1的show方法,结果:S1.show
s2_obj = S2()
Func(s2_obj)#在Func函数中传入Ss类的对象ss_obj,执行Ss的show方法,结果:S2.show
·Python “鸭子类型”
classF1(object):
defshow(self):
print'F1.show'
classS1(F1):
defshow(self):
print'S1.show'
classS2(F1):
defshow(self):
print'S2.show'
defFunc(obj):
printobj.show()
s1_obj = S1()
Func(s1_obj)
s2_obj = S2()
Func(s2_obj)
1.17类属性、实例属性
在了解了类基本的东西之后,下面看一下python中这几个概念的区别
先来谈一下类属性和实例属性
在前面的例子中我们接触到的就是实例属性(对象属性),顾名思义,类属性就是类对象所拥有的属性,它被所有类对象的实例对象所共有,在内存中只存在一个副本,这个和C++中类的静态成员变量有点类似。对于公有的类属性,在类外可以通过类对象和实例对象访问
1.17.1类属性
classPeople(object):
name ='Tom'#公有的类属性
__age =12#私有的类属性
p = People()
print(p.name)#正确
print(People.name)#正确
print(p.__age)#错误,不能在类外通过实例对象访问私有的类属性
print(People.__age)#错误,不能在类外通过类对象访问私有的类属性
1.17.2实例属性
classPeople(object):
address ='山东'#类属性
def__init__(self):
self.name ='xiaowang'#实例属性
self.age =20#实例属性
p = People()
p.age =12#实例属性
print(p.address)#正确
print(p.name)#正确
print(p.age)#正确
print(People.address)#正确
print(People.name)#错误
print(People.age)#错误
1.17.3通过实例对象修改类属性
classPeople(object):
country ='china'#类属性
print(People.country)
p = People()
print(p.country)
p.country ='japan'
print(p.country)#实例属性会屏蔽掉同名的类属性
print(People.country)
delp.country#删除实例属性
print(p.country)
总结
如果需要在类外修改类属性,必须通过类对象去引用然后进行修改。如果通过实例对象去引用,会产生一个同名的实例属性,这种方式修改的是实例属性,不会影响到类属性,并且之后如果通过实例对象去引用该名称的属性,实例属性会强制屏蔽掉类属性,即引用的是实例属性,除非删除了该实例属性。
1.18类方法和静态方法
1.18.1类方法
是类对象所拥有的方法,需要用修饰器@classmethod来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数(当然可以用其他名称的变量作为其第一个参数,但是大部分人都习惯以'cls'作为第一个参数的名字,就最好用'cls'了),能够通过实例对象和类对象去访问。
classPeople(object):
country ='china'
#类方法,用classmethod来进行修饰
@classmethod
defgetCountry(cls):
returncls.country
p = People()
printp.getCountry()#可以用过实例对象引用
printPeople.getCountry()#可以通过类对象引用
类方法还有一个用途就是可以对类属性进行修改:
classPeople(object):
country ='china'
#类方法,用classmethod来进行修饰
@classmethod
defgetCountry(cls):
returncls.country
@classmethod
defsetCountry(cls,country):
cls.country = country
p = People()
printp.getCountry()#可以用过实例对象引用
printPeople.getCountry()#可以通过类对象引用
p.setCountry('japan')
printp.getCountry()
printPeople.getCountry()
1.18.2静态方法
需要通过修饰器@staticmethod来进行修饰,静态方法不需要多定义参数
classPeople(object):
country ='china'
@staticmethod
#静态方法
defgetCountry():
returnPeople.country
printPeople.getCountry()
总结
从类方法和实例方法以及静态方法的定义形式就可以看出来,类方法的第一个参数是类对象cls,那么通过cls引用的必定是类对象的属性和方法;而实例方法的第一个参数是实例对象self,那么通过self引用的可能是类属性、也有可能是实例属性(这个需要具体分析),不过在存在相同名称的类属性和实例属性的情况下,实例属性优先级更高。静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须通过类对象来引用
1.19工厂设计模型
设计一个卖车的4S店,该怎样做呢?
#定义车类
classCar(object):
#定义车的方法
defmove(self):
print("---车在移动---")
defstop(self):
print("---停车---")
#定义一个销售车的店类
classCarStore(object):
deforder(self):
self.car = Car()#找一辆车
self.car.move()
self.car.stop()
说明
上面的4s店,只能销售那一种类型的车
如果这个是个销售北京现代品牌的车,比如伊兰特、索纳塔等,该怎样做呢?
设计一个卖北京现代车的4S店
#定义伊兰特车类
classYilanteCar(object):
#定义车的方法
defmove(self):
print("---车在移动---")
defstop(self):
print("---停车---")
#定义索纳塔车类
classSuonataCar(object):
#定义车的方法
defmove(self):
print("---车在移动---")
defstop(self):
print("---停车---")
#定义一个销售北京现代车的店类
classCarStore(object):
deforder(self, typeName):
#根据客户的不同要求,生成不同的类型的车
iftypeName =="伊兰特":
car = YilanteCar()
eliftypeName =="索纳塔":
car = SuonataCar()
returncar
这样做,不太好,因为当北京现代又生产一种新类型的车时,又得在CarStore类中修改,有没有好的解决办法呢?
使用函数实现
#定义伊兰特车类
classYilanteCar(object):
#定义车的方法
defmove(self):
print("---车在移动---")
defstop(self):
print("---停车---")
#定义索纳塔车类
classSuonataCar(object):
#定义车的方法
defmove(self):
print("---车在移动---")
defstop(self):
print("---停车---")
#用来生成具体的对象
defcreateCar(typeName):
iftypeName =="伊兰特":
car = YilanteCar()
eliftypeName =="索纳塔":
car = SuonataCar()
returncar
#定义一个销售北京现代车的店类
classCarStore(object):
deforder(self, typeName):
#让工厂根据类型,生产一辆汽车
car = createCar(typeName)
returncar
使用类来实现
#定义伊兰特车类
classYilanteCar(object):
#定义车的方法
defmove(self):
print("---车在移动---")
defstop(self):
print("---停车---")
#定义索纳塔车类
classSuonataCar(object):
#定义车的方法
defmove(self):
print("---车在移动---")
defstop(self):
print("---停车---")
#定义一个生产汽车的工厂,让其根据具体的订单生产车
classCarFactory(object):
defcreateCar(self,typeName):
iftypeName =="伊兰特":
car = YilanteCar()
eliftypeName =="索纳塔":
car = SuonataCar()
returncar
#定义一个销售北京现代车的店类
classCarStore(object):
def__init__(self):
#设置4s店的指定生产汽车的工厂
self.carFactory = CarFactory()
deforder(self, typeName):
#让工厂根据类型,生产一辆汽车
car = self.carFactory.createCar(typeName)
returncar
咋一看来,好像只是把生产环节重新创建了一个类,这确实比较像是一种编程习惯,此种解决方式被称作简单工厂模式
工厂函数、工厂类对具体的生成环节进行了封装,这样有利于代码的后需扩展,即把功能划分的更具体,4s店只负责销售,汽车厂只负责制造