类与函数

函数式编程和面向对象编程的区别是什么?人们应该如何决定应该选择什么样的编程范型?一个人比另一个人有什么好处?

即使对于没有任何编程经验的人来说,函数也很容易理解,只要有一定的数学背景。另一方面,课程似乎更难掌握。

假设我想创建一个类/函数来计算出生年份和当前年份的人的年龄。我应该为此创建一个类还是创建一个函数? 还是选择取决于具体情况?

另外,我正在研究 Python,但我想这个问题很一般。

177434 次浏览

Create a function. Functions do specific things, classes are specific things.

Classes often have methods, which are functions that are associated with a particular class, and do things associated with the thing that the class is - but if all you want is to do something, a function is all you need.

Essentially, a class is a way of grouping functions (as methods) and data (as properties) into a logical unit revolving around a certain kind of thing. If you don't need that grouping, there's no need to make a class.

It depends on the scenario. If you only want to compute the age of a person, then use a function since you want to implement a single specific behaviour.

But if you want to create an object, that contains the date of birth of a person (and possibly other data), allows to modify it, then computing the age could be one of many operations related to the person and it would be sensible to use a class instead.

Classes provide a way to merge together some data and related operations. If you have only one operation on the data then using a function and passing the data as argument you will obtain an equivalent behaviour, with less complex code.

Note that a class of the kind:

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

isn't really a class, it is only a (complicated)function. A legitimate class should always have at least two methods(without counting __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).)

Like what Amber says in her answer: create a function. In fact when you don't have to make classes if you have something like:

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

Here you just have a function encapsulate in a class. This just make the code less readable and less efficient. In fact the function compute can be written just like this:

def compute(arg1, arg2, other):
return arg1 + arg2 % other

You should use classes only if you have more than 1 function to it and if keep a internal state (with attributes) has sense. Otherwise, if you want to regroup functions, just create a module in a new .py file.

You might look this video (Youtube, about 30min), which explains my point. Jack Diederich shows why classes are evil in that case and why it's such a bad design, especially in things like API.
It's quite a long video but it's a must see.

Classes (or rather their instances) are for representing things. Classes are used to define the operations supported by a particular class of objects (its instances). If your application needs to keep track of people, then Person is probably a class; the instances of this class represent particular people you are tracking.

Functions are for calculating things. They receive inputs and produce an output and/or have effects.

Classes and functions aren't really alternatives, as they're not for the same things. It doesn't really make sense to consider making a class to "calculate the age of a person given his/her birthday year and the current year". You may or may not have classes to represent any of the concepts of Person, Age, Year, and/or Birthday. But even if Age is a class, it shouldn't be thought of as calculating a person's age; rather the calculation of a person's age results in an instance of the Age class.

If you are modelling people in your application and you have a Person class, it may make sense to make the age calculation be a method of the Person class. A method is basically a function which is defined as part of a class; this is how you "define the operations supported by a particular class of objects" as I mentioned earlier.

So you could create a method on your person class for calculating the age of the person (it would probably retrieve the birthday year from the person object and receive the current year as a parameter). But the calculation is still done by a function (just a function that happens to be a method on a class).

Or you could simply create a stand-alone function that receives arguments (either a person object from which to retrieve a birth year, or simply the birth year itself). As you note, this is much simpler if you don't already have a class where this method naturally belongs! You should never create a class simply to hold an operation; if that's all there is to the class then the operation should just be a stand-alone function.

I'm going to break from the herd on this one (Edit 7 years later: I'm not a lone voice on this anymore, there is an entire coding movement to do just this, called 'Functional Programming') and provide an alternate point of view:

Never create classes. Always use functions.

Edit: Research has repeatedly shown that Classes are an outdated method of programming. Nearly every research paper on the topic sides with Functional Programming rather than Object Oriented Programming.

