python面向对象OOP入门

简介:

 一、面向对象简介

      面向对象编程不是python独有,几乎所有高级语言都支持;面向对象不管在那个语言中都有三大特性:即:封装、继承、多态;具体的本文主要讲python面向对象--类及三大特性的具体实现;



二、面向对象之python类特性一:封装

       python通过类实现面向对象的编程;编程大致分为面向过程式的函数式编程,和面向对象编程;

类(Class)和实例(Instance)是面向对象最重要的概念。

1、类的简单定义与使用

1
2
class  '类名' :
     语句块

如:

1
2
3
4
5
6
7
class  Foo:
     def  bar( self ):
         print ( "Foo.bar" )
         
         
obj  =  Foo()    #obj即是Foo类的实例
obj.bar()      #通过实例去调用调用Foo类中的方法

结果输出Foo.bar

以上就是最简单的类定义与使用;

2、self是什么?

我们注意到以上的示例中除了class关键字用来定义类,def定义方法(类中的函数我们称方法,可以理解为类中封装的函数就是方法)后面有一个self参数,那么 这个self参数是什么?先来一个例子:

1
2
3
4
5
6
7
class  Foo:
     def  bar( self ,arg):
          print ( self ,arg)
          
z1  =  Foo()
print (z1)
z1.bar( 11 )

输出结果:

9aa71c6f354aba5244e37212544fbef4.png-wh_

以上结果可以看到z1是Foo实例打印出内存地址和self的内存地址一样,这说明self就代指实例本身;

因此self就是类实例化出来的对象(实例 ),通俗的讲,就是那个实例对象调用,就代指那个实例对象


3、实例出对象

实例对象是从类实例化而来obj = 类名([参数....]),拥有自己的作用域命名空间,就是拥有自己的内存地址空间;同时可以对实例对象添加赋值;用于保存实例自己的数据;同时可以调用类中封装好的方法或数据;

1
2
3
4
5
6
7
8
class  Foo:
     def  bar( self ,arg):
         print ( self .name, self .age,arg)
 
obj  =  Foo()       #实例化obj对象
obj.name  =  "san"    #向实例中添加对象name并赋值
obj.age  =  18         #向实例中添加对象age并赋值
obj.bar( 666 )         #调用类方法并传入参数6666

输出结果:

('san', '18', 666)

从上面的示例可以看出类在定义时并没有创建name 和age ;obj实例对象创建后通过赋值

让obj拥有了name和age;因此实例obj在调用类中bar方法时可以获取自身命名空间中name

和age值;实际上实例化后的对象如果没有做出限制(__slots__),可以向对象中保存添加任意多的值;


4、通过__slots__限制姨实例对象添加赋值

通过以上示例我们了解到,在从类实例化对象后,可以往对象中添加任意多的值,只要内存足够大;然而任何无节制的增加,都有可能带来隐患,所以我们通过__slots__功能来限制实例对象的赋值;

1
2
3
4
5
6
7
8
9
10
11
12
13
class  Foo:
     __slots__  =  ( "name" , "age" )     #只允许往实例赋name,age
     def  bar( self ,arg):
         print ( self .name, self .age,arg)
     def  Other( self ):      
         print ( self .other)
   
=  Foo()           #实例化z
z.name  =  "san"      #向z对象赋值name
z.age  =  18          #向z对象赋值age
z.bar( "6666" )
z.other  =  "othter"    #向z对象赋值other
z.Other()

运行结果如图:

ea9e37732cb6c62cc58c1570d1a24778.png-wh_

这次没有赋值成功,被限制了提示:“AttributeError: 'Foo' object has no attribute 'other'“;这正是我们所要的。


5、构造函数__init__

对于以上的示例我们往实例化后的对象中赋值也可以限制能赋的值,如果要实例化出多个类对象,每个类对象有共同的属性值,如name,age,sex等,那么我们可以通过构造函数__init__方法来实现,即在实例化创建对象时传入name,age,来达到类似obj.name ="san" obj.age = 18这样的效果:

