Why does setattr fail on a bound method

后端 未结 2 1640
臣服心动
臣服心动 2020-12-03 03:48

In the following, setattr succeeds in the first invocation, but fails in the second, with:

AttributeError: \'method\' object has no attribute \'         


        
相关标签:
2条回答
  • 2020-12-03 03:55

    Q: "Is there a way of setting an attribute on a method such that it will only exist on one instance, not for each instance of the class?"

    A: Yes:

    class c:
    
        def m(self):
    
            print(type(c.m))
            setattr(c.m, 'i', 0)
    
            print(type(self))
            setattr(self, 'i', 0)
    

    The static variable on functions in the post you link to is not useful for methods. It sets an attribute on the function so that this attribute is available the next time the function is called, so you can make a counter or whatnot.

    But methods have an object instance associated with them (self). Hence you have no need to set attributes on the method, as you simply can set it on the instance instead. That is in fact exactly what the instance is for.

    The post you link to shows how to make a function with a static variable. I would say that in Python doing so would be misguided. Instead look at this answer: What is the Python equivalent of static variables inside a function?

    That is the way to do it in Python in a way that is clear and easily understandable. You use a class and make it callable. Setting attributes on functions is possible and there are probably cases where it's a good idea, but in general it will just end up confusing people.

    0 讨论(0)
  • 2020-12-03 04:15

    The short answer: There is no way of adding custom attributes to bound methods.

    The long answer follows.

    In Python, there are function objects and method objects. When you define a class, the def statement creates a function object that lives within the class' namespace:

    >>> class c:
    ...     def m(self):
    ...         pass
    ...
    >>> c.m
    <function m at 0x025FAE88>
    

    Function objects have a special __dict__ attribute that can hold user-defined attributes:

    >>> c.m.i = 0
    >>> c.m.__dict__
    {'i': 0}
    

    Method objects are different beasts. They are tiny objects just holding a reference to the corresponding function object (__func__) and one to its host object (__self__):

    >>> c().m
    <bound method c.m of <__main__.c object at 0x025206D0>>
    >>> c().m.__self__
    <__main__.c object at 0x02625070>
    >>> c().m.__func__
    <function m at 0x025FAE88>
    >>> c().m.__func__ is c.m
    True
    

    Method objects provide a special __getattr__ that forwards attribute access to the function object:

    >>> c().m.i
    0
    

    This is also true for the __dict__ property:

    >>> c().m.__dict__['a'] = 42
    >>> c.m.a
    42
    >>> c().m.__dict__ is c.m.__dict__
    True
    

    Setting attributes follows the default rules, though, and since they don't have their own __dict__, there is no way to set arbitrary attributes.

    This is similar to user-defined classes defining __slots__ and no __dict__ slot, when trying to set a non-existing slot raises an AttributeError (see the docs on __slots__ for more information):

    >>> class c:
    ...     __slots__ = ('a', 'b')
    ...
    >>> x = c()
    >>> x.a = 1
    >>> x.b = 2
    >>> x.c = 3
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'c' object has no attribute 'c'
    
    0 讨论(0)
提交回复
热议问题