您的位置:首页 > 编程语言 > Python开发

Python学习之面向对象(封装、继承、多态)

2018-01-30 01:04 771 查看

面向对象

关于面向对象大家应该很熟知,即使说不出他的概念,但是至少记住他的三大特征:封装、继承、多态。

封装

所谓封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。

类的定义

基本形式:

class ClassName(object):
pass


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)


定义一个Animal类:

类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。

实例变量:定义在方法中的变
4000
量,属于实例。

初始化方法
__init__
:被称为类的构造函数或初始化方法,当创建了这个类的实例时就会调用该方法。

实例方法:类的实例化对象调用,

self:代表类的实例,而非类本身,self 在定义实例方法时是必须有的,虽然在调用时不必传入相应的参数。

静态方法:用@staticmethod修饰,类可以不用实例化就可以调用该方法,当然也可以实例化调用,不强制要求传递参数。

类方法:用@classmethod修饰,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数。

类的实例化

实例化

没有new关键字,只需要一个实例名来接收类,并且赋上需要初始的值

dog=Animal("阿黄")
cat=Animal("喵喵")


调用方法:

实例方法的调用:

当实例调用时,默认将当前实例传进去。

类调用时,只能以 类名.method(类实例) 形式调用。

dog.move()
cat.move()


静态方法的调用:实例和类调用,没有默认的参数传进函数

Anmial.eat()
dog.eat()


类方法的调用:

当实例调用classmethod方法时,默认会把当前实例所对应的类传进去,

当类调用classmethod方法时,默认把此类传进去。

Anmial.run()
dog.eat()


至于
__init__()
,是在实例化对象时自动调用

调用变量:

print(dog.eye)#2
print(dog.animalName)#阿黄
print(cat.eye)#2
print(cat.animalName)#喵喵


一个完整的示例:

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只眼睛


类的私有属性和私有方法

对于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


注:前后都有双下划线的是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__']


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()#小朋友会踢足球


我们可以通过
Child.__mro__
打印Child的继承路线:(请记住这个继承顺序,不能乱)

(<class '__main__.Child'>, <class '__main__.Father'>, <class '__main__.Grandpa'>, <class 'object'>)


首先小朋友是他自己,其次他是Father的孩子,其次是Granpa的孙子,再其次他的祖先是object,这个继承顺序不能乱,就像祖孙三代的关系不能乱。

默认小盆友是继承了父亲的打篮球,但是我现在希望的是小盆友是继承爷爷的踢足球,那就要重写play方法,修改继承顺序:

super(Father, self).play()
#super里写的Father并不是继承Father,而是Father的上一辈Grandpa

#默认是这个样子的
super(Child, self).play()


其实可以直接用类名调用相应的play方法(这样child就既会踢足球又会打篮球了)如:

def play(self):
Grandpa.play(self)
Father.play(self)


当然继承里也不能这样写
Child(Grandpa,Father)


因为这样写的继承顺序是:

(<class '__main__.Child'>, <class '__main__.Grandpa'>,  <class '__main__.Father'>,<class 'object'>)


系统会报错:

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)


Father自己有了
__init__()
,重写了Granpa的
__init__()
,所以要调用Grandpa的
__init__()
,这样Child就默认继承了Father的
__init__()
,他就有了name和age。

多态:

所谓多态就是指一个类实例的相同方法在不同情形有不同表现形式。多态机制使具有不同内部结构的对象可以共享相同的外部接口。这意味着,虽然针对不同对象的具体操作不同,但通过一个公共的类,它们(那些操作)可以通过相同的方式予以调用。

python的多态并没什么好讲的

当派生类,重写了基类的方法时就实现了多态性。(子类重写父类方法)

python的封装、继承、多态就先告一段落,有任何疑问都可以留言评论。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: