前两天一位已经学习python一段时间的小伙伴问了这样一个问题:虽然已经使用python一年多了,也用python写过很多脚本,代码量从几十行到上千行的也有,但是从未使用过类(class),似乎用函数(def)就能解决所有问题,使用类有什么好处?到底什么时候该用类呢?

关于这个问题,算是困惑了许多刚接触python的同学,那么本文就尝试从多个角度来解读这个问题。首先还是先来看看官方给出类与函数的解释。

类提供了一种组合数据和功能的方法。 创建一个新类意味着创建一个新的对象类型,从而允许创建一个该类型的新实例 。 每个类的实例可以拥有保存自己状态的属性。 一个类的实例也可以有改变自己状态的(定义在类中的)方法。函数的本质就是一段有特定功能、可以重复使用的代码,这段代码已经被提前编写好了,并且为其起一个“好听”的名字。在后续编写程序过程中,如果需要同样的功能,直接通过起好的名字就可以调用这段代码。很显然,这样的答案并没有让人搞明白类和函数到底不一样在哪里。但是里面提到了类是创建一个对象,所以类是面向对象程序设计(Object Oriented Programming)。也就是我们常说的OOP。而OOP高度关注的是代码的组织,可重用性和封装。

几个例子

上面的官方解释上去还是很抽象,那么我们开始说人话。简单来说当Python中没有可以完全表达我们要表示的内容的数据类型时,那么就需要使用一个类。来看下面的例子。如果我正在计算某人的年龄,则只需使用int 因为它可以满足我的需求。如果我们需要在游戏中表示像敌人之类的东西,则可以创建一个类则可以创建一个类Enemy,其中包含诸如health和armor的数据,并包含诸如fire_weapon射击时的功能。然后,我们还可以创建另一个类FlyingEnemy,Enemy该类从该类继承所有内容,但又具有一个fly方法,因此具有其他功能。

我们再来看一个例子。假设我们需要编写一个音乐播放器。在这个播放器中,我们有关于不同类型数据的信息,如歌曲、专辑、艺术家和播放列表。还有一些可以播放歌曲、播放专辑、播放艺术家或播放播放列表的功能。我们将每种数据存储在字典中,不同类型的数据有不同的字段名,因为每个play函数需要做不同的事情,所以我们就有四个不同的函数:

some_song = {

"title": "Yellow Submarine",

"artist": the_beatles, # 指向到包含该艺术家的词典

"album": yellow_submarine_album, # 指向包含此相册的dict的链接

"duration": insert_time_object_here,

"filepath": "path/to/file/on/disk"

}

# 其他数据类型的结构也类似

# 一些函数

def play_song(song):

# 获取歌的路径

path = song["filepath"]

# 播放路径

call_some_library_function(path)

def play_album(album):

# 找到专辑里所有的歌曲

# 分别调用play_song

def play_artist(artist):

# 找到这位艺术家所有的专辑

# 分别调用play_album

def play_playlist(playlist):

# 找到播放列表中的所有歌曲

# 分别调用play_song

这样写有什么不好?我们有四个非常相似的函数,每个函数都与特定类型的数据相关。你必须把它们叫做不同的东西,而不仅仅是play,你必须确保你把正确的数据传递给它们。虽然这四种不同的类型都可以“播放”,但是没有一种通用的方法可以在不知道它是什么的情况下播放任何东西。那么在OOP下,怎么实现呢:

class Song:

def __init__(self, title, artist, album, duration, filepath):

self.title = title

self.artist = artist

self.album = album

self.duration = duration

self.filepath = filepath

def play(self):

path = self.filepath

call_some_library_function(path)

这样就定义了如何创建一个新的Song对象。该方法将字段值作为参数,并将它们作为对象的属性赋值。self是一个特殊参数(名称不保留;它可以被称为任何东西),它是对对象本身的引用。是一种从同一对象的其他方法内部访问属性和方法的方法。当我们从对象外部访问它们时(要使用play方法时将执行此操作),则可以使用在该范围内为对象指定的任何名称。

那么在之前:

# some_song是上面定义的歌

play_song(some_song)

在使用class之后:

# self参数没有在这里传递;它会自动添加

some_song = Song("Yellow Submarine",

the_beatles,

yellow_submarine_album,

insert_time_object_here,

"path/to/file/on/disk"

)

some_song.play()

为什么这样更好?如果我们有一个对象,则不必知道它是什么就可以播放,因为现在播放任何内容的语法都是相同的:anyobject.play()即对象“知道”如何使用“自己的”数据进行处理的设计思想。无需从外部检查对象是否具有某些字段并决定如何处理这些内部字段,而是调用play对象提供的方法,并在每个类内部定义该类型的对象应如何实现此功能。我们继续看下面两段代码来实现输出一些学生的成绩,首先是使用类:

class Student(object):

def __init__(self, name, age, gender, level, grades=None):

self.name = name

self.age = age

self.gender = gender

self.level = level

self.grades = grades or {}

def setGrade(self, course, grade):

self.grades[course] = grade

def getGrade(self, course):

return self.grades[course]

def getGPA(self):

return sum(self.grades.values())/len(self.grades)

# 定义一些学生

john = Student("John", 12, "male", 6, {"math":3.3})

jane = Student("Jane", 12, "female", 6, {"math":3.5})

# 现在我们可以很容易地得到分数

print(john.getGPA())

print(jane.getGPA())

再来看看用函数怎么实现

def calculateGPA(gradeDict):

return sum(gradeDict.values())/len(gradeDict)

students = {}

name, age, gender, level, grades = "name", "age", "gender", "level", "grades"

john, jane = "john", "jane"

math = "math"

students[john] = {}

students[john][age] = 12

students[john][gender] = "male"

students[john][level] = 6

students[john][grades] = {math:3.3}

students[jane] = {}

students[jane][age] = 12

students[jane][gender] = "female"

students[jane][level] = 6

students[jane][grades] = {math:3.5}

print(calculateGPA(students[john][grades]))

print(calculateGPA(students[jane][grades]))

这两段代码都实现了输出学生的成绩,但是在使用函数的时候,我们需要记住学生是谁,成绩存储在哪里,似乎不是很困难(如果需要输出的学生更多呢),但是OOP避免了这一点。并且代码也更加pythonic。

结束语

最后,让我们回到刚开始的问题上来,上面说了这么多类的好处所以我们就应该更多的去使用类吗?并不是!

其实从某种意义上来说,类并不比函数更好。只是在某些情况下使用类能够更好的帮助我们写代码。所以如果发现自己使用各种数据集调用some_function(data),那么将其用类表示为data.some_function()可能提高我们的效率。至于到底在何时使用类,我们来看看其他程序员的理解当我们拥有一堆共享状态的函数,或者将相同的参数传递给每个函数时,我们可以重新考虑代码使用类。

类的“可重用性”意味着我们可以在其他应用程序中重用之前的代码。如果我们在自己的文件中编写了类,则只需将其放在另一个项目中即可使其工作。

函数对于小型项目非常有用,但是一旦项目开始变大,仅使用函数就可能变得混乱。类是组织和简化代码的一种非常好的方法

通常,如果在函数内部找到自写函数,则应考虑编写类。如果我们在一个类中只有一个函数,那么请坚持只写一个函数。

如果需要在函数调用之间保留一些状态,那么最好使用带有该函数的类作为方法

