python面向对象编程(代码片段)

礁之 礁之     2023-01-19     423

关键词:

文章目录


此文参考廖雪峰官网:面向对象编程 - 廖雪峰的官方网站 (liaoxuefeng.com)

一、什么是面向对象编程

  • 面向对象编程(Object Oriented Programming),简称OOP,是一种程序设计思想,OOP把对象当作程序的基本单元,一个对象包含了数据和操作数据的函数
  • 面向对象的程序设计把计算机程序当作一组对象的集合,每个对象都可以接受其他对象发来的消息,并且进行处理,而计算机程序的执行就是一系列消息在各对象之间进行传递,和面向对象不同,面向过程的程序设计把计算机程序当作一系列的命令集合,即一组函数的顺序执行,为了简化程序设计,面向过程把函数继续切分为子函数,从而降低系统的复杂度
  • 在Python中,所有的数据类型都可以看作为对象,当然也可以自定义对象,自定义的对象数据类型就是面向对象中类(class)的概念,下面来看一个案例来说明面向对象和面向过程的区别:
- 现在我们需要处理学生的成绩表,为了表示一个学生的成绩,面向过程的程序可以使用一个字典表示,例如:
# -*- coding: utf-8 -*-
std_1 = 'name':'zhangsan','score':98
std_2 = 'name':'lisi','score':97 

def print_score(std):
    return '%s : %s' % (std['name'],std['score'])
    
print(print_score(std_1))
print(print_score(std_2))

#输出:
zhangsan : 98
lisi : 97
  
- 如果使用面向对象的程序设计思想,首先思考的不是程序的执行流程,而是'学生'的这种数据类型应该被看作一个'对象',这个对象有'name''score'两种'属性(property)',如果想要输出一个学生的成绩,首先就需要先创建一个'学生'对应的对象,然后给这个'学生'对象发送一个'打印成绩''消息',让对象自己把指定的数据打印出来,例如:
# -*- coding: utf-8 -*-
class Student(object):

    def __init__(self,name,score):
        self.name = name
        self.score = score

    def print_score(self):
        print('%s : %s' % (self.name,self.score))

- 给对象发送消息实际就是调用对象对应的'关联函数',这个关联函数也叫做'对象的方法',下面就是面向对象的程序调用
zhangsan = Student('zhangsan',98)
lisi = Student('lisi',97)
zhangsan.print_score()
lisi.print_score()

#输出:
zhangsan : 98
lisi : 97
  • 对象(class)是一种抽象的概念,上面定义的对象Student,指的就是学生这个概念,而实例(instance)则指一个个具体的对象,例如上面的zhangsanlisi就是两个具体的Student,也就是实例
  • 从上面的案例可以看出,面向对象的程序设计思想其实就是抽象出对象(class),然后根据对象创建实例(instance)
  • 最后,面向对象的抽象程度比函数高,因为一个对象既包含数据,也包含操作数据的方法,数据封装、继承、多态是面向对象的三大特点

二、类(class)和实例(instance)

  • 面对对象最重要的概念就是类(class)实例(instance),类是抽象的模板,比如上面的Student类,而实例是根据类创建出来的具体的对象,每个对象都有相同的方法,但是各自的数据可能不同,例如上面的zhangsanlisi
  • Student类为例:
# -*- coding: utf-8 -*-
class Student(object):
    pass

(1)先看第2行

class Student(object):
 
在Python中,类是通过'class'关键字进行定义的,'class'后面跟着的是类名,类名通常是以大写字母开头的,紧接着就是'(object)',这个表示的是'Student'类是从'object'类继承下来的,继承这个概念在后面会说
通常如果说没有合适的继承类,那么就可以直接使用'object'类,这是所有的类最终都会继承的类

(2)定义好了Student类,就可以根据Student类创建出Student的实例,而创建实例是通过类名()实现的,例如:

>>> class Student(object):
...     pass
... 
>>> zhangsan = Student()
>>> zhangsan
<__main__.Student object at 0x0000018C3E6A6A10>
>>> Student
<class '__main__.Student'>

可以看到,变量'zhangsan'指向的是'Student'实例,输出的信息中,'object at'后面是内存地址,每个object的地址都不一样,而'Student'本身就是一个类

