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

P

Phillip B Oldham

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.
 
C

Chris Rebert

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
 
P

Phillip B Oldham

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.
 
C

Chris Rebert

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
 
M

Michael Hartl

Phillip said:
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.
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?
 
S

Steven D'Aprano

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.
 
B

Bruno Desthuilliers

Chris Rebert a écrit :
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.
 
B

Bruno Desthuilliers

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.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top