即使对于没有任何编程经验但具有一定数学背景的人,函数也易于理解。另一方面,类似乎更难掌握。
假设我要创建一个类/函数,计算给定生日和当前年份的人的年龄。我应该为此创建类还是函数?还是选择取决于方案?
PS我正在使用Python,但是我想这个问题很普遍。
即使对于没有任何编程经验但具有一定数学背景的人,函数也易于理解。另一方面,类似乎更难掌握。
假设我要创建一个类/函数,计算给定生日和当前年份的人的年龄。我应该为此创建类还是函数?还是选择取决于方案?
PS我正在使用Python,但是我想这个问题很普遍。
Answers:
创建一个函数。函数做特定的事情,类做特定的事情。
类通常具有方法,这些方法是与特定类相关联的函数,并且可以执行与该类所具有的事物相关联的操作-但是,如果您只想做某事,那么功能就是您所需要的。
本质上,类是一种将功能(作为方法)和数据(作为属性)分组为围绕某种事物的逻辑单元的方法。如果您不需要该分组,则无需上课。
class
功能和数据分组。在任何情况下,这样做都没有好处。我们具有将它们组合在一起的功能,数据类型和模块。即使您需要特殊的函数多态性:针对不同数据类型的不同函数实现,也比OOP类有更好的解决方案。考虑最终与语言无关的Haskell样式类型类。
就像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
仅当您拥有多个功能且保持内部状态(带有属性)有意义时,才应使用类。否则,如果要重新组合功能,只需在新.py
文件中创建一个模块。
您可能会看这段视频(Youtube,大约30分钟),这解释了我的观点。Jack Diederich展示了为什么在这种情况下类是邪恶的,以及为什么它是如此糟糕的设计,尤其是在API之类的情况下。
这是一个很长的视频,但这是必看的。
类(或更确切地说,它们的实例)用于表示事物。类用于定义特定对象类(其实例)支持的操作。如果您的应用程序需要跟踪人员,则Person
可能是一类;该类的实例代表您要跟踪的特定人员。
函数用于计算事物。他们接收输入并产生输出和/或产生影响。
类和函数并不是真正的替代品,因为它们不是相同的东西。考虑上一堂课以“根据给定他/她的生日和当前年份来计算一个人的年龄”确实没有任何意义。您可能会或可能不会有类以表示任何的概念Person
,Age
,Year
,和/或Birthday
。但是,即使Age
是一堂课,也不应将其视为计算一个人的年龄;而一个人的年龄的计算结果中的实例Age
类。
如果您在应用程序中对人员建模并且您有一个Person
类,则将年龄计算作为该类的一种方法可能是有意义的Person
。方法本质上是一个定义为类一部分的函数。正如我前面提到的,这就是“定义特定对象类支持的操作”的方式。
因此,您可以在人员类上创建一种用于计算人员年龄的方法(它可能会从人员对象中检索生日,并将当前年份作为参数)。但是计算仍然是由一个函数完成的(只是一个恰好是类上方法的函数)。
或者,您可以简单地创建一个接收参数的独立函数(可以从中检索出生年份的个人对象,也可以只是出生年份本身)。如您所注意到的,如果您还没有该方法自然所属的类,这会简单得多!您绝不应该仅仅为了进行操作而创建一个类。如果仅是该类的全部,那么该操作应该只是一个独立的函数。
Person
为anamedtuple
并编写所需的任何功能。
这取决于场景。如果你只需要计算一个人的年龄,那么既然你想实现一个使用功能单一的特定行为。
但是,如果您要创建一个对象,其中包含一个人的出生日期(可能还有其他数据),并允许对其进行修改,那么计算年龄可能是与该人有关的许多操作之一,使用一个类代替。
类提供了一种将某些数据和相关操作合并在一起的方法。如果您仅对数据执行一项操作,则使用函数并将数据作为参数传递,您将获得等效的行为,而代码却不那么复杂。
请注意此类:
class A(object):
def __init__(self, ...):
#initialize
def a_single_method(self, ...):
#do stuff
实际上不是一个类,它只是一个(复杂的)函数。一个合法的类应该总是至少有两个方法(不计数__init__
)。
我知道这是一个有争议的话题,现在我很可能会被淘汰。但是这是我的想法。
对于我自己,我认为最好是尽可能避免上课。如果我需要复杂的数据类型,则可以使用简单的struct(C / C ++),dict(python),JSON(js)或类似的方法,即没有构造函数,没有类方法,没有运算符重载,没有继承等。使用类时,您会被OOP本身所迷惑(什么设计模式,什么应该是私有的,等等),而将注意力集中在您想首先编写的基本内容上。
如果您的项目变得又大又混乱,那么OOP就变得有意义了,因为需要某种直升机视图系统架构。“功能与类别”还取决于您要完成的任务。
在回答您的问题之前:
如果您没有Person
课程,那么首先必须考虑是否要创建Person
课程。您打算经常重用概念Person
吗?如果是这样,您应该创建一个Person
类。(您可以通过传入变量的形式访问此数据,而不必担心凌乱和草率。)
要回答您的问题:
您可以访问他们的出生年份,因此在这种情况下,您可能要上一Person
堂课someperson.birthdate
。在这种情况下,您必须问自己,someperson.age
一个可重用的值吗?
答案是肯定的。我们通常比出生日期更关心年龄,因此,如果出生日期是一个字段,则年龄绝对应该是派生字段。(在这种情况下,我们不会这样做:如果我们正在计算someperson.chanceIsFemale
或someperson.positionToDisplayInGrid
或其他不相关的值,我们将不会扩展Person
该类;您只是问自己:“另一个程序是否会关心我正在考虑使用的类来扩展该类的字段? “”这个问题的答案将决定您是否扩展原始类,或创建一个函数(或您自己的类,诸如此类PersonAnalysisData
)。
我将在这一方面与众不同,并提出另一种观点:
永远不要创建类。
依赖类有一种明显的趋势,导致编码人员创建肿且缓慢的代码。传递类(因为它们是对象)比调用一个函数并传递一两个字符串要多得多。正确的函数命名约定几乎可以完成创建类所能做的所有事情,并且只有一小部分的开销和更好的代码可读性。
但这并不意味着您不应该学习理解课程。如果您与其他人一起编码,人们会一直使用它们,并且您需要知道如何处理这些类。编写代码以依赖函数意味着代码将更小,更快,更易读。我已经看到了巨大的站点,这些站点仅使用快速且灵活的函数编写而成,并且我已经看到了具有最小功能的微型站点,这些函数严重依赖类并且不断崩溃。(当您拥有扩展包含作为其类的一部分的类的类的类时,您知道您已经失去了易于维护的所有外观。)
归根结底,您要传递的所有数据都可以轻松地由现有数据类型处理。
类被创建为精神上的拐杖,并且没有提供任何实际的额外功能,而从长远来看,它们过于复杂的代码倾向于创建该拐杖。
永远不要创建类。至少正在讨论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__))
与这个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之类的代码进行交互。
顺便说一句,大多数其他语言都有一些记录类型功能,例如namedtuples。例如,Scala具有案例类。这种逻辑在那里同样适用。
namedtuple
您所倡导的方法似乎没有提供任何方法封装或数据隐藏(除非我遗漏了一些东西)。真正的OO方法的两个主要优点是:您可以将数据以及对其执行作用的功能打包在一起,并且可以在对象中维护私有状态。如果您不这样做,那么您将只能重塑OOP,并且代码将更难使用,而不是更简单。