1
2
3
4
5
6
7
8
9
10
class  Foo:
     def  __init__( self ,name,age):    #构造方法,这里有几个参数,实例化对象时就要传递对应的参数
         self .name  =  name
         self .age  =  age
         print ( "__init__ is start." )    #对象实例化时自动执行
     def  foo( self ,arg):
         print ( self .name, self .age,arg)
         
obj  =  Foo( "san" , 18 )
obj.bar( "good" )

运行结果如图:

18c3e5747dc3d500e64c039d5759db01.png-wh_

从运行结果中可以看出 只要实例化出对象obj = Foo(["参数...."])时就执行__init__方法中的内容;利用这个功能,我们给各实例传入name,age参数时自动赋值;这就是构造方法的功能;其中参数可选,有无参数在于你设计类时是否需要。


三、类特性二:继承

继承民是面向对象的特性之一,即子类(又叫派生类)继承父类(又叫基类或超类)的特性(属性,方法等);同时子类可以重写父类中的方法或属性。说到继承这时提一嘴,python2.x和python3.x继承有点区别,python2.x是经典类,通过class 类名(object)变成新式类,python3.x默认是新式类;新式类和经典类的区别在于,多继承时,继承顺序不一样;经典类采用”深度优先“方式 查找;而新式类则是广度优先去查找;这里就不详述,以下的示例在python 2.7中所以你会看到类名后有一个object,python3.x下可以不用object。关于深度优先和广度优先这里先不详述,下面有示例:


1、简单继承

示例:

1
2
3
4
5
6
7
8
9
10
11
12
class  F( object ):            #父类F
     def  f1( self ):
             print ( "F.f1" )
     def  f2( self ):        
             print ( "F.f2" )
class  S(F):                 #子类S
     def  s1( self ):
             print ( "s.s1" )
             
son  =  S()     #从S类实例化对象son
son.s1()      #调用S类的s1方法
son.f1()      #由于在S类中没有找到f2,所以到父类F中找到f1

运行结果如图:

2fba26d013759d6e4fbd3ae2b784f7e0.png-wh_

说明:虽然S类中没有f2方法,但由于S类继承了F类,而F类中有f1方法,因此可以调用F中的f1方法。


2、重写

有时候我们在继承了父类后,父类中的方法可能并不能满足需求,直接修改父类中的方法又破坏了原类,可能影响到其他引用,此时可以通过在子类中定义一个父类中同名的方法,就可以达到重写父类方法的目的。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class  F( object ):
     def  f1( self ):
         print ( "F.f1" )
     def  f2( self ):
         print ( "F.f2" )
class  S(F):
     def  s1( self ):
         print ( "s.s1" )
     def  f2( self ):   # 不继承,重写
         print ( "S.f2" )
son  =  S()
son.s1()   #S类的s1方法
son.f1()   #继承F类的f1方法
son.f2()   #S自己的f2方法,重写(覆盖)F类的f2方法

运行结果:

b1fafa511759fe0979af523debe8eb62.png-wh_

3、子类中调用超类方法

虽然上面的例子中重写了父类中的同名方法,满足了需求,但能否在重写的基础上,引用超类中的方法呢?即先运行超类中的同名方法,再定义自己的同名方法?答案是必须可以啊!

这里有两种方法,一起列举,现实中只用其中的一种,建议使用super:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class  F( object ):
     def  f1( self ):
         print ( "F.f1" )
     def  f2( self ):
         print ( "F.f2" )
class  S(F):
     def  s1( self ):
         print ( "s.s1" )
     def  f2( self ):   # 不继承,重写
         print ( "S.f2" )
         super (S, self ).f2()     #调用父类f2方法一: super(子类,self).父类中的方法(...)
         F.f2( self )             #调用父类f2方法二:父类.方法(self,....)
         
son  =  S()
son.s1()   #S类的s1方法
son.f1()   #继承F类的f1方法
son.f2()   #S自己的f2方法,重写(覆盖)F类的f2方法

运行结果:

51e043336dc34b0227b62683247ab525.png-wh_

