Python中的空函数对象

15

我听说 Python 中的函数就像列表、字典等一样是对象。但如果要使用函数进行类似的操作,有什么类似的方法呢?

# Assigning empty list to 'a'
a = list()

# Assigning empty function to 'a'
a = lambda: pass
# ???

你会如何做到这一点?此外,这是否是必要的或适当的? 下面是我想更好地使用它以获得上下文的意义:

我有一个 QListWidget,用于选择与字典中的键相关联的项目。此字典中的值也是字典,其中包含某些项目的特定属性,我可以添加这些特定属性作为键,并通过调用不同的函数来初始化或更新其值。 因此,我在窗口中存储了一个变量,当按下按钮时,该变量将被更新,以告诉此脚本要更新哪个属性。

正如您所见,我希望存储该函数以根据情况使用正确的函数将数据映射到函数。

# Get selection from the list
name = selected_item
# Initialize an empty function
f = lambda: pass
# Use property that is being added now, which was updated by the specific button that was pushed
property_list = items[name][self.property_currently_being_added]
if self.property_currently_being_added == "prop1":
    f = make_property1()
elif self.property_currently_being_added == "prop2":
    f = make_property2()
elif self.property_currently_being_added == "prop3":
    f = make_property3()
elif self.property_currently_being_added == "prop4":
    f = make_property4()

# map the certain function to the data which was retrieved earlier
added_property = map(f, data)
property_list.append(added_property)

3
为什么你认为需要一个“空”的函数?你当然可以按照你写的方式来做,但为什么需要呢?你的函数在定义之前是否可能被调用? - Henry Keiter
1
我同意Henry的观点,似乎没有太大的理由去这样做。在你的例子中,你可以简单地使用else: raise ValueError等方式。如果self.property_currently_being_added == None或其他情况下,似乎你不希望这个函数继续执行。 - Adam Smith
3
这段话的意思是,这个函数并不一定非得用 lambda 表示法。你可以很容易地写成 def f(): pass,效果是一样的。lambda 表示法只有在以下情况下才有必要使用:(a) 你不想给这个函数命名(但本例中你把它直接赋值给 f 了);(b) 你需要在一个表达式中定义它,而不是一个语句中(但本例中你直接在赋值语句中使用它);或者 (c) 你在上 Scheme 课,教授讨厌 Python,宁愿教 Scheme(本例中可能不适用)。 - abarnert
1
你可以创建一个恒等函数。但使用lambda表达式行不通。 - wim
1
如果你真的想要一个_真正_的空函数对象,可以试试这个:c = types.CodeType(0, 0, 0, 0, 0, b'', (), (), (), '<main>', 'f', 0, b''),然后 f = types.FunctionType(c, {})。当然,这个函数调用时只会引发一个SystemError……但是你期望一个空函数有多有用呢? :) - abarnert
显示剩余3条评论
4个回答

31

首先,这个方法无法正常工作的原因是:

a = lamdba: pass

问题在于,lambda 只允许使用表达式,并定义一个返回表达式值的函数。由于 pass 是一个语句而不是表达式,所以这是非法的。

但是,下面的代码可以正常工作:

a = lambda: None

在Python中,一个函数如果没有return语句的话,执行结束后会默认返回None。因此,下面这两个函数是等价的:


```python def func1(): result = 42
def func2(): result = 42 return result ```
def a(): return None
def a(): pass

然而,我不明白你为什么想要将这个函数写成一个lambda表达式和一个赋值语句; 使用def会更短更易读,并且会给你一个可以自省的函数对象和一个好的名字(a而不是<lambda>),等等。唯一需要使用lambda的原因是当你不想给函数起个名字或者需要在表达式中定义函数。显然,这两种情况都不适用于你,因为你直接在赋值语句中使用lambda。所以,只需使用def


与此同时,从某种意义上说,这是一个“空函数”,或者至少尽可能为空(例如,通过调用dis.dis(a),你可以看到它仍然需要两个字节码来做除了掉下末尾并返回None之外的任何事情),但它对你的情况没有用处。你不需要一个“空函数”。如果你试图将你的a传递给map,你只会得到一个TypeError,因为你正在尝试用一个参数调用一个没有参数的函数(因为这就是map做的)。

你可能想要的是一个恒等函数,它只是原样返回其参数。像这样:

def a(x): return x

但我不确定那是否是你想要的。在这种情况下,你想要按原样附加data吗?还是你想要做一些不同的事情,比如提前返回、引发异常、不附加任何东西等等?


最后,我不明白为什么你需要一个函数。如果没有要映射的内容,为什么不直接不调用map呢?你已经有一个完全正常的else子句来捕获这种情况了(如果你想要提前返回或引发异常,这非常方便...)。或者,如果你喜欢,你可以开始使用f = None,然后使用if f:来决定是否映射。或者,如果你真的想要:

added_property = [f(element) if f else element for element in data]

… 或者 …

added_property = map(f, data) if f else data
作为最后一点说明,您可能需要一个字典而不是重复相同内容的长if/elif链:
propfuncs = {'prop1': make_property1(),
             'prop2': make_property2(),
             'prop3': make_property3(),
             'prop4': make_property4()}

然后,所有的杂乱无章都转化为这两行代码:

f = propfuncs.get(self.property_currently_being_added)
added_property = map(f, data) if f else data

当然,更好的设计可能是用一个单独的函数来替换所有那些make_propertyN函数,你可以调用它作为make_property(1)或者make_property('prop1')... 但是我没有看到它们实际做什么,所以无法确定。


谢谢 @abarnert 这个回答很棒。你提到的 propfuncs 的想法不错,我认为这可能会有所帮助。make_propertyN() 函数实际上是用来初始化其他嵌套字典的函数,但我认为对于我的特定情况来说,这是一个很好的方法,希望它也能帮助其他人。 - chase

7
为了完整性以及由于标题是“Python中的空函数对象”,更一般的情况是一个带任意数量参数的空函数对象,因此您可以在任何回调中使用它。这就是它:

callback = lambda *_, **__: None

这里有一个解释:http://echochamber.me/viewtopic.php?t=64825

(Explanation is here: http://echochamber.me/viewtopic.php?t=64825)

1

我很惊讶地发现你甚至可以做...

def a(): "This is a test"

a()

0

这感觉就像你在寻找一个Nothing函子,我猜如果你了解单子的话,甚至不需要一个空函数,灵感来自于PyMonad有一个不错的Nothing实现,我通常喜欢创建自己的,但这是一个很好的起点。


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