当函数或方法调用中省略括号时,这意味着什么?

26

我有这个示例代码:

class objectTest():
    def __init__(self, a):
        self.value = a

    def get_value(self):
        return self.value

a = objectTest(1)
b = objectTest(1)
        
print(a == b)
print(a.get_value() == b.get_value)
print(a.get_value() == b.get_value())
print(a.get_value == b.get_value)

显示的结果为:

False
False
True 
False

为什么这不会引起错误? get_value是一个方法,那么为什么我们在没有先调用它的情况下可以像这样使用它呢?


另请参阅: Python中的'<function at ...>'是什么意思?


2
以防万一您到这里是因为您真的想在不使用括号的情况下调用函数,请注意有时可以通过hacky decators实现。例如:`>>> f = lambda *args: print('hi')
@f ... class _: pass ... hi `
- Chris_Rands
3
什么时候你会需要这样做? - wjandrea
6个回答

39

如前所述,函数和方法是一等对象。您可以在其末尾添加一些括号(圆括号)来调用它们。但似乎您希望更多地了解为什么Python甚至让我们这样做。我们为什么要关心函数是否是一等对象呢?

有时候您不想调用它们,而是想传递对可调用对象本身的引用。

from multiprocessing import Process
t = Process(target=my_long_running_function)

如果在上述代码中添加了括号,那么它将在主线程中执行my_long_running_function,这显然不是你想要的!你想要的是提供一个可调用对象的引用给Process,让它在新的进程中运行。

有时候你只需要指定可调用对象,让其他东西......

def do_something(s):
    return s[::-1].upper()

map(do_something,['hey','what up','yo'])
Out[3]: ['YEH', 'PU TAHW', 'OY']

在这种情况下,(map)需要填写它的参数。

也许你只想将一堆可调用对象放入某个集合中,并以动态方式获取所需的对象。

from operator import *

str_ops = {'<':lt,'>':gt,'==':eq} # etc
op = str_ops.get(my_operator)
if op:
    result = op(lhs,rhs)

以上是将操作符的字符串表示映射到其实际操作的一种方法。

那么在这种情况下,如果我在代码中看到一个 assert do_something,那意味着什么? 简而言之,使用 assert 断言一个函数会实现什么效果? - Apurva Kunkulol

25
在Python中,函数和方法本身也是对象。因此,您可以像比较其他对象一样比较它们。
>>> type(a.get_value)
<type 'instancemethod'>
>>> type(a.get_value())
<type 'int'>

通常情况下,您不会将方法与其他任何内容进行比较,因为这并不特别有用。 一个地方它有用的是当您想要将一个函数传递到另一个函数时。一个常见用法是使用str.lower作为生成排序键的方法来进行不区分大小写的字符串排序:

>>> list_of_strings=['A','b','C']
>>> sorted(list_of_strings)
['A', 'C', 'b']
>>> sorted(list_of_strings, key=str.lower)
['A', 'b', 'C']

5
简单来解释一下Mark的评论,当你在结尾不加圆括号输入“a.get_value”时,实际上并没有调用这个方法。你只是在引用与该方法相关联的对象。 - Behram Mistree
1
提供一些如何使用它以及为什么这是可取的示例,并获得+1。 - M4rtini

7
def mul(a, b):
    return a * b

def add(a, b):
    return a + b

def do(op, a, b):
    return op(a, b)

do(add, 2, 3)  # return 5

6
print(a.get_value() == b.get_value)   # 1
print(a.get_value() == b.get_value()) # 2
print(a.get_value == b.get_value)     # 3

1) 调用 a.get_value() 的返回值是否等于方法 b.get_value

2) a.get_value() 返回的结果是否与 b.get_value() 相同?

3) 方法引用 a.get_value 是否等于方法引用 b.get_value

这是完全有效的Python代码 :)


1

有些评论员想要一个有用的例子。一个应用场景是在线程编程中。我们需要传递目标给线程,但不能使用括号。否则,目标将在主线程中创建,而这正是我们想避免的。

例如:

在test1.py中,我调用ThreadTest时不使用括号。test_thread在线程中启动,并允许test1.py继续运行。

在test2.py中,我将ThreadTest()作为目标传递。在这种情况下,该线程不允许test2.py继续运行。

test1.py

import threading
from thread_test import ThreadTest

thread = threading.Thread(target=ThreadTest)
thread.start()
print('not blocked')

test2.py

import threading
from thread_test import ThreadTest

thread = threading.Thread(target=ThreadTest())
thread.start()
print('not blocked')

test_thread.py

from time import sleep


class ThreadTest():
    def __init__(self):
        print('thread_test started')
        while True:
            sleep(1)
            print('test_thread')

test1.py的输出:

thread_test started
not blocked
test_thread
test_thread
test_thread

test2.py的输出结果为:

thread_test started
test_thread
test_thread
test_thread

我正在Linux Mint上使用Python3.5。


否则目标将在主线程中创建,这正是我们试图避免的。您的意思是目标在主线程中执行调用(而不是“创建”)吗? @Oppy - undefined

1
没有括号和有括号的函数之间的区别在于使用括号时,您将获得该函数的输出,而不使用括号时,您将创建该函数的副本。例如:
def outerFunction(text): 
    text = text 

    def innerFunction(): 
        print(text) 

    return innerFunction()

if __name__ == '__main__': 
    outerFunction('Hey!') 
    x = outerFunction
    y = x 
    x('Hey i am busy can you call me later')
    y('this is not a function')

在这里,我们将函数outerFunction复制到x中,然后将y复制到x中。


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