由于S类中的f2方法除了输出自己的功能S.f2外还通过两种调用父类F中的f2方法,所以输出了两次F.f2;

注意这里为了演示只调用了父类中的f2方法,达到继承和重写优化父类中的f2方法,也可以通过这两种方法中的一种调用父类中的其他方法;


4、类多继承

类可以继承多个类,即在class 类名(父类1,父类2,...)那么问题来,如果所继承的类都有一个同名的方法,那调用时如何选择?上面提到了经典类是深度优先,新式类时广度优先,本文不做深度优先和广度优先查找比较,只讲新式类的广度优先;有兴趣的可以自行查找资料。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class  Base( object ):
     def  a( self ):
         print ( "Base.a" )
class  F1(Base):
     def  a1( self ):
         print ( 'F1.a' )
     def  a11( self ):
         print ( "F1.a11" )
         
class  F2(Base):
     def  a2( self ):
         print ( 'F2.a' )
     def  a11( self ):
         print ( "F2.a11" )
class  S(F1,F2):
     pass
class  S2(F2,F1):
     pass
obj  =  S()
obj.a11()
obj2  =  S2()
obj2.a11()

运行结果:

d6e7b907756a11b080b43873044109ff.png-wh_

obj继承顺序是F1,F2   结果是F1.a11

obj2继承顺序是F2,F1 结果是F2.all


5、有交集的多继承查找


obj对象从A类实例化而来,A类继承了B和C类,B类继承D,C类也继承D类;baba方法在C类和D类中都有,此时obj.baba()方法结果是怎样?

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class  D:
     def  bar( self ):
         print ( 'D.bar' )
     def  baba( self ):
         print ( "D.baba" )
class  C(D):
     def  bar( self ):
         print ( 'C.bar' )
     def  baba( self ):
         print ( "C.baba" )
class  B(D):
     def  bar( self ):
         print ( 'B.bar' )
class  A(B, C):
     def  bar( self ):
         print ( 'A.bar' )
 
obj  =  A()
obj.bar()
obj.baba()

运行结果:

240e625fda7accf24feb8d18c15590c5.png-wh_


# 执行bar和baba方法时
# 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错
# 所以,查找顺序:A --> B --> C --> D
# 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了


同样的例子我们调回python2.7作为经典类执行,看结果。

2a17181b8338f0c29f30416761f2e9df.png-wh_

 执行bar和baba方法时
首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错
 所以,查找顺序:A --> B --> D --> C
 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了

这里的baba方法在D中找到,所以显示为D.baba

深度优先和广度优先查找顺序如图:

67efcf62e4f16c675b9552340a4f0194.png-wh_


6、多继承之__init__执行顺序

以下是一个模拟实践中多继承情况下__init__构造方法的执行,有利于我们阅读项目源码。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class  BaseRequest:     #(3)
    def  __init__( self ):
        print ( "BaseRequest.init" )
class  RequestHandler(BaseRequest):   #(2)
     def  __init__( self ):
         print ( "RequestHandler.init" )
         BaseRequest.__init__( self )    #调用父类的__init__方法
     #     super(RequestHandler,self).__init__()
 
     def  serve_forever( self ):      #(5)
         print ( "RequestHandler.serve_forever" )
         self .process_request()
 
     def  process_request( self ):
         print ( 'RequestHandler.process_request' )
class  Minx:
     def  process_request( self ):    #(6)
         print ( "Minx.process_request" )
 
class  son(Minx,RequestHandler):
     pass
 
obj  =  son()      #(1)
obj.serve_forever()     #(4)

以上的注释后的数字是执行的流程。obj = son()的作用:

实例化出obj对象;

执行__init__构造方法,在多继承环境 下,和上面讲的调用其他类方面查找一致,首先查找 son类中的__init__如果没有按新式类的方度优先顺序查找所继承类中的__init__,这里首先找到Minx,没有,再找到RequestHandler类,执行打印“RequestHandler.init”,里的__init__再次执行了父类中的__init__方法,因此又打印出"BaseRequest.init";

