Python学习之面向对象(封装、继承、多态)
面向对象
关于面向对象大家应该很熟知,即使说不出他的概念,但是至少记住他的三大特征:封装、继承、多态。
封装
所谓封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
类的定义
基本形式:
class ClassName(object):
pass
- 1
- 2
- class定义类的关键字.
- ClassName类名,类名的每个单词的首字母大写(驼峰规则).
- object是父类名,object是一切类的基类。在python3中如果继承类是基类可以省略不写。
- pass 是类身体,由变量(类变量、实例变量)、方法组成(实例方法、静态方法、类方法)
示例:
class Animal():
eye=2 #类变量
def __init__(self,name):
self.animalName=name#实例变量
print("我是初始化方法,也可以叫我构造器")
def move(self):
print("我是实例方法")
@staticmethod
def eat(food):
Animal.eye
print("我是静态方法:",Animal.eye)
@classmethod
def run(cls):
print("我是类方法:",cls.eye)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
定义一个Animal类:
- 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
- 实例变量:定义在方法中的变量,属于实例。
- 初始化方法
__init__
:被称为类的构造函数或初始化方法,当创建了这个类的实例时就会调用该方法。 - 实例方法:类的实例化对象调用,
- self:代表类的实例,而非类本身,self 在定义实例方法时是必须有的,虽然在调用时不必传入相应的参数。
- 静态方法:用@staticmethod修饰,类可以不用实例化就可以调用该方法,当然也可以实例化调用,不强制要求传递参数。
- 类方法:用@classmethod修饰,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数。
类的实例化
实例化
没有new关键字,只需要一个实例名来接收类,并且赋上需要初始的值
dog=Animal("阿黄")
cat=Animal("喵喵")
- 1
- 2
调用方法:
实例方法的调用:
当实例调用时,默认将当前实例传进去。
类调用时,只能以 类名.method(类实例) 形式调用。
dog.move()
cat.move()
- 1
- 2
静态方法的调用:实例和类调用,没有默认的参数传进函数
Anmial.eat()
dog.eat()
- 1
- 2
类方法的调用:
当实例调用classmethod方法时,默认会把当前实例所对应的类传进去,
当类调用classmethod方法时,默认把此类传进去。
Anmial.run()
dog.eat()
- 1
- 2
至于__init__()
,是在实例化对象时自动调用
调用变量:
print(dog.eye)#2
print(dog.animalName)#阿黄
print(cat.eye)#2
print(cat.animalName)#喵喵
- 1
- 2
- 3
- 4
一个完整的示例:
class Animal():
eye=2 #类变量
def __init__(self,name):
self.animalName=name#实例变量
print("我是初始化方法,也可以叫我构造器")
def move(self,way):
print("我是实例方法:","%s在%s移动"%(self.animalName,way))
@staticmethod
def eat(self,food):
Animal.eye
print("我是静态方法:","%s吃%s"%(self.animalName,food))
@classmethod
def run(cls,self):
print("我是类方法:","%s有%s只眼睛"%(self.animalName,cls.eye))
dog=Animal("阿黄")#我是初始化方法,也可以叫我构造器
cat=Animal("喵喵")#我是初始化方法,也可以叫我构造器
dog.move("马路上")#我是实例方法: 阿黄在马路上移动
Animal.eat(dog,"骨头")#我是静态方法: 阿黄吃骨头
Animal.eat(cat,"小鱼")#我是静态方法: 喵喵吃小鱼
Animal.run(dog)#我是类方法: 阿黄有2只眼睛
Animal.run(cat)#我是类方法: 喵喵有2只眼睛
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
类的私有属性和私有方法
对于python中的类属性,或者方法,可以通过双下划线_或者单下划线来实现一定程度的私有化。
_:以单下划线开头只能允许其本身与子类进行访问,(对于实例只是隐藏起来了,可访问,可修改)。(protected)
__:以双下划线开头只能允许类本身调用,类的实例不能直接调用。(private)
python 的私有不是真正的私有,只是约定俗称的规则。即使私有了我们依然可以通过
dog._Animal__leg(但是dog._Animal_a 不可以访问)来访问私有变量__leg。当然设计者也可以在类中设置方法让访问者操作私有属性。
示例:
class Animal():
__leg="四条腿"
_eye="两只眼睛"
def __init__(self,name):
self.__name=name
def get__leg(self):
return self.__leg
def set__leg(self,leg):
self.__leg=leg
def __play(self):
print("%s在玩"%self.__name)
dog=Animal("小狗")
print(dog._eye)#两只眼睛
print(dog.get__leg())#四条腿
print(dog._Animal__leg)#四条腿
#print(dog._Animal_eye)#AttributeError: 'Animal' object has no attribute '_Animal_eye'
dog.set__leg("三条腿")
print(dog.get__leg())#三条腿
print(dog._Animal__name)#小狗
dog._Animal__play()#小狗在玩
print(dog.__dict__)#{'_Animal__name': '小狗', '_Animal__leg': '三条腿'}
print(dir(dog))#dir查看类的所有属性和方法
['_Animal__leg', '_Animal__name', '_Animal__play', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_eye', 'get__leg', 'set__leg']
从上述也可以看出__leg ,在内存中是_Animal__leg
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
注:前后都有双下划线的是python的特殊方法如__init__(), __del__()
等。
继承:
继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
python的继承分单继承和多继承。
单继承:
示例:
class man():
__sing="唱歌"
_dance="跳舞"
def __init__(self,name,age):
self.name=name
self.age=age
def say(self):
print("我是%s,我%s岁。"%(self.name,self.age))
class son(man):
def __init__(self,sex,name,age):
self.sex=sex
super().__init__(name,age)
class girl(man):
pass
son=son("男","张三",12)
son.say()#我是张三,我12岁。
girl=girl("小红",14)
girl.say()#我是小红,我14岁。
print(sorted(dir(son),reverse=True))
#['sex', 'say', 'name', 'age', '_man__sing', '_dance', '__weakref__', '__subclasshook__', '__str__', '__sizeof__', '__setattr__', '__repr__', '__reduce_ex__', '__reduce__', '__new__', '__ne__', '__module__', '__lt__', '__le__', '__init_subclass__', '__init__', '__hash__', '__gt__', '__getattribute__', '__ge__', '__format__', '__eq__', '__doc__', '__dir__', '__dict__', '__delattr__', '__class__']
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
son,girl都继承了man的属性和方法,从dir(son)可看出,__sing没有继承,_dance继承了。
多继承:
我举了一个祖孙三代的例子:
爷爷有一个名字,会说话,会踢足球;父亲继承了爷爷,但是会跑,并且重写了play方法会打篮球;小朋友是父亲的儿子,继承了父亲,自然也继承了爷爷,但是他并不会打篮球,他会踢足球。问题来了,爷爷和父亲都有play(),小朋友到底继承了谁的play()?
首先请看示例:
class Grandpa():
def __init__(self,name):
self.name=name
def say(self):
print("%s会说话"%self.name)
def play(self):
print("%s会踢足球"%self.name)
class Father(Grandpa):
def run(self):
print("%s会跑了"%self.name)
def play(self):
print("%s会打篮球"%self.name)
class Child(Father,Grandpa):
def play(self):
#super(Child, self).play()
super(Father, self).play()
print(Child.__mro__)#(<class '__main__.Child'>, <class '__main__.Father'>, <class '__main__.Grandpa'>, <class 'object'>)
c=Child("小朋友")
c.run()#小朋友会跑了
c.say()#小朋友会说话
c.play()#小朋友会踢足球
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
我们可以通过Child.__mro__
打印Child的继承路线:(请记住这个继承顺序,不能乱)
(<class '__main__.Child'>, <class '__main__.Father'>, <class '__main__.Grandpa'>, <class 'object'>)
- 1
首先小朋友是他自己,其次他是Father的孩子,其次是Granpa的孙子,再其次他的祖先是object,这个继承顺序不能乱,就像祖孙三代的关系不能乱。
默认小盆友是继承了父亲的打篮球,但是我现在希望的是小盆友是继承爷爷的踢足球,那就要重写play方法,修改继承顺序:
super(Father, self).play()
#super里写的Father并不是继承Father,而是Father的上一辈Grandpa
#默认是这个样子的
super(Child, self).play()
- 1
- 2
- 3
- 4
- 5
其实可以直接用类名调用相应的play方法(这样child就既会踢足球又会打篮球了)如:
def play(self):
Grandpa.play(self)
Father.play(self)
- 1
- 2
- 3
当然继承里也不能这样写Child(Grandpa,Father)
因为这样写的继承顺序是:
(<class '__main__.Child'>, <class '__main__.Grandpa'>, <class '__main__.Father'>,<class 'object'>)
- 1
系统会报错:
TypeError: Cannot create a consistent method resolution
order (MRO) for bases Grandpa, Father
假如Father又有了一个实例属性age(其他都省略,我们只讨论init())
class Grandpa():
def __init__(self,name):
self.name=name
class Father(Grandpa):
def __init__(self,age,name):
self.age=age
super().__init__(name)
class Child(Father,Grandpa):
'''def __init__(self,age,name):
super().__init__(age)
super(Father, self).__init__(name)'''
def sing(self):
print("我叫%s,我今年%s岁"%(self.name,self.age))
c=Child(12,"xfy")
c.sing()
f=Father(12,"f")
print(f.name)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
Father自己有了__init__()
,重写了Granpa的__init__()
,所以要调用Grandpa的__init__()
,这样Child就默认继承了Father的__init__()
,他就有了name和age。
多态:
所谓多态就是指一个类实例的相同方法在不同情形有不同表现形式。多态机制使具有不同内部结构的对象可以共享相同的外部接口。这意味着,虽然针对不同对象的具体操作不同,但通过一个公共的类,它们(那些操作)可以通过相同的方式予以调用。
python的多态并没什么好讲的
当派生类,重写了基类的方法时就实现了多态性。(子类重写父类方法)
python的封装、继承、多态就先告一段落,有任何疑问都可以留言评论。
文章来源: blog.csdn.net,作者:徐同学呀,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/weixin_36586120/article/details/79201338
- 点赞
- 收藏
- 关注作者
评论(0)