【问题标题】:adding properties dynamically creates unwanted aliases动态添加属性会创建不需要的别名
【发布时间】:2020-04-12 00:34:50
【问题描述】:

我想为我的类动态添加属性,如下所示,但是我最终创建了别名。我怎样才能防止这种情况发生?

class A:

    def __init__(self, a, b):
        self._a = a
        self._b = b

for attr in ('a', 'b'):
    f = lambda self: getattr(self, '_'+attr)
    setattr(A, attr, property(f, None))

a = A(0,1)

print(a.a)
print(a.b)

然而这会产生:

1
1

编辑:

关于闭包作用域的评论是相关的,但是这留下了一个问题,即是否可以动态生成引用 self open 的某些属性的属性。

特别是关于上面的例子:如果有的话,我如何设置属性以使 a.a 返回 0 而不是 1?如果我只是尝试将属性参数传递给 lambda 函数,则需要传递该属性,因此这是行不通的。

【问题讨论】:

  • 这能回答你的问题吗? Python lambda closure scoping
  • 那么,这是否意味着无法动态设置引用自身某些属性的类的属性?
  • 这意味着 attr 在创建 lambda 时没有被评估,而是名称 attr 绑定到外部范围内的变量,最终具有值 'b' ,所以两个 lambda 表达式都获得了 _b 属性。
  • 我想我明白这一点。那么剩下的问题是:可以生成动态引用 self 的某些属性的属性吗?或者特别是关于上面的例子:如何设置属性使得 a.a 返回 0 而不是 1

标签: python dynamic properties alias


【解决方案1】:

为了获得您想要的结果,您需要将您的 lambda 封装在另一个函数中,如下所示:

def make_fget(attr):
    return lambda self: getattr(self, '_' + attr)

这样,当您调用make_fget 时,本地名称attr 将绑定到传递的参数。

您可以像在原始代码中一样在循环中调用它:

for attr in ('a', 'b'):
    setattr(A, attr, property(make_fget(attr), None))

这里的区别在于,在您的原始版本中,循环本质上是在每次迭代时重新分配 attr,而 lambda 在它被调用时只在外部范围(循环)中查看 attr,它最终将得到上一次迭代中分配的任何内容。

通过包装在另一个函数中,在每次循环迭代中,您实际上为返回的 lambda 创建了一个新的外部范围(函数调用),名称 attr 绑定到传递的参数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-10-20
    • 2014-04-15
    • 1970-01-01
    • 1970-01-01
    • 2011-08-28
    • 2015-03-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多