4

在 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
4

1 回答 1

1

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函数或未绑定方法对象的问题,这取决于您的 Python 版本。如果A继承foo,您将不得不处理该问题或访问它继承的类的foo字典.)

于 2016-04-20T16:48:37.430 回答