python什么时候用函数和类_【Python】一文说清楚类与函数的选择相关推荐

  1. python可变类型与不可变类型作为函数参数区别_不要用可变类型对象做函数默认参数...

    不要用可变类型对象做函数默认参数 1. 可变对象做默认参数 内置数据类型int,float,bool,str,tuple 是不可变对象, 字典,集合,列表是可变对象. 在定义python函数时,千万不 ...

  2. python实验指导书答案函数代码复用_#Python学习笔记(5)函数和代码重用,复用

    七段数码管绘制 绘制单段数码管 def drawLine(draw): turtle.pendown() if draw else turtle.penup() #将绘制一条线定义为一个基本函数, 参 ...

  3. 小甲鱼python课后题和答案简书_【py小甲鱼笔记】-函数

    课后作业: 1. 请问这个函数有多少个参数? def MyFun((x, y), (a, b)): return x * y - a * b 如果你回答两个,那么恭喜你错啦,答案是0,因为类似于这样的 ...

  4. python田字格函数简化代码_[Python]使用生成器来简化代码

    原本只是大概知道生成器是什么,但一直不知道怎么用,或是什么情景下用,后来才发现: 在需要一边读数据一边处理任务时,如果直接为每个任务都写一个函数,那么读数据的部分就要在每个函数都重复一遍 直接将所有任 ...

  5. java友元类_友元实例:友元类及友元函数

    学习了c++这么久,一直没有对友元进行了解,据说友元不是特别好用(据说,不是我说的),因此直到今天才去了解.其实友元确实不是很常用,但友元功能确实很实用,它不但能够释放类中的非公有成员,同时还能保证了 ...

  6. java 元类_[译]什么是元类metaclass?

    类即对象 在理解元类之前,需要先掌握Python中的类,Python中类的概念与SmallTalk中类的概念相似. 在大多数语言中,类是用来描述如何创建对象的代码段,这在Python中也是成立的: & ...

  7. java comparator接口类_常见的接口与类 -- Comparator

    接口Comparator 1.1 前面我们讲过Java提供了一个用于比较的接口Comparable,提供了一个比较的方法,所有实现该接口的类,都动态的实现了该比较方法.实际上Java中除了比较一个接口 ...

  8. sql自定义函数学习思路_学习SQL:用户定义的函数

    sql自定义函数学习思路 You can create several user-defined objects in a database. One of these is definitely u ...

  9. 二元函数matlab画图_噶米matlab实验五二元函数的图形.docx

    实验五二元函数的图形 [实验目的] 1.了解二元函数图形的绘制. 2.了解空间曲面等高线的绘制. 3.了解多元函数插值的方法. 4.学习.掌握 MATLAB 软件有关的命令. [实验内容] 画出函数 ...

  10. evaluate函数使用无效_[Python实战]使用栈实现简易计算器

    我们这次实现的命令行计算器,支持加减乘除.括号.浮点数.负数,以及查看历史和退出功能. 主要的思路:read - parse - print - loop. read 阶段是指读取用户在提示符(cal ...

最新文章

  1. 60分钟看懂HMM的基本原理
  2. 【转载】SAP Retail寄售门店关键配置
  3. 从输入 URL 到页面加载完的过程中都发生了什么事情 —— 网络优化篇
  4. 【错误记录】Google Play 上架报错 ( 我们检测到您的应用程序包含未经认证的广告SDK或未经批准用于儿童导向服务的SDK )
  5. VTK修炼之道35:边缘检测_Laplace算子
  6. 计算机网络思科实验,思科综合实验
  7. 单元测试02:Open-Cover安装与使用
  8. 图像目标分割_3 SegNet + U-Net
  9. 搞不懂,为啥现在什么公司都在考算法???
  10. 关于Paxos 幽灵复现问题的看法
  11. 消息传递的图神经网络
  12. ZooKeeper 3.5.5 基础使用
  13. LeetCode(169)——求众数(JavaScript)
  14. 1024福利|硬核无Bug,码上有红包!
  15. 西门子SITOP电源维修6EP1336-3BA00冗余电源模块维修概述
  16. 工具类--生成随机姓名及指定长度随机字符串
  17. [ISA]部署ISA防火墙策略的十六条守则
  18. php agrs zf2,添加ZF2_PATH environment variable 环境变量
  19. MySQL条件查询IN和NOT IN左右两侧包含NULL值的处理方式
  20. 海康摄像头之FTP服务器搭建及使用

热门文章

  1. 基于卷积神经网络的不良地质体识别与分类
  2. Win11的两个实用技巧系列之玩游戏闪跳、错误代码0x80004005解决
  3. 浙江省第四届“知识产权杯”创意设计大赛
  4. 微信小程序:恋爱话术,土味情话、小程序源码
  5. 微信智能硬件应用——微信插座控制
  6. window 查看文件大小工具(TreeSize)
  7. 分享一个好用美观的桌面便签工具
  8. 中科点击:大数据应用的六种商业模式
  9. 财务系统开发-知识储备
  10. 【三节课数据分析】ch3.数据分析的九种方法