python - 创建类后在方法上设置属性会引发 "' instancemethod' object has no attribute“但属性显然存在

标签 python methods first-class-functions

在 Python(2 和 3)中,我们可以将属性分配给函数:

>>> class A(object):
...     def foo(self):
...         """ This is obviously just an example """
...         return "FOO{}!!".format(self.foo.bar)
...     foo.bar = 123
...
>>> a = A()
>>> a.foo()
'FOO123!!'

这很酷。

但是为什么我们不能稍后更改 foo.bar 呢?例如,在构造函数中,像这样:

>>> class A(object):
...     def __init__(self, *args, **kwargs):
...         super(A, self).__init__(*args, **kwargs)
...         print(self.foo.bar)
...         self.foo.bar = 456  # KABOOM!
...     def foo(self):
...         """ This is obviously just an example """
...         return "FOO{}!!".format(self.foo.bar)
...     foo.bar = 123
...
>>> a = A()
123
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in __init__
AttributeError: 'instancemethod' object has no attribute 'bar'

Python 声称没有 bar,即使它在前一行打印得很好。

如果我们尝试直接在类上更改它,则会发生同样的错误:

>>> A.foo.bar
123
>>> A.foo.bar = 345  # KABOOM!
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'instancemethod' object has no attribute 'bar'

这里发生了什么,即为什么我们会看到这种行为?

有没有办法在创建类后设置函数的属性?

(我知道有多种选择,但我很想知道这里方法的属性,或者可能是更广泛的问题。)


动机:Django 利用了在方法上设置属性的可能性,例如:

class MyModelAdmin(ModelAdmin):
    ...

    def custom_admin_column(self, obj):
        return obj.something()
    custom_admin_column.admin_order_field ='relation__field__span'
    custom_admin_column.allow_tags = True

最佳答案

在类体内设置 foo.bar 是可行的,因为 foo 是实际的 foo 函数。但是,当您这样做时

self.foo.bar = 456

self.foo 不是那个函数。 self.foo 是一个实例方法对象,在您访问它时按需创建。由于多种原因,您无法为其设置属性:

  1. 如果这些属性存储在 foo 函数中,那么分配给 a.foo.bar 会对 b.foo.bar 产生意想不到的影响,这与对属性分配的所有常见预期相反。
  2. 如果这些属性存储在 self.foo 实例方法对象中,它们将不会在您下次访问 self.foo 时显示,因为下次您将获得一个新的实例方法对象。
  3. 如果这些属性存储在 self.foo 实例方法对象上,并且您更改规则,使 self.foo 始终是同一个对象,那么这会使 Python 中的每个对象大量膨胀以存储一堆您几乎不需要的实例方法对象。<
  4. 如果这些属性存储在 self.__dict__ 中,那么没有 __dict__ 的对象呢?此外,您需要想出某种名称修改规则,或将非字符串键存储在 self.__dict__ 中,这两者都有各自的问题。

如果你想在类定义完成后在 foo 函数上设置属性,你可以使用 A.__dict__['foo'].bar = 456 来实现。 (我已经使用 A.__dict__ 来绕过 A.foo 是函数还是未绑定(bind)方法对象的问题,这取决于您的 Python 版本。如果 A 继承了 foo ,您将不得不处理该问题或访问它继承 foo 的类。)

关于python - 创建类后在方法上设置属性会引发 "' instancemethod' object has no attribute“但属性显然存在,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36749310/

相关文章:

python - Python 中的 Monte Carlo 和 Metropolis 算法非常慢

Java 在构造函数之前调用方法

作为一流函数的 Ruby 匿名类

ruby - 为什么 Ruby 1.9 lambda 调用不可能没有圆括号前面的点?

python - 拆分 RDD

python - 将一个列表的内容循环弹出到列表列表中

python - 如何使用mockito模拟os.path.join

多个类中的 Java 按钮操作

java - Android 简单方法不起作用?

python - 记录一流分配的功能