还可以给一个实例变量自由的绑定属性,例如:
>>> zhangsan.name = 'zhangsan' 
>>> zhangsan.name
'zhangsan'

(3)由于类可以起到模板的作用,因此,可以在创建实例的时候,把一些必要的属性写进入,通过一个特殊的__init__方法,在创建实例的时候,把namescore等属性绑定,例如:

>>> class Student(object):
...     def __init__(self,name,score):
...             self.name = name
...             self.score = score
... 
>>> zhangsan = Student('zhangsan',98) 
>>> zhangsan.name
'zhangsan'
>>> zhangsan.score
98
>>> lisi = Student()    #传入空参数
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Student.__init__() missing 2 required positional arguments: 'name' and 'score'
>>> lisi = Student('lisi',97,22)  #多传入一个参数
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Student.__init__() takes 3 positional arguments but 4 were given

#注意特殊方法__init__是两个_
可以看到在定义特殊方法'__init__'时,后面的第一个参数是'self',而且也必须是'self',这个参数表示创建的实例本身,因此,在'__init__'方法内部,就可以把各种属性绑定到'self',因为'self'就指向创建的实例本身
不过,在有了'__init__'方法之后,在创建实例的时候,就不能传入空参数或多个参数,必须传入和'__init__'方法相匹配的参数,但是'self'参数不需要传,Python解释器会自己把'实例变量'传入到self

(4)和普通函数相比,在类中定义的函数只有一点不同,那就是函数的第一个参数永远都是self,并且在调用该函数时,不用传递参数,除此之外,和普通函数没有其他区别,仍然可以使用默认参数、可变参数、关键字参数、命名关键字参数

三、特性之一——数据封装

  • 面向对面编程的一个重要的特点就是数据封装,在上面的Student类中,根据对象创建的实例里,都有各自的namescore的数据,可以通过函数来访问这些数据,例如:

    >>> class Student(object):
    ...     def __init__(self,name,score):
    ...             self.name = name
    ...             self.score = score
    ... 
    >>> zhangsan = Student('zhangsan',98) 
    >>> zhangsan.name
    'zhangsan'
    >>> zhangsan.score
    98
    >>> def print_score(std):
    ...     return '%s : %s' % (std.name,std.score) 
    ... 
    >>> print_score(zhangsan) 
    'zhangsan : 98'
    
  • 但是,既然Student实例本身就拥有这些数据,要访问这些数据,就没有必要从外面的函数去访问,可以直接在Student类的内部定义访问数据的函数,这样,就可以把数据封装起来了,这些封装数据的函数和Student类本身是关联起来的,我们称之为类的方法,更改后可以这样写:

    >>> class Student(object):
    ...     def __init__(self,name,score):
    ...             self.name = name
    ...             self.score = score
    ...     def print_score(self):
    ...             return '%s : %s' % (self.name,self.score) 
    ... 
    >>> zhangsan = Student('zhangsan',98) 
    >>> zhangsan.print_score()           
    'zhangsan : 98'
    
    可以发现,'zhangsan'可以直接引用'Student'类中的'print_score'函数
    同样的'print_score'函数的参数也是'self',也是不用传递的,直接在实例变量上调用即可
    
    如果有第三个参数age,但是在__init__中并没有定义,可以这样写:
    # -*- coding: utf-8 -*-
    class Student(object):
        
        def __init__(self,name,score):
            self.name = name
            self.score = score
    
        def print_score(self):
            return print('%s %s %s' % (self.name,self.score,self.age))
    
    zhangsan = Student('zhangsan',98)
    zhangsan.age = 22  #定义实例变量age
    zhangsan.print_score()        
    
    #输出:
    zhangsan 98 22
    
  • 从上面可以看出,在根据Student类创建实例时,只需要指定namescore的值即可,关于如何打印出来,这些都是在Student类的内部定义的,从而使这些数据和逻辑被封装起来,调用时并不知道内部的细节

  • 封装的另一个好处就是可以给Student类增加新的方法,例如:

# -*- coding: utf-8 -*-
class Student(object):
    
    def __init__(self,name,score):
        self.name = name
        self.score = score

    def print_score(self):
        return print('%s : %s' % (self.name,self.score))

    def get_grade(self):
        if self.score >= 90 and self.score <= 100:
            return print('A')
        elif self.score >= 80:
            return print('B')
        else:
            return print('C')

