python面向对象二
1.1类的静态属性,类方法,类的静态方法
1.1.1静态属性(@property)
在类中:
python内置的@property装饰器就是负责把一个方法(函数)变成属性来调用。
class Student: def __init__(self,name,age,score): self.name = name self.age = age self.score = score @property def get_score(self): return self.score def learn(self): return self.name s1 = Student("小明",12,88) print(s1.get_score) #使用的@property 属性调用,简短了函数代码 print(s1.learn()) #原来方法的调用 # s1.get_score = 12 #不能修改该属性用了@property # s1.learn = 12 #可以修改该函数属性 没用@property #注@property广泛应用在类的定义中,让调用者写出简短的代码。
可以封装函数的逻辑,让用户调用的时候,让函数的方法看起来像普通属性。
1.1.2类方法(@classmethod)
class Kls: money = 10 def __init__(self,money): self.init = money + 10 @classmethod def Get_no_of_instance(cls): #cls应该是传入的一个类名 # print(cls.money) 相当于是#print(Kls.money) return cls.money s1 = Kls(12) #类实例化一个对象 print(s1.init) #查看实例属性的变量init的值 print(s1.money) #查看类属性的变量 Kls.Get_no_of_instance() s1.Get_no_of_instance() #这里的调用,会把实例所属的类给传进去 #注:这里是类在调用自已的方法,跟实例没有任何关系
@classmethod类方法,只是给类使用(不管是否存在实例),只能访问实例变量
1.1.3静态方法(@staticmethod)
经常有一些跟类有关系的功能但在运行时,又不需要实例和类参与的情况下需要用到静态方法,比如更改其他类的属性等能用到的方法。
class Kls: money = 10 def __init__(self,money): self.init = money + 10 @classmethod def Get_no_of_instance(cls): #cls应该是传入的一个类名 # print(cls.money) 相当于是#print(Kls.money) return cls.money @staticmethod def test(x,y): print(x,y) #这跟类的变量,和实例变量完全没关系 return x,y def do(self): print("这里会把自已实例(self)给传进去") s2 = Kls(55) s2.test(1,2) Kls.test(11,22)
@staticmethod只是名义上归属类的管理,不能使用类的变量和实例的变量,只是类的工具包,很少用。
1.2类组合
例如:定义一个人的类,人有头,躯干,手,脚等数据属性,这几个属性可以是通过一个类实例化的对象,这就是组合。
组合用途:
1:做关联
2:小的组成大的
class Hand: pass class Foot: pass class Trunk: pass class Head: pass class Person: def __init__(self,id_num,name,hand,foot,trunk,head): self.id_num=id_num self.name=name self.hand=Hand() self.foot=Foot() self.trunk=Trunk() self.head=Head()
例:
class School: def __init__(self,name,addr): self.name = name self.addr = addr def tell_info(self): print("shcool %s %s" %(self.name,self.addr)) class Course: def __init__(self,name,price,period,school): self.name = name self.price = price self.period = period self.schoool = school class Tearcher: def __init__(self,name,hobby,school): self.name = name self.hobby = hobby self.school = school s1 = School("北大","北京") s2 = School("上大","上海") s3 = School("重大","重庆") info = """ 1 北大 北京校区 2 上大 上海校区 3 重大 深圳校区 """ while True: print(info) menu = { "1":s1, "2":s2, "3":s3, } choice = input("请选择学校>>:") school_obj = menu[choice] name = input("课程名>>:") price = input("课程价格>>:") period = input("课程周期>>:") Tname = input("老师名字>>") Thobby = input("老师爱好>>") teacher_obj = Tearcher(Tname,Thobby,school_obj) new_course = Course(name,price,period,school_obj) print("%s【老师】【爱好】%s【课程】%s【价格】%s【周期】%s【校区】%s 【地址】%s"\ %(teacher_obj.name,teacher_obj.hobby,\ new_course.name,new_course.price,new_course.period,\ school_obj.name,school_obj.addr))
1.3面向对象编程三大特性
1.继承
2.封装
3.多态
1.3.1继承
类的继承:就跟生活中父,子,继承的关系类似,父类又称为基类。
python中的类的继承分为:单继承和多继承
class ParentClass1: #基类 pass class ParentClass2: #基类 pass class SubClass(ParentClass1): #单继承 子类 pass class SubClass(ParentClass1,ParentClass2): #多继承 子类(subclass) pass
1.3.2子继承继承了父类什么属性
继承的本质是父类把自已的属性引用传递给了子类,子类可以调用父类的属性,其实父类的属性是不属于儿子的
因此。在子类中定义的任何数据属性和函数属性都存在于子类的属性中,调用时优先从自已的属性字典里面查。
class Father: money = 100 #父类自已有100 def __init__(self): pass def Teach(self): print("教儿子") class Son(Father): money = 9999999 #儿子通过发展的9999999 s1 = Son() print(s1.money) #子类自已的属性 s1.Teach() #子类调用父类的方法
1.3.3什么时候用继承
1.当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好
例如:描述一个机器人类,机器人这个大类是由很多互不相关的小类组成,如机械胳膊类、腿类、身体类、电池类
2.当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好
例如:
猫可以:喵喵叫、吃、喝、拉、撒
狗可以:汪汪叫、吃、喝、拉、撒
如果我们要分别为猫和狗创建一个类,那么就需要为 猫 和 狗 实现他们所有的功能,如下所示:
class 猫: def 喵喵叫(self): print '喵喵叫' def 吃(self): # do something def 喝(self): # do something def 拉(self): # do something def 撒(self): # do something class 狗: def 汪汪叫(self): print '喵喵叫' def 吃(self): # do something def 喝(self): # do something def 拉(self): # do something def 撒(self): # do something 伪代码
上述代码不难看出,吃,喝、拉、撒是猫和狗都具有的功能,而我们却分别的猫和狗的类中编写了两次。如果使用 继承 的思想,如下实现:
动物:吃、喝、拉、撒
猫:喵喵叫(猫继承动物的功能)
狗:汪汪叫(狗继承动物的功能)
class 动物: def 吃(self): # do something def 喝(self): # do something def 拉(self): # do something def 撒(self): # do something # 在类后面括号中写入另外一个类名,表示当前类继承另外一个类 class 猫(动物): def 喵喵叫(self): print ('喵喵叫') # 在类后面括号中写入另外一个类名,表示当前类继承另外一个类 class 狗(动物): def 汪汪叫(self): print ("汪汪汪") 伪代码
继承的实现
#!/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 ("%s 撒 " %self.name) class Cat(Animal): def __init__(self, name): self.name = name self.breed = '猫' def cry(self): print('喵喵叫') class Dog(Animal): def __init__(self, name): self.name = name self.breed='狗' def cry(self): print('汪汪叫') # ######### 执行 ######### c1 = Cat('黑猫') c1.eat() c2 = Cat('白猫') c2.drink() d1 = Dog('二哈') d1.eat() d1.cry()
继承同时具有的两种含义:
1.继承基类的方法,并且做出自已的改变或者扩展(代码重用)
2.声明某个子类兼容与某积累,定义一个接口类,子类继承接口类,并且实现接口中定义的方法。
但是在实践中,继承的第一种含义并不很大,甚至常常是有害的,因为它使得子类与积累出现强耦合。
继承的第二种函数非常重要,又叫“接口继承”
接口继承实质上是要求作出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体的细节。可一视同仁的处理时限了特定接口的所有对象。这种程序设计上,叫归一化。
1.3.4类的继承顺序
上面的是python2中类的继承顺序
python3中类的继承顺序
#!/usr/bin/env python #-*- coding:utf-8 -*- class A: def test(self): print("A") class B(A): # def test(self): # print("B") pass class C(A): def test(self): print("C") class D(B): # def test(self): # print("D") pass class E(C): def test(self): print("E") class F(D,E): # def test(self): # print("F") pass f = F() f.test() # print(F.mro()) print(F.__mro__) #python3中类继承顺序:F->D->B->E->C->A
#python2经典类继承顺序:F->D->B->A->E->C python2经典类, 需要切换到python2的解释器
python对类的继承顺序。是根据mro的解析来继承的。
这个mro列表就是一个简单的所有积累的线形顺序的列表。
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
1.3.5子类中调用父类的方法
子类继承了父类的方法,然后想进行修改。注:是基于原有的基础上修改,那么就需要在子类中调用父类的方法。
方法一:父类名.父类方法()
class Vehicle: # 定义交通工具类 父类 Country = 'China' def __init__(self, name, speed, load, power): self.name = name self.speed = speed self.load = load self.power = power def run(self): print('开动啦...') class Subway(Vehicle): # 地铁 子类 def __init__(self, name, speed, load, power, line): Vehicle.__init__(self, name, speed, load, power) #父类的属性 self.line = line def run(self): print('地铁%s号线欢迎您' % self.line) Vehicle.run(self) #子类中调用父类的方法 line13 = Subway('中国地铁', '180m/s', '1000人/箱', '电', 13) line13.run()
方法二:super()
class Vehicle: #定义交通工具类 Country='China' def __init__(self,name,speed,load,power): #注 self.name=name self.speed=speed self.load=load self.power=power def run(self): print('开动啦...') class Subway(Vehicle): #地铁 def __init__(self,name,speed,load,power,line): #super(Subway,self) 就相当于实例本身 super(Subway,self).__init__(name,speed,load,power) #注:调用父类的属性 self.line=line def run(self): print('地铁%s号线欢迎您' %self.line) super(Subway,self).run() #子类调用父类的方法supper line13=Subway('中国地铁','180m/s','1000人/箱','电',13) line13.run()
当你使用super()函数时,Python会在MRO列表上继续搜索下一个类。只要每个重定义的方法统一使用super()并只调用它一次,那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次 。
1.4多态
什么是多态
类的继承有两层含义:改变 扩展
多态就是类的这两层意义的一个具体的实现机制。即调用不同的类实例化得到对象下的相同方法。实现的过程不一样而已了
python中的标准类型就是多态概念的一个很好的示范如(str.__len__(),list.__len__(),tuple.__len__可以len(str),len(list),len(tuple))
多态实际上是依附于继承的两种含义的:“改变”和“扩展”本身就意味着必须有机制去自动选用你改变/扩展过的版本,故无多态,则两种含义就不可能实现。
所以,多态实质上是继承的实现细节;那么让多态与封装、继承这两个概念并列,显然是不符合逻辑的
1.5封装
什么是封装:在实例生活中,装就好比一个麻袋,用来存放物品;封,就是这个麻袋口子给封上。
在面向对象中这个麻袋就是类或者对象,内部装了数据属性和函数属性,那么对于类和对象来说,封装的慨念就表示隐藏。
例:
就是类中定义私有的属性和方法,只在类的内部使用,外部无法访问。
python不依赖语言特性去实现第二层面的封装,而是通过遵循一定的数据属性和函数属性的命名约定来达到封的效果
约定一:任何一单下划线开头的名字都应该是内部的,私有的。
class People: _star='earth' def __init__(self,id,name,age,salary): self.id=id self.name=name self._age=age self._salary=salary def _get_id(self): print('我是私有方法啊,我找到的id是[%s]' %self.id) #我们明明约定好了的,只要属性前加一个单下划线,那他就属于内部的属性,不能被外部调用了啊,为何还能调用??? print(People._star) p1=People('3706861900121221212','yj',18,10) print(p1._age,p1._salary) p1._get_id()
上面私有_属性:还能访问。
python并不会真的阻止你访问私有属性,这只是以一种约定。
约定二:双下划线开头的名字
class People: __star='earth' #私有 star = "moon" #共有 def __init__(self,id,name,age,salary): self.id=id self.name=name self.__age=age #私有 self._salary=salary #私有 def _get_id(self): print('我是私有方法啊,我找到的id是[%s]' %self.id) p1=People('333333','xixi',18,10) print(People.star)#能访问 print(p1._salary) #能访问 print(People.__star)#不能访问
私有属性怎么可以访问:
例:
class Site: def __init__(self, name, url): self.name = name # public self.__url = url # private def who(self): print('name : ', self.name) print('url : ', self.__url) def __foo(self): # 私有方法 print('这是私有方法') def foo(self): # 公共方法 print('这是公共方法') self.__foo() # print(Site.__dict__) x = Site('baidu', 'www.baidu.com') print(x.__dict__)#查看对象的属性字典 print(x._Site__url) #通过属性字典调用私有方法 # print(Site.__dict__)#查看类的属性字典 x._Site__foo()
为什么可以访问私有属性了?
python没有从根本上限制你的访问。python之所以这么设计,原因就是:python做成非严格意义的封装,避免我们滥用封装。
封装在于明确区分内外,使得类实现则可以修改封装内的东西,而不影响外部调用者;而外部调用者也可以知道自己不可以碰哪里。这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。