Python动态函数属性

标签 python

我在尝试实现动态排序时遇到了一个有趣的问题。 给定以下代码:

>>> l = []
>>> for i in range(2):
>>>     def f():
>>>         return f.v
>>>     f.v = i
>>>     l.append(f)

你必须小心如何使用l中的函数:

>>> l[0]()
1
>>> l[1]()
1
>>> [h() for h in l]
[1, 1]
>>> [f() for f in l]
[0, 1]
>>> f = l[0]
>>> f()
0
>>> k = l[1]
>>> k()
0
>>> f = l[1]
>>> k()
1
>>> del f
>>> k()
NameError: global name 'f' is not defined

函数的行为取决于当前的f

我应该怎么做才能避免这个问题?如何设置不依赖于函数名称的函数属性?

更新

阅读您的评论和答案,这是我的实际问题。

我有一些数据要根据用户输入进行排序(所以我事先不知道排序标准)。用户可以选择对数据的哪一部分应用连续排序,这些排序可以是升序或降序。

所以我的第一个尝试是遍历用户输入,为每个标准定义一个函数,将这个函数存储在一个列表中,然后使用这个列表作为 sorted 的键,如下所示:key=lambda x: [f(x) for f in functions]。为了避免将条件乘以函数本身,我在函数定义之前计算了一些需要的值并将它们绑定(bind)到函数(不同的函数具有不同的预计算值)。

在调试时,我了解到函数属性不是这里的解决方案,所以我确实用 __call__ 方法编写了一个类。

最佳答案

问题是由于 return f.v 加载了 global f,而不是您想要的。1 反汇编代码可以看到:

>>> dis.dis(l[0])
  3           0 LOAD_GLOBAL              0 (f)
              3 LOAD_ATTR                1 (v)
              6 RETURN_VALUE

在填充 l 的循环之后,f 是对最后创建的闭包的引用,如您在此处所见:

>>> l
[<function f at 0x02594170>, <function f at 0x02594130>]
>>> f
<function f at 0x02594130>

因此,当你调用l[0]()时,它仍然加载指向最后创建的函数的f,并返回1。当你重新定义f 通过执行 f = l[0],然后全局 f 现在指向第一个函数。

您似乎想要的是一个具有状态的函数,它实际上是一个类。因此,您可以这样做:

class MyFunction:
  def __init__(self, v):
    self.v = v
  def __call__(self):
    return self.v

l = [MyFunction(i) for i in range(2)]

l[0]() # 0
l[1]() # 1

虽然最好先解释您的实际问题,因为可能会有更好的解决方案。

1:您可能会问,为什么它不加载全局 f 而不是当前实例?

回想一下,当您创建一个类时,您需要传递一个 self 参数,如下所示:

# ...
def my_method(self):
  return self.value

self 实际上是对对象当前实例的引用。这就是 Python 知道在哪里加载属性 value 的方式。它知道它必须查看 self 引用的实例。所以当你这样做时:

a.value = 1
a.my_method()

self 现在是对 a 的引用。

所以当你这样做的时候:

def f():
  return f.v

Python 无法知道 f 实际是什么。它不是参数,所以它必须从其他地方加载它。在您的情况下,它是从全局变量加载的。
因此,当您执行 f.v = i 时,当您执行f 的实例设置属性 v 时,无法知道您在函数主体中指的是哪个实例。

关于Python动态函数属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35318231/

相关文章:

python - 如何在 pandas DataFrame 中按索引仅保留一组特定行

python - "in"的时间复杂度(包含运算符)

Python 将字符串输入与现有字典匹配的简单方法

java - 无法将数据从 Java 客户端发送到 Python 服务器

python - Google Cloud Platform 数据流集成

python - 如何按多个键对 3d 数组进行排序

python - 显示允许的 HTTP 方法

python - mongoengine 调用导致 django View 被调用两次

python - Pandas 多索引将 float 更改为字符串

python - Django Manager.values() 返回一个神秘的 None