Can I run an operation on an object's attribute when reading?

Discussion in 'Python' started by Phillip B Oldham, Jan 19, 2009.

  1. Is it possible to automatically run an operation on a object's
    attribute when reading? For instance, if I have the following:

    class Item(object):
    tags = ['default','item']

    item = Item()

    desc = item.tags

    When I'm reading the item.tags, I'd like to automagically have the
    value converted to a string, eg: "default item". I know I could write
    a getter to do this for me, but I'd like to avoid that if possible on
    this occasion.

    Any help would be great! Thanks.
    Phillip B Oldham, Jan 19, 2009
    #1
    1. Advertising

  2. Phillip B Oldham

    Chris Rebert Guest

    On Mon, Jan 19, 2009 at 4:04 AM, Phillip B Oldham
    <> wrote:
    > Is it possible to automatically run an operation on a object's
    > attribute when reading? For instance, if I have the following:
    >
    > class Item(object):
    > tags = ['default','item']
    >
    > item = Item()
    >
    > desc = item.tags
    >
    > When I'm reading the item.tags, I'd like to automagically have the
    > value converted to a string, eg: "default item". I know I could write
    > a getter to do this for me, but I'd like to avoid that if possible on
    > this occasion.


    Assuming I'm interpreting you correctly (you're going to have to use
    something like a getter):

    class Item(object):
    def __init__(self):
    self._tags = ['default', 'item']

    @property
    def tags(self):
    return ' '.join(self._tags)

    print Item().tags #==> default item

    Cheers,
    Chris

    --
    Follow the path of the Iguana...
    http://rebertia.com
    Chris Rebert, Jan 19, 2009
    #2
    1. Advertising

  3. On Mon, Jan 19, 2009 at 12:15 PM, Chris Rebert <> wrote:
    > Assuming I'm interpreting you correctly (you're going to have to use
    > something like a getter):


    Thanks, but I'm looking for a way to do it *without* using a getter as
    I don't have easy access to the class (its being generated for me
    elsewhere). Essentially I'd like to overwrite (if possible) the
    default behavior when returning certain attributes on certain objects.
    Phillip B Oldham, Jan 19, 2009
    #3
  4. Phillip B Oldham

    Chris Rebert Guest

    On Mon, Jan 19, 2009 at 4:22 AM, Phillip B Oldham
    <> wrote:
    > On Mon, Jan 19, 2009 at 12:15 PM, Chris Rebert <> wrote:
    >> Assuming I'm interpreting you correctly (you're going to have to use
    >> something like a getter):

    >
    > Thanks, but I'm looking for a way to do it *without* using a getter as
    > I don't have easy access to the class (its being generated for me
    > elsewhere). Essentially I'd like to overwrite (if possible) the
    > default behavior when returning certain attributes on certain objects.


    To my knowledge, you can't really "overwrite" that behavior without
    editing the class (or how it's generated).
    The next closest thing would be to write a proxy class that defines a
    __getattr__ method implementing the semantics you want.

    Cheers,
    Chris

    --
    Follow the path of the Iguana...
    http://rebertia.com
    Chris Rebert, Jan 19, 2009
    #4
  5. Phillip B Oldham schrieb:
    > On Mon, Jan 19, 2009 at 12:15 PM, Chris Rebert <> wrote:
    >
    >> Assuming I'm interpreting you correctly (you're going to have to use
    >> something like a getter):
    >>

    >
    > Thanks, but I'm looking for a way to do it *without* using a getter as
    > I don't have easy access to the class (its being generated for me
    > elsewhere). Essentially I'd like to overwrite (if possible) the
    > default behavior when returning certain attributes on certain objects.
    > --
    > http://mail.python.org/mailman/listinfo/python-list
    >

    You could still add the getter to the class after it has been defined if
    that's your only problem with
    using a getter:

    class Item(object):
    tags = ['default','item']

    @property
    def tags(self):
    return ' '.join(self.tags)

    setattr(Item, "Tags", tags)

    print Item().Tags #==> default item


    But I don't think there's a way to do it without a different name (here
    "tags" - "Tags"), is there?
    Michael Hartl, Jan 19, 2009
    #5
  6. On Mon, 19 Jan 2009 04:04:23 -0800, Phillip B Oldham wrote:

    > Is it possible to automatically run an operation on a object's attribute
    > when reading? For instance, if I have the following:
    >
    > class Item(object):
    > tags = ['default','item']
    >
    > item = Item()
    >
    > desc = item.tags
    >
    > When I'm reading the item.tags, I'd like to automagically have the value
    > converted to a string, eg: "default item". I know I could write a getter
    > to do this for me, but I'd like to avoid that if possible on this
    > occasion.


    When all you have is a hammer, everything looks like a nail... Why do you
    have to read item.tags directly? Just write a function and call it
    instead of direct attribute access. This is the simplest, easiest
    solution with the fewest side-effects.

    def get_tags(item):
    return ' '.join(item.tags)

    desc = get_tags(item)


    If you don't like that solution, monkey-patch the class with a getter:

    @property
    def tags(self):
    return " ".join(self._tags)

    Item._tags = Item.tags
    Item.tags = tags

    This is dangerous, because other classes may expect Item.tags to be a
    list. Better to create your own accessor method:

    def get_tags(self):
    return " ".join(self.tags)

    Item.get_tags = get_tags

    item = Item()
    desc = item.get_tags()


    If you don't like monkey-patching (and you probably shouldn't), then
    write a proxy class that delegates to the original:


    _Item = Item
    class Item(object):
    def __init__(self, *args):
    self.__dict__['_item'] = _Item(*args)
    @property
    def tags(self):
    return ' '.join(self._item.tags)
    def __getattr__(self, attr):
    return getattr(self._item, attr)
    def __setattr__(self, attr, value):
    setattr(self._item, attr, value)


    I dare say there's a metaclass solution too, but I'm not crazy enough to
    do that.



    --
    Steven
    Steven D'Aprano, Jan 19, 2009
    #6
  7. Chris Rebert a écrit :
    > On Mon, Jan 19, 2009 at 4:22 AM, Phillip B Oldham
    > <> wrote:
    >> On Mon, Jan 19, 2009 at 12:15 PM, Chris Rebert <> wrote:
    >>> Assuming I'm interpreting you correctly (you're going to have to use
    >>> something like a getter):

    >> Thanks, but I'm looking for a way to do it *without* using a getter as
    >> I don't have easy access to the class (its being generated for me
    >> elsewhere). Essentially I'd like to overwrite (if possible) the
    >> default behavior when returning certain attributes on certain objects.

    >
    > To my knowledge, you can't really "overwrite" that behavior without
    > editing the class (or how it's generated).


    Assuming it's really about a *class* object (not instances of it), *and*
    it's a new-style class, it is possible:
    class Foo(object):
    def __init__(self):
    self.tags = ['foo', 'bar']

    def wrap_tag_access(cls):
    def fset(obj, value):
    obj._tags = value

    def fget(obj):
    print "do something here"
    return obj._tags

    cls.tags = property(fset=fset, fget=fget)
    return cls

    Foo = wrap_tag_access(Foo)
    f = Foo()
    print f.tags




    > The next closest thing would be to write a proxy class that defines a
    > __getattr__ method implementing the semantics you want.


    If it's a "classic" class, then yes, it's probably the best thing to do.
    Bruno Desthuilliers, Jan 19, 2009
    #7
  8. Steven D'Aprano a écrit :
    (snip)

    > When all you have is a hammer, everything looks like a nail... Why do you
    > have to read item.tags directly? Just write a function and call it
    > instead of direct attribute access.


    A sensible advice, but only relevant if this class instances are only
    used by code the OP do control. Which may or not be the case.
    Bruno Desthuilliers, Jan 19, 2009
    #8
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. david ullua
    Replies:
    13
    Views:
    640
  2. raan
    Replies:
    2
    Views:
    435
  3. Marc Aymerich
    Replies:
    0
    Views:
    64
    Marc Aymerich
    Nov 24, 2013
  4. Devin Jeanpierre
    Replies:
    12
    Views:
    100
    Steven D'Aprano
    Nov 24, 2013
  5. Chris Angelico
    Replies:
    0
    Views:
    56
    Chris Angelico
    Nov 24, 2013
Loading...

Share This Page