zhangsan = Student('zhangsan',98)
zhangsan.print_score()    
zhangsan.get_grade()    

#输出:
zhangsan : 98
A
  • 总结:

    1. 类是创建实例的模板,而实例是一个个具体的对象,每个实例拥有的数据都是相互独立的,互不影响

    2. 方法就是与实例绑定的函数,和普通函数不同,方法可以直接访问实例的数据

    3. 通过在实例上调用方法,其实就是直接操作了对象内部的数据,并且无需指定方法内部的实现细节

    4. 和静态语音不同,Python允许对实例变量绑定任何数据,这样的效果就是,就算是根据同一类创建出的实例,实例拥有的变量名称可能都是不一样的,例如:

      # -*- coding: utf-8 -*-
      class Student(object):
          
          def __init__(self,name,score):
              self.name = name
              self.score = score
      
      
      zhangsan = Student('zhangsan',98)
      lisi = Student('lisi',98)
      zhangsan.age =  22
      lisi.aaa = 333
      print(zhangsan.age)
      print(lisi.aaa)
      print(zhangsan.aaa)
      #输出
      22
      333
      Traceback (most recent call last):
        File "d:\\工作\\work\\py\\test02.py", line 15, in <module>
          print(zhangsan.aaa)
      AttributeError: 'Student' object has no attribute 'aaa'
          
          
      因为可以绑定任何数据,所有说'zhangsan'拥有name,score,age三个变量,而'lisi'则拥有name,score,aaa三个变量,因为是实例是相互独立的,所有'zhangsan''lisi'之间的变量数量、变量名等都不是互通的,所以zhangsan在最后调用aaa变量时报错了
      

四、访问限制

  • 类(class)中,可以有属性和方法,而外部代码可以通过调用实例变量的方法来操作或获取数据,从而隐藏了内部的复杂逻辑

  • 但是,从上面Student类的定义来看,外部代码可以随意改变一个实例的属性,例如:

    >>> class Student(object):
    ...     def __init__(self,name,score):
    ...             self.name = name
    ...             self.score = score
    ...     def print_score(self):
    ...             return print('%s : %s' % (self.name,self.score)) 
    ... 
    >>> zhangsan = Student('zhangsan',98) 
    >>> zhangsan.print_score()
    zhangsan : 98
    >>> zhangsan.score = 22   #修改score的值
    >>> zhangsan.print_score()           #再次调用,发现值已经变了
    zhangsan : 22
    
  • 可以看到实例属性的值是可以随意修改的,如果想要实例的内部属性不被外部修改,可以这样做:

    #可以在属性的名称前面加"__",在Python中,实例的变量名如果以__开头的话,那么这个变量就成了私有变量,只有内部可以访问,外部无法访问
    >>> class Student(object):
    ...     def __init__(self,name,score):
    ...             self.__name = name
    ...             self.__score = score
    ...     def print_score(self):
    ...             return print('%s : %s' % (self.__name,self.__score)) 
    ... 
    >>> zhangsan = Student('zhangsan',98)
    >>> zhangsan.print_score()
    zhangsan : 98
        
    #现在想对Student类中的score属性的值进行修改,再调用print_score方法时,发现并没有修改,最后发现,其实这相当于是新创建了一个score属性
    >>> zhangsan.score = 22   
    >>> zhangsan.print_score()   
    zhangsan : 98
    >>> zhangsan.score
    22
    
    #上面不行的话,有的人可能会说是因为属性名称不一样,那么现在来调用一下__score属性,发现也无法调用,这是因为在使用私有变量后,Python解释器就把__name的对外名称变成了_Student__score,使用这样的格式进行调用,是可以调用成功的,不过强烈建议不要使用这种方法进行修改、调用属性数据
    >>> zhangsan.__score 
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'Student' object has no attribute '__score'. Did you mean: 'score'?
    >>> zhangsan._Student__score 
    98
    
    #在不使用_Student__score这种格式去访问、修改指定的属性时,可以修改一下类,例如
    >>> class Student(object):
    ...     def __init__(self,name,score):
    ...             self.__name = name
    ...             self.__score = score
    ...     def print_score(self):
    ...             return print('%s : %s' % (self.__name,self.__score))
    ...     def set_name(self,name):
    ...             self.__name = name
    ...     def set_score(self,score):
    ...             self.__score = score
    ...     def get_name(self):
    ...             return print(self.__name) 
    ...     def get_score(self):
    ...             return print(self.__score) 
    ... 
    >>> zhangsan = Student('zhangsan',98) 
    >>> zhangsan.print_score()
    zhangsan : 98
    >>> zhangsan.get_name()    
    zhangsan
    >>> zhangsan.set_name('lisi') 
    >>> zhangsan.get_name()       
    lisi
    
    #虽然原先的"zhangsan.name = 98"也可以之间进行修改,但是通过在类中添加方法可以进行参数控制,例如:
    >>> class Student(object):
    ...     def __init__(self,name,score):
    ...             self.__name = name
    ...             self.__score = score
    ...     def set_score(self,score):        
    ...             if score >= 90 and score <=100:
    ...                     self.__score = score
    ...             else:
    ...                     return print('error')
    ...     def get_score(self):
    ...             return self.__score
    ... 
    >>> zhangsan = Student('zhangsan',98) 
    >>> zhangsan.get_score()  
    98
    >>> zhangsan.set_score(80) 
    error
    >>> zhangsan.get_score()   
    98
    >>> zhangsan.set_score(96) 
    >>> zhangsan.get_score()   
    96
    
    可以看到在'Student'类中,'set_score'方法添加了参数控制
    
  • 注意:

    1. 在Python中,变量名称类似于__xx__这样的以双下划线开头和结尾的是特殊变量,特殊变量是可以直接访问的,并不是私有变量
    2. 以一个下划线开头的,类似于_name这样的变量,是可以被外部访问的,但是在看到这样的变量时,要把它当成私有变量,不要随意访问,这也是一个不成文的规定
    3. 根据拥有私有变量的类去创建的实例,在调用时,不要直接使用类似于zhangsan.__score 这样的,因为虽然在类中定义的是__score,但其实Python解释器对外的名称是_Student__score,所以直接调用或修改zhangsan.__score,其实是调用的是另外一个变量