执行obj.serve_forever()时,同样的查找,打印出“RequestHandler.serve_forever”,注意这里又调用 了self.process_request()方法,由于self指的就是所调用的对象obj即obj.process_request()方法,因此重新从son(Minx,RequestHandler)中按顺序查找,而不是RequestHandler类下的 process_reques方法。因此找到Minx下的process_request方法,得到结果:“Minx.process_request”


执行结果如下:

43461690e1de0c056789b9529534ec27.png-wh_



四、python面向对像之多态

       多态简单的理解就是同一个方法在不现的对象调用时显示出的效果不一样,如+ 在整形数字相加时是数学运算,在字符串相加时加是连接符;

       python的面向对象原生支持多态,不像java等强类型语言,传递参数时必须指定类型,而python没有此此限制,这也是python原生动支持多态的原因之一。



本文主要说明了python面向对象的三大特性,封装,继承及多态,继承有多继承,新式继承与经典类继承的区别。个人总结,如有不当之处欢迎指正。










本文转自 dyc2005 51CTO博客,原文链接:http://blog.51cto.com/dyc2005/1982808,如需转载请自行联系原作者
目录
相关文章
|
29天前
|
监控 安全 应用服务中间件
python中Django入门(四)
python中Django入门(四)
28 0
|
26天前
|
存储 安全 API
【Python 基础教程 21】Python3 文件操作全面指南:从入门到精通的综合教程
【Python 基础教程 21】Python3 文件操作全面指南:从入门到精通的综合教程
73 0
|
5天前
|
JavaScript 前端开发 API
游戏开发入门:Python后端与Vue前端的协同工作方式
【4月更文挑战第11天】使用Python后端(Flask或Django)和Vue.js前端开发游戏变得流行,能提高开发效率和可维护性。本文指导如何构建这样的项目,包括设置环境、创建虚拟环境、搭建后端API及前端Vue组件,强调前后端协作和API接口的重要性。这种架构促进团队合作,提升代码质量和游戏体验。
|
7天前
|
机器学习/深度学习 人工智能 算法
机器学习基础:使用Python和Scikit-learn入门
【4月更文挑战第9天】本文介绍了使用Python和Scikit-learn进行机器学习的基础知识和入门实践。首先,简述了机器学习的基本概念和类型。接着,展示了如何安装Python和Scikit-learn,加载与处理数据,选择模型进行训练,以及评估模型性能。通过本文,读者可了解机器学习入门步骤,并借助Python和Scikit-learn开始实践。
|
9天前
|
机器学习/深度学习 数据可视化 数据挖掘
利用Python进行数据分析与可视化:从入门到精通
本文将介绍如何使用Python语言进行数据分析与可视化,从基础概念到高级技巧一应俱全。通过学习本文,读者将掌握Python在数据处理、分析和可视化方面的核心技能,为实际项目应用打下坚实基础。
|
26天前
|
存储 算法 数据挖掘
【Python 基础教程 25】全面入门指南:深度解析Python3的命名空间,作用域及变量使用教程
【Python 基础教程 25】全面入门指南:深度解析Python3的命名空间,作用域及变量使用教程
50 0
|
26天前
|
存储 机器学习/深度学习 数据安全/隐私保护
【Python 基础教程 24】全面入门Python面向对象编程:深度探索与实战教程
【Python 基础教程 24】全面入门Python面向对象编程:深度探索与实战教程
76 0
|
26天前
|
Linux 数据库连接 C++
【Python 基础教程 23】Python3 错误与异常处理全面指南:从入门到精通的实用教程
【Python 基础教程 23】Python3 错误与异常处理全面指南:从入门到精通的实用教程
103 0
|
26天前
|
监控 API C语言
【Python 基础教程 22】全面揭秘Python3 os模块:从入门到高级的实用教程指南
【Python 基础教程 22】全面揭秘Python3 os模块:从入门到高级的实用教程指南
60 1
|
26天前
|
存储 前端开发 C++
【Python 基础教程 09】全面掌握Python3列表:从入门到精通的综合教程与实战指南
【Python 基础教程 09】全面掌握Python3列表:从入门到精通的综合教程与实战指南
86 1