英文:
What is ...interface{} as an argument
问题
我正在参考http://golang.org/pkg/log/上的func Printf的源代码。
func Printf(format string, v ...interface{})
Printf调用Output函数将内容打印到标准日志记录器。参数的处理方式类似于fmt.Printf。
我有两个问题:
- '...'是什么意思?
- '...interface{}'是什么意思?
非常感谢!
英文:
I am referring to the source of func Printf on http://golang.org/pkg/log/
func Printf(format string, v ...interface{})
Printf calls Output to print to the standard logger. Arguments are handled in the manner of fmt.Printf.
I have two questions:
- What is the '...' ?
- What does ...interface{} mean?
Thank you very much
答案1
得分: 7
这与Go语言无关,所以与其他回答者不同,我将选择更一般的路线。
关于可变参数(...
)
...
,也称为“省略号”,表示函数可以接收可变数量的参数,通常称为可变参数(varargs)。这被称为可变参数函数。
这意味着根据以下签名:
func Printf(format string, v ...interface{}) (n int, err error) {}
Printf
将期望一个类型为string
的第一个参数,然后是0到N个类型为interface{}
的参数。在下一节中会详细介绍该类型。
虽然提供任意数量的参数的能力似乎非常方便,但根据语言中的实现方式,它也带来了一些注意事项:
- 内存消耗增加,
- 可读性降低,
- 代码安全性降低。
我将让你自己查阅上述资源以了解其中的原因。
关于空接口(interface{}
)
这个语法有点更加与Go相关,但提示就在名字中:interface
。
接口(或更接近Go范式的说法,协议)是一种定义其他对象遵循的契约的类型。根据这篇关于计算中的接口的Wikipedia文章(我加粗了重点并进行了修正):
在面向对象的语言中,**术语“接口”通常用于定义一个不包含数据但公开作为方法定义的行为的抽象类型。具有与该接口对应的所有方法的类被称为实现该接口。此外,一个类可以(在某些语言中)实现多个接口,因此可以同时具有不同类型。
因此,接口是一种类型定义;在任何可以交换对象的地方(在函数或方法调用中),可以使用接口来定义要交换的对象的类型,而不是特定的类。这允许后续的代码使用相同的函数交换不同的对象类型;旨在实现通用和可重用。
现在回到Go的空接口
Go是一种强类型语言,具有几种内置类型,包括接口类型,在当前(1.1)语言规范中对其进行了如下描述:
接口类型指定了一个称为其接口的方法集。接口类型的变量可以存储具有方法集为接口的任何超集的任何类型的值。这样的类型被称为实现该接口。
在接下来的部分,你将看到在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);
英文:
This isn't exactly specific to go, so unlike the other answerers I'll go the more general route.
On Variable Arguments (...
)
...
here, called "ellipsis", indicates the function can receive a variable number of arguments, often referred to as varargs (or var-args, or other spellings). This is called a variadic function.
It simply means that, according to the following signature:
func Printf(format string, v ...interface{}) (n int, err error) {}
Printf
will expect a first argument of type string
, and then between 0 and N arguments of type interface{}
. More on that type in the next section.
While the ability to supply any number of arguments can seem very handy, and without going into too much details here for risk of going off-topic, it comes with a few caveats depending on the implementation in the language:
- increase in memory consumption,
- decrease in readability,
- decrease in code security.
I'll leave it up to you to look up why from the resources above.
On the Empty Interface (interface{}
)
This syntax bit is a bit more Go-specific, but the hint is in the name: interface
.
An interface (or closer to Go's paradigm, a protocol), is a type that defines a contract for other objects to comply to. According to this Wikipedia article on interfaces in computing (emphasis in bold mine and corrections in italics mine):
> In object-oriented languages, **the term "interface" is often used to define an abstract type that contains no data, but exposes behaviors defined as methods. A class having all the methods corresponding to that interface is said to implement that interface. Furthermore, a class can [in some languages]) implement multiple interfaces, and hence can be of different types at the same time.
>
> An interface is hence a type definition; anywhere an object can be exchanged (in a function or method call) the type of the object to be exchanged can be defined in terms of an interface instead of a specific class. This allows later code to use the same function exchanging different object types; _[aiming to be]_ generic and reusable.
Now back to Go's Empty Interface
Go is a strongly typed language, with several built-in types, including Interface Types, which they describe as gollows in the current (1.1) language specifications:
> An interface type specifies a method set called its interface. A variable of interface type can store a value of any type with a method set that is any superset of the interface. Such a type is said to implement the interface.
Futher down, you are introduced to the construct you see in Printf
's signature, interface{}
(emphasis in bold mine):
> A type implements any interface comprising any subset of its methods and may therefore implement several distinct interfaces. For instance, all types implement the empty interface:
>
> interface{}
This basically means that any type an be represented as the "empty interface", and thus that Printf
can accept variables of any type for these varargs.
A Quick Comparison with Other Languages
Historically, the name printf
comes from the C function and from the binary of the same name, printf
meaning "print formatted", though there were variadic print functions in earlier languages, and variadic functions are used for many other scenarios. However, printf
is often considered the prime example of such a use. It's signature in C is:
int printf(const char *format, ...);
As a result of their practicality, varargs and printf
's familiar face show up in most languages...
In Java, printf
exists under multiple forms, notably from the PrintStream
class:
public PrintStream printf(String format, Object... args)
Some other langauges do not bother with specifying variable arguments and make it implicit, for instance in JavaScript, the arguments
special variable within a function allows to access any arguments passed to a function, whether they match the prototype or not.
The console.log()
method would be an example similar to printf
, with the following pseudo-signature expanded for clarity (but actually simply using arguments
):
console.log(obj1 [, obj2, ..., objN);
console.log(msg [, subst1, ..., substN);
答案2
得分: 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以fmt.Println的方式打印到标准记录器。
func Println(v ...interface{}) {
std.Output(2, fmt.Sprintln(v...)) // Output接受参数(int, string)
}
我们在对Sprintln的嵌套调用中的v后面加上...,告诉编译器将v视为参数列表;否则,它将只将v作为单个切片参数传递。
英文:
The documentation directly answers to your question. Here is the link and the related part:
http://golang.org/doc/effective_go.html
The signature of Printf uses the type ...interface{} for its final argument to specify that an arbitrary number of parameters (of arbitrary type) can appear after the format.
func Printf(format string, v ...interface{}) (n int, err error) {
Within the function Printf, v acts like a variable of type []interface{} but if it is passed to another variadic function, it acts like a regular list of arguments. Here is the implementation of the function log.Println we used above. It passes its arguments directly to fmt.Sprintln for the actual formatting.
// 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)
}
We write ... after v in the nested call to Sprintln to tell the compiler to treat v as a list of arguments; otherwise it would just pass v as a single slice argument.
答案3
得分: 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{}
,会让你茅塞顿开。
英文:
The Go documentation is pretty good and the Language specification is really well written and understandable. Why not have a look?
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 in your browser and looking for ...
and interface{}
will enlighten you.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论