五、特性之二、三——继承和多态

-继承

  • 在上面的内容中,有说到过继承这一概念,例如:

    class Student(object)
    
  • 在这里Student(object)object就是Student继承的类,也就是说,我们在定义类时,是可以继承现有的类的,创建的新类叫做子类(subclass),而被继承的类叫做父类,也可以叫做基类、超类

  • 下面来看几个案例:

    - 定义一个'Animal'类,添加一个'run'方法
    >>> class Animal(object):
    ...     def run(self):
    ...             return print('Animal is running!!!')
    ... 
    
    - 定义'Dog''Cat'类,两个类都继承'Animal'>>> class Dog(Animal):
    ...     pass
    ... 
    >>> class Cat(Animal):
    ...     passpython面向对象编程(代码片段)
    

    文章目录一、什么是面向对象编程二、类(class)和实例(instance)三、特性之一——数据封装四、访问限制五、特性之二、三——继承和多态-继承-多态六、获取对象信息-Type()-isinstance()-dir()七、实例属性和类属性此文参考廖雪峰官网... 查看详情

    python面向对象编程进阶(代码片段)

    前言上上篇我们讲解了Python面向对象编程的基础知识,《Python基础篇:面向对象怎样炼成的》这篇我们继续来讨论面向对象编程相关的内容。目录前言可见性和属性装饰器动态属性静态方法和类方法继承和多态总结可见... 查看详情

    python面向对象编程基础(代码片段)

    活在当下的程序员应该都听过“面向对象编程”一词,也经常有人问能不能用一句话解释下什么是“面向对象编程”,我们先来看看比较正式的说法。把一组数据结构和处理它们的方法组成对象(object),把相同行... 查看详情

    python面向对象编程进阶(代码片段)

    python面向对象编程进阶 一.isinstance(obj,cls)和issubclass(sub,super)isinstance(obj,cls)检查是否obj是否是类cls的对象1classFoo(object):2pass34obj=Foo()56isinstance(obj,Foo)issubclass(sub,super)检查sub类是否是super类的派生类1class 查看详情

    python学习-面向对象1(代码片段)

    参考:http://www.cnblogs.com/wupeiqi/概述python的编程方式分为三种面向过程:根据业务的逻辑,从上到下一行一行的编写代码函数式:将某些功能封装在函数里,需要调用时只需要调用函数面向对象:对函数进行再次封装与分类,更... 查看详情

    进阶学python:python面向对象基础!(代码片段)

    什么是编程范式?在了解面向对象编程之前,我们需要了解三大编程范式以及其之间的区别和利弊即:面向过程编程、函数式编程、面向对象编程。编程:是程序员用特定的语法+数据结构+算法组成的代码来... 查看详情

    第八篇python面向对象编程(代码片段)

    11面向对象编程面向对象编程——ObjectOrientedProgramming,简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。面向过程的程序设计把计算机程序视为一系列的命令集合... 查看详情

    python入门-6面向对象编程:07面向对象三大特征(封装继承多态)-继承(代码片段)

    一:面向对象三大特征介绍  Python是面向对象的语言,也支持面向对象编程的三大特性:继承、封装(隐藏)、多态。  封装(隐藏)    隐藏对象的属性和实现细节,只对外提供必要的方法。相当于将“细节封装... 查看详情

    python面向对象基础和高级复习(代码片段)

    ...列相同属性和方法的集合体现实世界中先有对象后有类,python中先有类,再实例化出对象对象的属性的查找顺序先对象本身-->类-->父类-->父类 查看详情

    从0开始的python学习014面向对象编程(代码片段)

     简介到目前为止,我们的编程都是根据数据的函数和语句块来设计的,面向过程的编程。还有一种我们将数据和功能结合起来使用对象的形式,使用它里面的数据和方法这种方法叫做面向对象的编程。类和对象是面向对象编... 查看详情

    python:函数与面向对象编程总结(代码片段)

    文章目录函数函数的定义函数的参数用模块管理函数高阶函数Lambda函数递归调用例题例题1:汉诺塔问题例题2:爬楼梯例题3:冒泡排序例题4:编写实现查找元素的函数例题5:判断快乐数面向对象编程面向对象... 查看详情

    03python面向对象编程5(代码片段)

    ...向对象的三大特征之一,也是实现软件复用的重要手段。Python的继承是多继承机制,即一个子类可以同时有多个直接父类。Python子类继承父类的语法是在定义子类时,将多个父类放在子类之后的圆括号里。语法格式如下:In [... 查看详情

    python面向对象编程_类(代码片段)

    ...多态4.1封装4.2继承五、super()函数⭐其实面向对象是初学Python时已经了解了,现在又有些新的感悟,将其总结在此,有新的体会会继续总结的!面向对象与过程一、类和实例类:英文名字Class,有“类别"&... 查看详情

    python编程基础(代码片段)

    Python编程基础(三)打卡第三天啦!!!面向对象基础(上)面向对象介绍OOP将数据与函数绑定在一起,进行封装面向过程和面向对象类和对象类是模板,由类名、属性、方法构成对象是根据模... 查看详情

    python编程基础(代码片段)

    Python编程基础(三)打卡第三天啦!!!面向对象基础(上)面向对象介绍OOP将数据与函数绑定在一起,进行封装面向过程和面向对象类和对象类是模板,由类名、属性、方法构成对象是根据模... 查看详情

    python学习笔记之面向对象(代码片段)

    一、面向过程VS面向对象 编程范式   编程是程序员用特定的语法+数据结构+算法组成的代码来告诉计算机如何执行任务的过程,一个程序是程序员为了得到一个任务结果而编写的一组指令的集合,正所谓条条大路... 查看详情

    python基础篇:面向对象怎样炼成的(代码片段)

    前言大家好,我是辣条哥今天好好给大家分析一下面向对象编程,面向对象编程是一种非常流行的“编程范式”(programmingparadigm),所谓编程范式就是“程序设计的方法论”,简单的说就是程序员对程序... 查看详情

    03python面向对象编程1(代码片段)

    1.创建和使用类1.1创建Dog类。根据Dog类创建的每个实例都将存储名字和年龄。我们赋予了每条小狗蹲下(sit())和打滚(roll_over())的能力:In [2]:classDog():"""Asimpleattempttomodeladog."""def__init__(self,name,age):"""Initializenameandageattributes."... 查看详情