"first-class"对象是什么?

257
在给定的编程语言中,何时将对象或其他内容称为“一等公民”,原因是什么?它们与那些没有这种特性的语言有何不同?
当人们说“一切都是对象”(例如在Python中)时,他们确实意味着“一切都是一等公民”吗?
6个回答

235

简而言之,它意味着对象使用没有限制。它与任何其他对象相同。

一等公民是指可以被动态创建、销毁、传递给函数、作为值返回,并具有编程语言中其他变量拥有的所有权利的实体。

根据不同的语言,这可能意味着:

  • 可以表达为匿名文字值
  • 可存储在变量中
  • 可以存储在数据结构中
  • 具有固有身份(独立于任何给定名称之外)
  • 可与其他实体进行平等比较
  • 可作为过程/函数的参数传递
  • 可作为过程/函数的结果返回
  • 可在运行时构建
  • 可打印
  • 可读取
  • 可在分布式进程间传输
  • 可在运行进程之外存储

出处

在C++中,函数本身不是一等公民对象,但是:

  • 您可以重载'()'运算符,使对象函数成为一等公民。
  • 函数指针是一等公民。
  • boost bind、lambda和function确实提供了一等公民函数。

在C++中,类不是一等公民对象,但是这些类的实例是。在Python中,类对象都是一等公民对象。(有关类作为对象的详细信息,请参见此答案)。

以下是Javascript一等公民函数的示例:

// f: function that takes a number and returns a number
// deltaX: small positive number
// returns a function that is an approximate derivative of f
function makeDerivative( f, deltaX )
{
    var deriv = function(x)
    { 
       return ( f(x + deltaX) - f(x) )/ deltaX;
    }
    return deriv;
}
var cos = makeDerivative( Math.sin, 0.000001);
// cos(0)     ~> 1
// cos(pi/2)  ~> 0

资料来源

不是一等对象的实体被称为二等对象。C ++中的函数是二等的,因为它们不能动态创建。

关于编辑:

编辑:当有人说“一切都是对象”(例如在Python中)时,他确实意味着“一切都是头等的”吗?

术语“对象”可以宽泛使用,并不意味着它是头等的。更合理的做法可能是称整个概念为“头等实体”。但在Python中,他们确实力求使一切成为头等的。我相信你所引述的那个人的意图是指头等的实体。


4
可以举些不被视为'一等公民'的对象例子吗? - Sudip Bhandari
1
@SudipBhandari 我也曾经想过同样的问题,最终偶然发现了维基百科上关于这个主题的有用文章:一等公民/对象。我发现 Robin Popplestone 的定义特别有帮助。(顺便说一句,发布一个 WP 文章可能看起来非常明显,但我没有意识到这是一个基本的编程语言概念) - Mike B
有人能给出一个匿名字面值的例子吗? - RARA
2
@Papaya-Automaton:当然,几乎所有的原始类型都可以:整数、字符串、浮点数,布尔常量“true”和“false”。根据编程语言的不同,你可能会有更多选择(例如,在C语言中使用数组 [1,2,3],或在Scheme中使用列表 '(1 2 3))。 - Alex M.

29

当有人说“一切皆对象”(比如在Python中),他是否真的意味着“一切都是第一类对象”?

是的。

Python中的所有东西都是一个合适的对象,即使是其他语言中的“原始类型”也是如此。

你会发现像 2 这样的对象实际上拥有相当丰富和复杂的接口。

>>> dir(2)
['__abs__', '__add__', '__and__', '__class__', '__cmp__', '__coerce__', '__delattr__', '__div__', '__divmod__', '__doc__', '__float__', '__floordiv__', '__getattribute__', '__getnewargs__', '__hash__', '__hex__', '__index__', '__init__', '__int__', '__invert__', '__long__', '__lshift__', '__mod__', '__mul__', '__neg__', '__new__', '__nonzero__', '__oct__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__str__', '__sub__', '__truediv__', '__xor__']
因为Python中所有东西都是一等对象,所以相对而言没有太多晦涩的特殊情况。
例如,在Java中有原始类型(int、bool、double、char)不是真正的对象,这就是为什么Java必须引入Integer、Boolean、Double和Character作为一等类型。这可能对初学者来说很难教授——一个原始类型和一个类必须并存并不明显。
这也意味着一个对象的类本身也是一个对象。这与C++不同,C++中的类并不总是具有明确的运行时存在。
数字2的类型是"int"类型的对象,它有方法、属性和类型。
>>> type(2)
<class 'int'>
内置类型如int的类型是type 'type'对象。它还有方法和属性。

内置类型如int的类型是type 'type'对象。它还有方法和属性。

>>> type(type(2))
<class 'type'>

1
这适用于现代Python。在旧版Python(版本1?那是在我之前)中,您无法从“int”继承。因此,“旧式”与“新式类”(在3中,不再有旧式类)的区别。 - Keith Pinson
2
这在现代Python中甚至不是真的。忽略为什么这会有用,所有位运算/布尔运算符都不是一等对象。尝试 dir(&)。仍然有语言语法的部分不能赋值给变量,也不能用作一等对象。 - Tristan Bodding-Long

20

“一等公民”指可以按照通常方式对其进行操作。大多数情况下,这只意味着您可以将这些一等公民作为参数传递给函数,或从函数中返回它们。

对于对象来说,这是不言自明的,但对于函数甚至类来说,情况并非总是如此明显:

void f(int n) { return n * 2; }

void g(Action<int> a, int n) { return a(n); }

// Now call g and pass f:

g(f, 10); // = 20

这是C#中一个示例,其中函数实际上并不是一等对象。因此,上述代码使用了一个小的解决方法(即称为Action<>的通用委托),以将函数作为参数传递。其他语言,例如Ruby或Python,允许将甚至类和代码块视为普通变量(或在Ruby的情况下,常量)。


19

3

我认为这是一种用自然语言描述事物的比喻之一。该术语主要用于描述函数作为一等对象的情境。

如果您考虑面向对象的编程语言,我们可以为对象赋予各种特性,例如:继承、类定义、能够传递到代码的其他部分(方法参数)、能够存储在数据结构中等等。如果我们可以对一个通常不被视为对象的实体进行相同的操作,例如 JavaScript 中的函数,则将这些实体视为一等对象。

这里的“一等”基本上意味着不被当作二等对象(行为降级)。模拟是完美的或无法区分的。


3
一个简单的理解方法是问: 什么不是“一等公民”?
在Python中,例如像+或*这样的运算符不是一等公民。
您可以直接使用这些运算符:
2+2
"foo"+"bar"
2*3
"bla"*3

但是你不能将运算符分配给变量:

operator = +
operators = [+,*]

这也意味着:您不能将运算符作为函数参数传递,因为运算符不是一等公民。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接