Reliance on classes has a significant tendency to cause coders to create bloated and slow code. Classes getting passed around (since they're objects) take a lot more computational power than calling a function and passing a string or two. Proper naming conventions on functions can do pretty much everything creating a class can do, and with only a fraction of the overhead and better code readability.

That doesn't mean you shouldn't learn to understand classes though. If you're coding with others, people will use them all the time and you'll need to know how to juggle those classes. Writing your code to rely on functions means the code will be smaller, faster, and more readable. I've seen huge sites written using only functions that were snappy and quick, and I've seen tiny sites that had minimal functionality that relied heavily on classes and broke constantly. (When you have classes extending classes that contain classes as part of their classes, you know you've lost all semblance of easy maintainability.)

When it comes down to it, all data you're going to want to pass can easily be handled by the existing datatypes.

Classes were created as a mental crutch and provide no actual extra functionality, and the overly-complicated code they have a tendency to create defeats the point of that crutch in the long run.

Edit: Update 7 years later... Recently, a new movement in coding has been validating this exact point I've made. It is the movement to replace Object Oriented Programming (OOP) with functional programming, and it's based on a lot of these exact issues with OOP. There are lots of research papers showing the benefits of Functional programming over Object Oriented Programming. In addition to the points I've mentioned, it makes reusing code much easier, makes bugfixing and unit testing fasters and easier. Honestly, with the vast number of benefits, the only reason to go with OOP over Functional is compatibility with legacy code that hasn't been updated yet.

i know it is a controversial topic, and likely i get burned now. but here are my thoughts.

For myself i figured that it is best to avoid classes as long as possible. If i need a complex datatype I use simple struct (C/C++), dict (python), JSON (js), or similar, i.e. no constructor, no class methods, no operator overloading, no inheritance, etc. When using class, you can get carried away by OOP itself (What Design pattern, what should be private, bla bla), and loose focus on the essential stuff you wanted to code in the first place.

If your project grows big and messy, then OOP starts to make sense because some sort of helicopter-view system architecture is needed. "function vs class" also depends on the task ahead of you.

function

  • purpose: process data, manipulate data, create result sets.
  • when to use: always code a function if you want to do this: “y=f(x)”

struct/dict/json/etc (instead of class)

  • purpose: store attr./param., maintain attr./param., reuse attr./param., use attr./param. later.
  • when to use: if you deal with a set of attributes/params (preferably not mutable)
  • different languages same thing: struct (C/C++), JSON (js), dict (python), etc.
  • always prefer simple struct/dict/json/etc over complicated classes (keep it simple!)

class (if it is a new data type)

  • a simple perspective: is a struct (C), dict (python), json (js), etc. with methods attached.
  • The method should only make sense in combination with the data/param stored in the class.
  • my advice: never code complex stuff inside class methods (call an external function instead)
  • warning: do not misuse classes as fake namespace for functions! (this happens very often!)
  • other use cases: if you want to do a lot of operator overloading then use classes (e.g. your own matrix/vector multiplication class)
  • ask yourself: is it really a new “data type”? (Yes => class | No => can you avoid using a class)

array/vector/list (to store a lot of data)

  • purpose: store a lot of homogeneous data of the same data type, e.g. time series
  • advice#1: just use what your programming language already have. do not reinvent it
  • advice#2: if you really want your “class mysupercooldatacontainer”, then overload an existing array/vector/list/etc class (e.g. “class mycontainer : public std::vector…”)

enum (enum class)

  • i just mention it
  • advice#1: use enum plus switch-case instead of overcomplicated OOP design patterns
  • advice#2: use finite state machines

Never create classes. At least the OOP kind of classes in Python being discussed.

Consider this simplistic class:

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 this namedtuple version:

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))

The namedtuple approach is better because:

  • namedtuples have more concise syntax and standard usage.
  • In terms of understanding existing code, namedtuples are basically effortless to understand. Classes are more complex. And classes can get very complex for humans to read.
  • namedtuples are immutable. Managing mutable state adds unnecessary complexity.
  • class inheritance adds complexity, and hides complexity.

I can't see a single advantage to using OOP classes. Obviously, if you are used to OOP, or you have to interface with code that requires classes like Django.

BTW, most other languages have some record type feature like namedtuples. Scala, for example, has case classes. This logic applies equally there.