views:

115

answers:

3

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"
+3  A: 

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.

nosklo
OK, I can live with that
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 = dataOwner.a.foo()How does foo get the state info from Owner?
@unknown (google): oh, for that you store the data on the instance itself. So you can fetch it from the methods.
nosklo
I don't quite follow you, can you elaborate on that just a bit?
@unknown (google): def __set__(self, instance, value): instance._value = value
nosklo
A: 

I could use a class factory to pop out a new Owner class every time I need one. This might work for my application because I only need a few separate instances of the Owner class. I modified the tests to take the class factory function instead of a class. And I changed the definition of A to show how it maintains a copy of the Owner's state.

class A(object):
    def __init__(self, state):
        self.state = state
    def __set__(self, instance, value):
        print "__set__ called: ", value

def fact(state):
    class D(object):
        a = A(state)
    return D

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

def test2( classf_in ):
    o1, o2 = classf_in("state zero")(), classf_in("state one")()
    if o1.a is o2.a:
        print "fail"
    else:
        print "pass"

Is there a way to define class D using only __new__ and __init__ overrides?

that's not an answer. ask another question or edit your question.
nosklo
Sorry, I disagree. I think this answer might help others better understand this feature in python.
A: 

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
that's a class factory function, disguised as a class
nosklo
Yes it is, and proud of it.