thomas
thomas

Reputation: 1885

Controlling getter and setter for a python's class

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 !

Upvotes: 6

Views: 8062

Answers (6)

ilya n.
ilya n.

Reputation: 18826

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.

Upvotes: 0

Chris Ciesielski
Chris Ciesielski

Reputation: 1203

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.

Upvotes: 2

Martin v. Löwis
Martin v. Löwis

Reputation: 127587

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.

Upvotes: 3

Steve Gilham
Steve Gilham

Reputation: 11277

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

Upvotes: 1

Ferran
Ferran

Reputation: 15053

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.

Upvotes: 2

Lennart Regebro
Lennart Regebro

Reputation: 172437

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

Upvotes: 3

Related Questions