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

网友投稿 632 2022-05-30

面向对象

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

封装

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

类的定义

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学习之面向对象(封装、继承、多态)

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__)#(, , , ) 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的继承路线:(请记住这个继承顺序,不能乱)

(, , , )

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)

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

(, , ,)

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的封装、继承、多态就先告一段落,有任何疑问都可以留言评论。

Python 面向对象编程

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:创建型设计模式简介
下一篇:ZooKeeper快速入门系列(3) | Zookeeper的内部原理(六大原理)
相关文章