类与函数

时间:2013-08-13 07:12:58

标签: class function oop

即使对于没有任何编程经验但具有公平数学背景的人来说,功能也很容易理解。另一方面,课程似乎更难掌握。

假设我想创建一个类/函数来计算给定他/她的生日年和当年的人的年龄。我应该为此创建一个类,还是一个函数? 或者是依赖于场景的选择?

P.S。我正在研究Python,但我想这个问题是通用的。

8 个答案:

答案 0 :(得分:88)

创建一个功能。函数执行特定事物,类特定事物。

类通常有方法,这些方法是与特定类关联的函数,并且执行与类相关的事情 - 但如果你想要的只是做某事,那么你只需要一个函数。 p>

本质上,类是一种将函数(作为方法)和数据(作为属性)分组为围绕某种事物的逻辑单元的方法。如果您不需要分组,则无需上课。

答案 1 :(得分:14)

喜欢Amber says in her answer:创建一个函数。事实上当你必须创建类时,如果你有类似的东西:

class Person(object):
    def __init__(self, arg1, arg2):
        self.arg1 = arg1
        self.arg2 = arg2

    def compute(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文件中创建一个模块。

You might look this video (Youtube, about 30min),这解释了我的观点。杰克·迪德里希(Jack Diederich)展示了为什么在这种情况下课程是邪恶的,以及为什么它是如此糟糕的设计,特别是在像API这样的东西 它很长但是必须看到

答案 2 :(得分:6)

类(或者更确切地说是它们的实例)用于表示事物。类用于定义特定对象类(其实例)支持的操作。如果您的应用程序需要跟踪人员,那么Person可能是一个类;此类的实例代表您正在跟踪的特定人员。

函数用于计算事物。他们接收输入并产生输出和/或产生影响。

类和函数不是真正的替代品,因为它们不是同一个东西。考虑将课程“计算给出他/她的生日年和当年的年龄”是没有意义的。您可能有也可能没有类来表示PersonAgeYear和/或Birthday的任何概念。但即使Age是一个阶级,也不应该将其视为计算一个人的年龄;而是在Age类的实例中计算一个人的年龄结果

如果您正在为应用程序中的人员建模并且您有一个Person类,那么将年龄计算作为Person类的方法可能是有意义的。方法基本上是一个被定义为类的一部分的函数;这就是你如前所述“定义特定类对象支持的操作”的方式。

因此,您可以在您的人员类上创建一个方法来计算人的年龄(它可能会从人物对象中检索生日年并将当前年份作为参数接收)。但是计算仍然由一个函数完成(只是一个碰巧是类上方法的函数)。

或者您可以简单地创建一个独立的函数来接收参数(从中检索出生年份的人物对象,或者仅仅是出生年份本身)。如你所知,如果你还没有这个方法自然属于的类,这就简单多了!你永远不应该创建一个简单的类来进行操作;如果那是 all ,那么该操作应该只是一个独立的函数。

答案 3 :(得分:5)

这取决于场景。如果您计算人的年龄,那么请使用函数,因为您要实现单个特定行为。

但是如果你想创建一个包含一个人的出生日期(可能还有其他数据)的对象,允许修改它,那么计算年龄可能是与该人有关的许多操作之一,它会明智地使用一个班级。

类提供了将一些数据和相关操作合并在一起的方法。如果对数据只有一个操作,那么使用函数并将数据作为参数传递,您将获得等效行为,代码更复杂。

注意一类:

class A(object):
    def __init__(self, ...):
        #initialize
    def a_single_method(self, ...):
        #do stuff

实际上不是一个类,它只是一个(复杂的)函数。合法的类应始终至少有两个方法(不计算__init__)。

答案 4 :(得分:3)

在回答你的问题之前:

如果您没有Person课程,首先必须考虑是否要创建Person课程。您是否计划经常重复使用Person的概念?如果是这样,您应该创建一个Person类。 (您可以通过传入变量的形式访问这些数据,而不必担心它是混乱和草率的。)

回答你的问题:

您可以访问他们的分娩日,因此在这种情况下,您可能会有一个Person类,其中someperson.birthdate字段。在这种情况下,你必须问自己,someperson.age是一个可重用的值吗?

答案是肯定的。我们经常关心年龄超过生日,所以如果生日是一个领域,年龄绝对应该是衍生领域。 (我们不会这样做的情况:如果我们计算someperson.chanceIsFemalesomeperson.positionToDisplayInGrid或其他无关值这样的值,我们就不会扩展Person类;你只要问自己,“另一个程序是否会关心我正在考虑扩展类的字段?“该问题的答案将决定你是扩展原始类,还是创建一个函数(或者你自己的类,如PersonAnalysisData或其他东西)。 )

答案 5 :(得分:3)

我知道这是一个有争议的话题,我现在可能会被烧掉。但这是我的想法。

对于我自己,我认为最好尽可能避免上课。如果我需要一个复杂的数据类型,我使用简单的struct(C / C ++),dict(python),JSON(js)或类似的,即没有构造函数,没有类方法,没有运算符重载,没有继承等。当使用类时,你可以被OOP本身带走(什么设计模式,什么应该是私人的,bla bla),并且首先关注你想要编码的基本内容。

如果您的项目变得庞大而混乱,那么OOP开始变得有意义,因为需要某种直升机视图系统架构。 “功能与阶级”也取决于你前面的任务。

功能

  • 目的:处理数据,操纵数据,创建结果集。
  • 何时使用:如果你想这样做,总是编写一个函数:“y = f(x)”

struct / dict / json / etc(而不是类)

  • 目的:存储attr./param。,维护attr./param。,重用attr./param。,使用attr./param。后面。
  • 何时使用:如果你处理一组属性/参数(最好不是可变的)
  • 不同的语言相同的东西:struct(C / C ++),JSON(js),dict(python)等。
  • 总是比复杂的类更喜欢简单的struct / dict / json / etc(保持简单!)

类(如果是新数据类型)

  • 一个简单的视角:是附加方法的struct(C),dict(python),json(js)等。
  • 该方法只应与存储在类中的数据/参数组合使用。
  • 我的建议:永远不要在类方法中编写复杂的东西(改为调用外部函数)
  • 警告:不要滥用类作为函数的假名称空间! (这种情况经常发生!)
  • 其他用例:如果你想进行大量的运算符重载,那就使用类(例如你自己的矩阵/向量乘法类)
  • 问问自己:这真的是一种新的“数据类型”吗? (是=>类|否=>可以避免使用类)

array / vector / list(存储大量数据)

  • 目的:存储大量相同数据类型的同类数据,例如:时间序列
  • 建议#1:只使用您的编程语言已有的内容。不要重新发明它
  • 建议#2:如果你真的想要你的“类mysupercooldatacontainer”,那么重载现有的数组/向量/列表/等级类(例如“class mycontainer:public std :: vector ...”)

enum(枚举类)

  • 我只是提到它
  • 建议#1:使用enum plus switch-case而不是过于复杂的OOP设计模式
  • 建议#2:使用有限状态机

答案 6 :(得分:2)

永远不要创建课程。至少要讨论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方法更好,因为:

  • namedtuples具有更简洁的语法和标准用法。
  • 在理解现有代码方面,命名元组基本上无法理解。课程更复杂。课程可以非常复杂,供人类阅读。
  • namedtuples是不可变的。管理可变状态会增加不必要的复杂性。
  • class inheritance增加了复杂性,并隐藏了复杂性。

我无法看到使用OOP类的一个优势。显然,如果您习惯于OOP,或者您必须与需要Django等类的代码进行交互。

BTW,大多数其他语言都有一些记录类型的功能,如namedtuples。例如,Scala有案例类。这种逻辑同样适用于那里。

答案 7 :(得分:1)

我打算从这个群体中脱离出来并提供另一种观点:

永远不要创建课程。

对类的依赖会导致编码人员创建臃肿和缓慢的代码。传递的类(因为它们是对象)比调用函数和传递一两个字符串需要更多的计算能力。函数的正确命名约定几乎可以完成创建类所能做的所有事情,并且只需要一小部分开销和更好的代码可读性。

这并不意味着你不应该学习理解课程。如果你正在与他人一起编码,人们会一直使用它们,你需要知道如何兼顾这些课程。编写代码以依赖函数意味着代码将更小,更快,更易读。我已经看到大量网站只使用快速而快速的功能编写而且我已经看到了具有最小功能的小网站,这些网站严重依赖于类并且不断打破。 (当你有类扩展包含类作为其类的一部分的类时,你知道你已经失去了所有易于维护的外观。)

当涉及到它时,您想要传递的所有数据都可以通过现有数据类型轻松处理。

类被创建为一个心理拐杖并且不提供实际的额外功能,而且他们倾向于创建的过于复杂的代码从长远来看会破坏该拐杖的重点。