6

我指的是http://golang.org/pkg/log/上的func Printf的来源

func Printf(format string, v ...interface{})
Printf calls Output to print to the standard logger. Arguments are handled in the manner of fmt.Printf.

我有两个问题:

  1. 是什么 '...' ?
  2. ...interface{} 是什么意思?

非常感谢

4

3 回答 3

7

这并不完全是特定的,所以与其他回答者不同,我会走更一般的路线。

关于变量参数 ( ...)

...这里称为“省略号”,表示函数可以接收可变数量的参数,通常称为 varargs(或 var-args,或其他拼写)。这称为可变参数函数

它只是意味着,根据以下签名:

func Printf(format string, v ...interface{}) (n int, err error) {}

Printf将需要第一个类型的参数string,然后是 0 到 N 个类型的参数interface{}。下一节将详细介绍该类型。

虽然提供任意数量的参数的能力看起来非常方便,并且在此处不涉及太多细节以免偏离主题,但它带有一些警告,具体取决于语言中的实现:

  • 内存消耗增加,
  • 可读性下降,
  • 代码安全性降低。

我会留给你从上面的资源中查找原因。

在空界面 ( interface{})

这个语法位更特定于 Go,但提示在名称中:interface.

接口(或更接近 Go 的范式,协议)是一种类型,它定义了其他对象要遵守的契约。根据这篇关于计算接口的维基百科文章(粗体字强调,斜体字更正):

在面向对象的语言中,**术语“接口”通常用于定义不包含数据但公开定义为方法的行为的抽象类型。具有与该接口对应的所有方法的类被称为实现该接口。此外,一个类可以[在某些语言中])实现多个接口,因此可以同时具有不同的类型。

因此,接口是类型定义;在任何可以交换对象的地方(在函数或方法调用中)要交换的对象的类型可以根据接口而不是特定类来定义。这允许以后的代码使用相同的函数来交换不同的对象类型;_[旨在]_ 通用且可重用。

现在回到 Go 的空界面

Go 是一种强类型语言,具有多种内置类型,包括Interface Types,它们在当前 (1.1) 语言规范中将其描述为 gollows:

接口类型指定称为其接口的方法集。接口类型的变量可以使用作为接口的任何超集的方法集存储任何类型的值。据说这种类型实现了接口。

再往下,您会被介绍到您在Printf的签名中看到的构造,interface{}(以粗体强调):

一个类型实现了包含其方法的任何子集的任何接口,因此可以实现几个不同的接口。例如,所有类型都实现空接口

interface{}

这基本上意味着任何类型都可以表示为“空接口”,因此Printf可以接受这些可变参数的任何类型的变量。


与其他语言的快速比较

从历史上看,这个名字printf来自 C 函数和同名的二进制文件,printf意思是“打印格式”,尽管在早期的语言中有可变参数打印函数,可变参数函数用于许多其他场景。然而,printf通常被认为是这种用途的主要例子。它在 C 中的签名是:

int printf(const char *format, ...);

由于它们的实用性,可变参数和printf的熟悉面孔出现在大多数语言中......

在 Java 中,printf以多种形式存在,特别是来自PrintStream类:

public PrintStream printf(String format, Object... args)

其他一些语言不关心指定变量参数并使其隐含,例如在 JavaScript 中,函数中的arguments特殊变量允许访问传递给函数的任何参数,无论它们是否与原型匹配。

console.log()方法将是一个类似于 的示例printf,为清楚起见扩展了以下伪签名(但实际上只是使用arguments):

console.log(obj1 [, obj2, ..., objN);
console.log(msg [, subst1, ..., substN);
于 2013-09-05T07:47:21.547 回答
4

该文档直接回答了您的问题。这是链接和相关部分:

http://golang.org/doc/effective_go.html

Printf 的签名使用类型 ...interface{} 作为其最终参数,以指定任意数量的参数(任意类型)可以出现在格式之后。

func Printf(format string, v ...interface{}) (n int, err error) {

在函数 Printf 中,v 的行为类似于 []interface{} 类型的变量,但如果将其传递给另一个可变参数函数,则它的行为类似于常规的参数列表。下面是我们上面使用的函数 log.Println 的实现。它将其参数直接传递给 fmt.Sprintln 以进行实际格式化。

// Println prints to the standard logger in the manner of fmt.Println.
func Println(v ...interface{}) {
    std.Output(2, fmt.Sprintln(v...))  // Output takes parameters (int, string)
}

我们在对 Sprintln 的嵌套调用中在 v​​ 之后写 ... 来告诉编译器将 v 视为参数列表;否则它只会将 v 作为单个切片参数传递。

于 2013-09-05T06:39:16.790 回答
3

Go 文档非常好,语言规范写得很好,易于理解。为什么不看看?

http://golang.org/ref/spec#Function_types

http://golang.org/ref/spec#Passing_arguments_to_..._parameters

http://golang.org/ref/spec#Interface_types

Ctrl-F 在您的浏览器中寻找...interface{}会启发您。

于 2013-09-05T07:25:17.563 回答