Class Variable Access and Assignment

Bengt Richter bokr at oz.net
Fri Nov 4 19:25:34 EST 2005


On Fri, 04 Nov 2005 02:59:35 +1100, Steven D'Aprano <steve at REMOVETHIScyber.com.au> wrote:

>On Thu, 03 Nov 2005 14:13:13 +0000, Antoon Pardon wrote:
>
>> Fine, we have the code:
>> 
>>   b.a += 2
>> 
>> We found the class variable, because there is no instance variable,
>> then why is the class variable not incremented by two now?
Because the class variable doesn't define a self-mutating __iadd__
(which is because it's an immutable int, of course). If you want
b.__dict__['a'] += 2 or b.__class__.__dict__['a'] += 2 you can
always write it that way ;-)

(Of course, you can use a descriptor to define pretty much whatever semantics
you want, when it comes to attributes).

>
>Because b.a += 2 expands to b.a = b.a + 2. Why would you want b.a =

No, it doesn't expand like that. (Although, BTW, a custom import could
make it so by transforming the AST before compiling it ;-)

Note BINARY_ADD is not INPLACE_ADD:

 >>> def foo(): # for easy disassembly
 ...     b.a += 2
 ...     b.a = b.a + 2
 ...
 >>> import dis
 >>> dis.dis(foo)
   2           0 LOAD_GLOBAL              0 (b)
               3 DUP_TOP
               4 LOAD_ATTR                1 (a)
               7 LOAD_CONST               1 (2)
              10 INPLACE_ADD
              11 ROT_TWO
              12 STORE_ATTR               1 (a)

   3          15 LOAD_GLOBAL              0 (b)
              18 LOAD_ATTR                1 (a)
              21 LOAD_CONST               1 (2)
              24 BINARY_ADD
              25 LOAD_GLOBAL              0 (b)
              28 STORE_ATTR               1 (a)
              31 LOAD_CONST               0 (None)
              34 RETURN_VALUE

And BINARY_ADD calls __add__ and INPLACE_ADD calls __iadd__ preferentially.

About __ixxx__:
"""
These methods are called to implement the augmented arithmetic operations
(+=, -=, *=, /=, %=, **=, <<=, >>=, &=, ^=, |=).
These methods should attempt to do the operation in-place (modifying self)
and return the result (which could be, but does not have to be, self).
If a specific method is not defined, the augmented operation falls back
to the normal methods. For instance, to evaluate the expression x+=y,
where x is an instance of a class that has an __iadd__() method,
x.__iadd__(y) is called. If x is an instance of a class that does not define
a __iadd() method, x.__add__(y) and y.__radd__(x) are considered, as with
the evaluation of x+y. 
"""


><something> to correspond to b.__class__.a = <something>?
>
>I'm not saying that it couldn't, if that was the model for inheritance you
>decided to use. I'm asking why would you want it? What is your usage case
>that demonstrates that your preferred inheritance model is useful?

It can be useful to find-and-rebind (in the namespace where found) rather
than use separate rules for finding (or not) and binding. The tricks for
boxing variables in closures show there is useful functionality that
is still not as convenient to "spell" as could be imagined.
It is also useful to find and bind separately. In fact, IMO it's not
separate enough in some cases ;-)

I've wanted something like
    x := expr
to spell "find x and rebind it to expr" (or raise NameError if not found).
Extending that to attributes and augassign,
    b.a +:= 2
could mean find the "a" attribute, and in whatever attribute dict it's found,
rebind it there. Or raise an Exception for whatever failure is encountered.
This would be nice for rebinding closure variables as well. But it's been discussed,
like most of these things ;-)

Regards,
Bengt Richter



More information about the Python-list mailing list