Why does declaring a descriptor class in the __init__ function break the descriptor functionality?
Asked Answered
C

3

10

In class B below I wanted the __set__ function in class A to be called whenever you assign a value to B().a . Instead, setting a value to B().a overwrites B().a with the value. Class C assigning to C().a works correctly, but I wanted to have a separate instance of A for each user class, i.e. I don't want changing 'a' in one instance of C() to change 'a' in all other instances. I wrote a couple of tests to help illustrate the problem. Can you help me define a class that will pass both test1 and test2?

class A(object):
    def __set__(self, instance, value):
        print "__set__ called: ", value

class B(object):
    def __init__(self):
        self.a = A()

class C(object):
    a = A()

def test1( class_in ):
    o = class_in()
    o.a = "test"
    if isinstance(o.a, A):
        print "pass"
    else:
        print "fail"

def test2( class_in ):
    o1, o2 = class_in(), class_in()
    if o1.a is o2.a:
        print "fail"
    else:
        print "pass"
Clausen answered 16/6, 2009 at 21:55 Comment(0)
Z
5

Accordingly to the documentation:

The following methods only apply when an instance of the class containing the method (a so-called descriptor class) appears in the class dictionary of another new-style class, known as the owner class. In the examples below, “the attribute” refers to the attribute whose name is the key of the property in the owner class’ __dict__. Descriptors can only be implemented as new-style classes themselves.

So you can't have descriptors on instances.

However, since the descriptor gets a ref to the instance being used to access it, just use that as a key to storing state and you can have different behavior depending on the instance.

Zoie answered 16/6, 2009 at 22:29 Comment(4)
On second thought, maybe I can't live with that. In my application I want to put member functions in A that also depend on the state of the owner class. So for example: Owner(state info) Owner.a = data Owner.a.foo() How does foo get the state info from Owner?Clausen
@unknown (google): oh, for that you store the data on the instance itself. So you can fetch it from the methods.Zoie
I don't quite follow you, can you elaborate on that just a bit?Clausen
@unknown (google): def __set__(self, instance, value): instance._value = valueZoie
C
0

Here's a class that can pass the original tests, but don't try using it in most situations. it fails the isinstance test on itself!

class E(object):
    def __new__(cls, state):
        class E(object):
            a = A(state)
            def __init__(self, state):
                self.state = state
        return E(state)

#>>> isinstance(E(1), E)
#False
Clausen answered 17/6, 2009 at 0:12 Comment(1)
that's a class factory function, disguised as a classZoie
B
0

I was bitten by a similar issue in that I wanted to class objects with attributes governed by a descriptor. When I did this, I noticed that the attributes were being overwritten in all of the objects such that they weren't individual.

I raised a SO question and the resultant answer is here: class attribute changing value for no reason

A good document link discussing descriptors is here: http://martyalchin.com/2007/nov/24/python-descriptors-part-2-of-2/

An example descriptor from the aforementioned link is below:

class Numberise(object):
    def __init__(self, name):
        self.name = name

    def __get__(self, instance, owner):
        if self.name not in instance.__dict__:
            raise (AttributeError, self.name)
        return '%o'%(instance.__dict__[self.name])

    def __set__(self, instance, value):
        print ('setting value to: %d'%value)
        instance.__dict__[self.name] = value
Bouleversement answered 8/3, 2014 at 2:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.