is it possible to give an instance a value?

Steven D'Aprano steve at REMOVEME.cybersource.com.au
Tue Mar 6 22:21:22 EST 2007


On Tue, 06 Mar 2007 14:45:45 -0800, manstey wrote:

> class Test(object):
>      def __init__(self, val):
>            self.val = val
> 
>>>> a = Test('hello')
>>>> a.val  + ' happy'
> 'hello happy'
>>>> a + 'happy'
> TypeError: unsupported operand type(s) for +: 'Test' and 'str'
> 
> Is there a way to make a have the value a.val when it is used as
> above, or as an argument (eg function(a, 10, 'sdf') etc)?

That's kinda-sorta like "delegation", which is an alternative to
sub-classing. Whether you subclass string or delegate to a string, the end
result will be the same, only the implementation will be different.

However, while sub-classing str will give you an immutable object,
delegation can give you a mutable string. See UserString.MutableString for
details -- but beware that this is significantly slower than ordinary
strings.

Honestly, once you've been using immutable strings for a while, you'll
rarely care that they aren't mutable.



> The only fudge I discovered for simple addition was to add to the
> class
> 
>    def __add__(self, obj):
>        return a.val + obj
> 
> but this doesn't solve the problem in general. I have tried
> subclassing the string type, but as it is immutable, this is not
> flexible the way a.val is (i.e. it can't e reassigned and remain a
> subclass).

Because of the way Python works, you can't really expect this to work:

>>> s = MyClass("something") # s is assigned an instance of MyClass
>>> s = "ordinary string" # s magically remains a MyClass instance

Python just doesn't work that way. The name "s" can't force the object
"ordinary string" to be some other type. If you want "s" to have an
instance of MyClass assigned to it, you have to do it explicitly:

>>> s = MyClass("ordinary string") # not ordinary any more


If you are willing to write s.data instead of s, then you can make the
data attribute a property and automate most of that.


class MyString(str):
    pass # customise the behaviour you want to see

class MyClass(object):
    def __init__(self, data):
        self.data = data
    def _getter(self):
        return self._data
    def _setter(self, data):
        self._data = MyString(data)
    data = property(_getter, _setter, None, None)


Now you can do this:

>>> inst = MyClass("hello world")
>>> inst.data += "!"
>>> type(inst.data)
<class '__main__.MyString'>
>>> inst.data = "Nobody expects the Spanish Inquisition!"
>>> type(inst.data)
<class '__main__.MyString'>

But this doesn't work:

>>> inst = "dead parrot"
>>> type(inst) == MyClass # what we want to be True
False


But chances are that all of that scaffolding (delegation, properties,
etc.) would be unnecessary if you just change the way you do things. E.g.
instead of your example:

>>> Person.Address
'Sydney'
>>> Person.Address.type
'%String'

Python can do this:

>>> Person.Address
'Sydney'
>>> type(Person.Address)
<type 'str'>

If you sub-classed str like this:

class MyString(object):
    def __init__(self, data, type="%String"):
        self.data = data
        self.type = type

hoping to use it like this:

>>> fred = Person("Address", "Sydney", "%String") # or something...
>>> fred.Address
'Sydney'
>>> fred.Address.type
'%String'

then you're just making work for yourself.


Hope this makes sense.


-- 
Steven D'Aprano 




More information about the Python-list mailing list