tags:

views:

314

answers:

6

Hi there ! Consider the following class :

class Token:
    def __init__(self):
        self.d_dict = {}

    def __setattr__(self, s_name, value):
        self.d_dict[s_name] = value

    def __getattr__(self, s_name):
        if s_name in self.d_dict.keys():
            return self.d_dict[s_name]
        else:
            raise AttributeError('No attribute {0} found !'.format(s_name))

In my code Token have some other function (like get_all() wich return d_dict, has(s_name) which tell me if my token has a particular attribute).

Anyway, I think their is a flaw in my plan since it don't work : when I create a new instance, python try to call __setattr__('d_dict', '{}').

How can I achieve a similar behaviour (maybe in a more pythonic way ?) without having to write something like Token.set(name, value) and get(name) each I want to set or get an attribute for a token.

Critics about design flaw and/or stupidity welcome :)

Thank !

+1  A: 

You need to special-case d_dict.

Although of course, in the above code, all you do is replicate what any object does with __dict__ already, so it's pretty pointless. Do I guess correctly if you intended to special case some attributes and actally use methods for those?

In that case, you can use properties.

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x
Lennart Regebro
+1  A: 

A solution, not very pythonic but works. As Lennart Regebro pointed, you have to use a special case for d_dict.

class Token(object):

    def __init__(self):
        super(Token,self).__setattr__('d_dict', {})

    def __getattr__(self,name):
        return self.a[name]

    def __setattr__(self,name,value):
        self.a[name] = value

You need to use new style classes.

Ferran
A: 

It's worth remembering that __getattr__ is only called if the attribute doesn't exist in the object, whereas __setattr__ is always called.

Steve Gilham
+3  A: 

The special-casing of __dict__ works like this:

def __init__(self):
    self.__dict__['d_dict'] = {}

There is no need to use a new-style class for that.

Martin v. Löwis
I think I'll do that :)Anyway, I use Python 3.1 so all my class are new style class .
thomas
+2  A: 

Hi, the problem seems to be in time of evaluation of your code in __init__ method. You could define __new__ method and initialize d_dict variable there instead of __init__. Thats a bit hackish but it works, remember though to comment it as after few months it'll be total magic.

>>> class  Foo(object):
...     def __new__(cls, *args):
...             my_cls = super(Foo, cls).__new__(cls, *args)
...             my_cls.d_dict = {}
...             return my_cls

>>> f = Foo()
>>> id(f.d_dict)
3077948796L
>>> d = Foo()
>>> id(d.d_dict)
3078142804L

Word of explanation why I consider that hackish: call to __new__ returns new instance of class so then d_dict initialised in there is kind of static, but it's initialised with new instance of dictionary each time class is "created" so everything works as you need.

Chris Ciesielski
A: 

I think we'll be able to say something about the overall design of your class if you explain its purpose. For example,

# This is a class that serves as a dictionary but also has user-defined methods
class mydict(dict): pass

# This is a class that allows setting x.attr = value or getting x.attr:
class mysetget: pass

# This is a class that allows setting x.attr = value or getting x.attr:
class mygetsethas: 
    def has(self, key):
        return key in self.__dict__

x = mygetsethas()
x.a = 5
print(x.has('a'), x.a)

I think the last class is closest to what you meant, and I also like to play with syntax and get lots of joy from it, but unfortunately this is not a good thing. Reasons why it's not advisable to use object attributes to re-implement dictionary: you can't use x.3, you conflict with x.has(), you have to put quotes in has('a') and many more.

ilya n.
My goal is to provide a container that allow me to give it any arbitrary attribute (I don't in advance how many nor their name), eg : myClassInstance.name1 = value1myClassInstance.name2 = value2With the appropriate call, I want to retrieve a dictionary that will contain my attributes' name and value under the following form :{'name1': value1, 'name2': value2}valueX can be any python's or custom's type.
thomas
Then your best best is the second one, `class mysetget: pass`. You'll be able to set attributes and then retrieve them with 'x.__dict__`.
ilya n.