0

What I would like to do there is declaring class variables, but actually use them as vars of the instance. I have a class Field and a class Thing, like this:

class Field(object):
    def __set__(self, instance, value):
        for key, v in vars(instance.__class__).items():
            if v is self:
                instance.__dict__.update({key: value})

    def __get__(self, instance, owner):
        for key, v in vars(instance.__class__).items():
            if v is self:
                try:
                    return instance.__dict__[key]
                except:
                    return None


class Thing(object):
    foo = Field()

So when I instantiate a thing and set attribute foo, it will be added to the instance, not the class, the class variable is never actually re-set.

new = Thing()
new.foo = 'bar'
# (foo : 'bar') is stored in new.__dict__

This works so far, but the above code for Field is rather awkward. It has too look for the Field object instance in the classes props, otherwise there seems no way of knowing the name of the property (foo) in __set__ and __get__. Is there another, more straight forward way to accomplish this?

6
  • This is 2.7, vars() is similar to dict Commented Jun 17, 2012 at 12:59
  • 4
    Out of interest, what possible use does this have? Commented Jun 17, 2012 at 13:01
  • It just returns the __dict__, in some situations I think it tends to look cleaner. Commented Jun 17, 2012 at 13:21
  • @Eelke By all means, it's much nicer than accessing __dict__ directly. I'm still wondering what use you have for this though. Commented Jun 17, 2012 at 13:35
  • What's wrong with just setting attributes on the instance in __init__? Commented Jun 17, 2012 at 15:10

3 Answers 3

2

Every instance of Field (effectively) has a name. Its name is the attribute name (or key) which references it in Thing. Instead of having to look up the key dynamically, you could instantiate Fields with the name at the time the class attribute is set in Thing:

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

    def __set__(self, instance, value):
        instance.__dict__.update({self.name: value})

    def __get__(self, instance, owner):
        if instance is None:
            return self
        try:
            return instance.__dict__[self.name]
        except KeyError:
            return None

def make_field(*args):
    def wrapper(cls):
        for arg in args:
            setattr(cls, arg, Field(arg))
        return cls
    return wrapper

@make_field('foo')
class Thing(object):
    pass

And it can be used like this:

new = Thing()

Before new.foo is set, new.foo returns None:

print(new.foo)
# None

After new.foo is set, 'foo' is an instance attribute of new:

new.foo = 'bar'
print(new.__dict__)
# {'foo': 'bar'}

You can access the descriptor (the Field instance itself) with Thing.foo:

print(Thing.foo)
# <__main__.Field object at 0xb76cedec>

PS. I'm assuming you have a good reason why

class Thing(object):
    foo = None

does not suffice.

Sign up to request clarification or add additional context in comments.

1 Comment

@Eelke: Without the class decorator, you would have to write foo = Field('foo'). This forces you to repeat foo twice. I used the class decorator solely so that you do not have to repeat yourself.
2

Reread your question and realized I had it wrong:

You don't need to override the default python behavior to do this. For example, you could do the following:

class Thing(object):
    foo = 5

>>> r = Thing()
>>> r.foo = 10
>>> s = Thing()
>>> print Thing.foo
5
>>> print r.foo
10
>>> print s.foo
5

If you want the default to be 'None' for a particular variable, you could just set the class-wide value to be None. That said, you would have to declare it specifically for each variable.

2 Comments

@Lattyware , edited my answer to (hopefully) answer the actual question.
OK, I removed my -1, although I'm pretty sure the OP wanted to do this without doing it this way, I'm not really sure why though.
1

The easiest way would be to call the attribute something else than the name of the descriptor variable - preferably starting with _ to signal its an implementation detail. That way, you end up with:

def __set__(self, instance, value):
    instance._foo = value

def __get__(self, instance, owner):
    return getattr(instance, '_foo', None)

The only drawback of this is that you can't determine the name of the key from the one used for the descriptor. If that increased coupling isn't a problem compared to the loop, you could just use a property:

class Thing:
    @property
    def foo(self):
       return getattr(self, '_foo', None)

    @foo.setter
    def foo(self, value):
       self._foo = value

otherwise, you could pass the name of the variable into the descriptor's __init__, so that you have:

class Thing:
    foo = Field('_foo')

Of course, all this assumes that the simplest and most Pythonic way - use a real variable Thing().foo that you set to None in Thing.__init__ - isn't an option for some reason. If that way will work for you, you should prefer it.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.