即使对于没有任何编程经验但具有良好数学背景的人来说,函数也很容易理解。另一方面,类似乎更难掌握。
假设我想创建一个类/函数,根据他/她的生日年份和当前年份计算一个人的年龄。我应该为此创建一个类还是一个函数?还是选择取决于场景?
PS我正在研究Python,但我想这个问题是通用的。
创建一个函数。函数做特定的事情,类是特定的事情。
类通常具有方法,这些方法是与特定类相关联的函数,并执行与该类的事物相关联的事情 - 但如果您只想做某事,那么您只需要一个函数。
本质上,类是将函数(作为方法)和数据(作为属性)分组为围绕某种事物的逻辑单元的一种方式。如果您不需要该分组,则无需创建课程。
就像Amber 在她的回答中所说的:创建一个函数。实际上,如果您有以下情况,则不必上课:
class Person(object):
def __init__(self, arg1, arg2):
self.arg1 = arg1
self.arg2 = arg2
def compute(self, other):
""" Example of bad class design, don't care about the result """
return self.arg1 + self.arg2 % other
在这里,您只需将一个函数封装在一个类中。这只会降低代码的可读性和效率。实际上函数compute
可以这样写:
def compute(arg1, arg2, other):
return arg1 + arg2 % other
只有当你有超过 1 个函数并且保持内部状态(带有属性)有意义时,你才应该使用类。.py
否则,如果您想重新组合功能,只需在新文件中创建一个模块。
你可能会看这个视频(Youtube,大约 30 分钟),它解释了我的观点。Jack Diederich 说明了为什么类在这种情况下是邪恶的,以及为什么它的设计如此糟糕,尤其是在 API 等方面。
这是一个相当长的视频,但它是必须看到的。
我知道这是一个有争议的话题,我现在可能会被烧死。但这是我的想法。
就我自己而言,我认为最好尽可能长时间地避免上课。如果我需要一个复杂的数据类型,我会使用简单的结构(C/C++)、字典(python)、JSON(js)或类似的,即没有构造函数、没有类方法、没有运算符重载、没有继承等。使用类时,您可能会被 OOP 本身(什么设计模式,什么应该是私有的,等等)迷住了,而将注意力集中在您想要编码的基本内容上。
如果您的项目变得庞大而混乱,那么 OOP 开始变得有意义,因为需要某种直升机视图系统架构。“功能与类”也取决于您面前的任务。
类(或更确切地说是它们的实例)用于表示事物。类用于定义特定类对象(其实例)支持的操作。如果您的应用程序需要跟踪人员,那么Person
可能是一个类;此类的实例代表您正在跟踪的特定人。
函数用于计算事物。它们接收输入并产生输出和/或产生影响。
类和函数并不是真正的替代品,因为它们的用途不同。考虑创建一个类来“根据他/她的生日年份和当前年份计算一个人的年龄”真的没有意义。您可能有也可能没有类来表示Person
、Age
、Year
和/或的任何概念Birthday
。但即使Age
是一个班级,也不应该被认为是计算一个人的年龄;相反,计算一个人的年龄会产生一个类的实例Age
。
如果你在你的应用程序中为人建模并且你有一个类,那么将年龄计算作为类的一种方法Person
可能是有意义的。方法基本上是定义为类的一部分的函数;这就是我前面提到的“定义特定类对象支持的操作”的方式。Person
因此,您可以在 person 类上创建一个方法来计算人的年龄(它可能会从 person 对象中检索生日年份并接收当前年份作为参数)。但是计算仍然是由一个函数完成的(只是一个恰好是类上的方法的函数)。
或者您可以简单地创建一个接收参数的独立函数(可以从中检索出生年份的人员对象,或者只是出生年份本身)。正如您所注意到的,如果您还没有这个方法自然属于的类,这会简单得多!您永远不应该仅仅为了进行操作而创建一个类;如果这就是类的全部内容,那么该操作应该只是一个独立的功能。
这取决于场景。如果您只想计算一个人的年龄,请使用函数,因为您想实现单个特定行为。
但是,如果您想创建一个对象,其中包含一个人的出生日期(可能还有其他数据),并允许对其进行修改,那么计算年龄可能是与该人相关的许多操作之一,并且明智的做法是改用一个类。
类提供了一种将一些数据和相关操作合并在一起的方法。如果您对数据只有一个操作,那么使用函数并将数据作为参数传递,您将获得等效的行为,并且代码不太复杂。
请注意,此类:
class A(object):
def __init__(self, ...):
#initialize
def a_single_method(self, ...):
#do stuff
不是一个真正的类,它只是一个(复杂的)函数。一个合法的类应该总是至少有两个方法(不计算__init__
)。
Before answering your question:
If you do not have a Person
class, first you must consider whether you want to create a Person
class. Do you plan to reuse the concept of a Person
very often? If so, you should create a Person
class. (You have access to this data in the form of a passed-in variable and you don't care about being messy and sloppy.)
To answer your question:
You have access to their birthyear, so in that case you likely have a Person
class with a someperson.birthdate
field. In that case, you have to ask yourself, is someperson.age
a value that is reusable?
The answer is yes. We often care about age more than the birthdate, so if the birthdate is a field, age should definitely be a derived field. (A case where we would not do this: if we were calculating values like someperson.chanceIsFemale
or someperson.positionToDisplayInGrid
or other irrelevant values, we would not extend the Person
class; you just ask yourself, "Would another program care about the fields I am thinking of extending the class with?" The answer to that question will determine if you extend the original class, or make a function (or your own class like PersonAnalysisData
or something).)
我将在这个问题上脱颖而出 (7年后编辑:我不再是这个问题的唯一声音,有一个完整的编码运动可以做到这一点,称为“函数式编程”)并提供一个替代方案观点看法:
编辑:研究一再表明,类是一种过时的编程方法。几乎所有关于该主题的研究论文都与函数式编程而不是面向对象编程有关。
对类的依赖很容易导致编码人员创建臃肿而缓慢的代码。传递的类(因为它们是对象)比调用函数并传递一两个字符串需要更多的计算能力。函数的正确命名约定几乎可以完成创建类可以做的所有事情,并且只需要一小部分开销和更好的代码可读性。
这并不意味着你不应该学习理解类。如果你和其他人一起编码,人们会一直使用它们,你需要知道如何处理这些类。编写依赖函数的代码意味着代码将更小、更快、更易读。我见过只使用快速而快速的函数编写的大型网站,也见过一些功能极少的小型网站,这些网站严重依赖类并且经常中断。(当您的类扩展包含作为其类的一部分的类时,您知道您已经失去了所有易于维护的外表。)
归根结底,您要传递的所有数据都可以通过现有数据类型轻松处理。
类是作为精神支柱而创建的,并没有提供实际的额外功能,从长远来看,它们倾向于创建的过于复杂的代码会破坏该拐杖的意义。
编辑:7 年后更新...... 最近,编码方面的新运动一直在验证我提出的这一点。它是用函数式编程取代面向对象编程 (OOP) 的运动,它基于 OOP 的许多这些确切问题。有很多研究论文展示了函数式编程相对于面向对象编程的好处。除了我提到的几点之外,它还使重用代码变得更加容易,使错误修复和单元测试更快更容易。老实说,有很多好处,使用 OOP 而不是功能的唯一原因是与尚未更新的遗留代码的兼容性。
永远不要创建类。至少正在讨论 Python 中的 OOP 类。
考虑这个简单的类:
class Person(object):
def __init__(self, id, name, city, account_balance):
self.id = id
self.name = name
self.city = city
self.account_balance = account_balance
def adjust_balance(self, offset):
self.account_balance += offset
if __name__ == "__main__":
p = Person(123, "bob", "boston", 100.0)
p.adjust_balance(50.0)
print("done!: {}".format(p.__dict__))
vs 这个 namedtuple 版本:
from collections import namedtuple
Person = namedtuple("Person", ["id", "name", "city", "account_balance"])
def adjust_balance(person, offset):
return person._replace(account_balance=person.account_balance + offset)
if __name__ == "__main__":
p = Person(123, "bob", "boston", 100.0)
p = adjust_balance(p, 50.0)
print("done!: {}".format(p))
namedtuple 方法更好,因为:
inheritance
增加了复杂性,并隐藏了复杂性。我看不到使用 OOP 类的单一优势。显然,如果您习惯于 OOP,或者您必须与需要类的代码(如 Django)进行交互。
顺便说一句,大多数其他语言都有一些记录类型功能,比如命名元组。例如,Scala 就有案例类。这个逻辑同样适用于那里。