[Tutor] MutableString/Class variables

Oscar Benjamin oscar.j.benjamin at gmail.com
Thu May 9 15:47:23 CEST 2013


On 9 May 2013 14:16, Albert-Jan Roskam <fomcl at yahoo.com> wrote:
>
>
>> Subject: Re: [Tutor] MutableString/Class variables
>>
>> On 05/09/2013 08:10 AM, Albert-Jan Roskam wrote:
>>>  Hello,
>>>
>>>  I was just playing a bit with Python and I wanted to make a mutable string,
>> that supports item assignment. Is the way below the way to do this?
>>>  The part I am not sure about is the class variable. Maybe I should also
>> have reimplemented __init__, then call super inside this and add an updated
>> version of "self" so __repr__ and __str__ return that. More general:
>> when should class variables be used? I am reading about Django nd there they are
>> all over the place, yet I have always had the impression their use is not that
>> common.

Django code often uses classes to hold collections of essentially
static data. An example from the docs:

from django.db import models

class Poll(models.Model):
    question = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

class Choice(models.Model):
    poll = models.ForeignKey(Poll)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

In the code above the class attributes are used to customise the
behaviour of the models.Model class. This is quite nicely presented
compared with more typical ways of doing it e.g.:

Poll = models.Model(question = models.CharField(max_length=200)
                    pub_date = models.DateTimeField('date published'))

or perhaps something like

Poll = models.Model()
Poll.add(question = models.CharField(max_length=200))
Poll.add(pub_date = models.DateTimeField('date published'))



>>>
>>
>> You use a class attribute (not class variable) whenever you want to have
>> exactly one such value for the entire program.  If you want to be able
>> to manipulate more than one, then you use an instance attribute,
>> typically inside the __init__() method.
>
> Hmmm, so is it fair to say that this is the OOP equivalent of 'nonlocal' in Python 3? It has meaning inside the entire scope of the class, not outside it, there not 'global'.

Class attributes can be accessed from a class or an instance whereas
instance attributes are only accessible from an instance:

>>> class A:
...   class_attribute = 1
...   def __init__(self):
...     self.instance_attribute = 2
...
>>> a = A()
>>> a.instance_attribute
2
>>> a.class_attribute
1
>>> A.class_attribute
1
>>> A.instance_attribute
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: class A has no attribute 'instance_attribute'


>
> Here's a modified version; in my previous attempts I messed up the arguments of super(). ;-) This feels better. But then again, I never *really* understood __new__ entirely.
>
> class MutableStr(str):

You don't want to inherit from str. You want a mutable string and if
you inherit from str you will awlays be extending an immutable builtin
object. All of the inherited string methods will use that immutable
data and ignore any extra data that you associate with your instances
(or your class). This means that there is no point in inheriting them.
Otherwise you should override all of them since your class would have
a load of methods that do the wrong thing.

What happens if you have an instance of your class, mutate it and then
call its join method? What happens if you create two equal instances,
mutate them to be different and try to use both as dictionary keys? Or
if you compare them with a==b?

>     def __init__(self, s):
>         super(str, MutableStr).__init__(s)
>         self.s = s

If you're storing the string as an attribute there's really no point
in inheriting from str.

>     def __repr__(self):
>         return self.s
>     def __str__(self):
>         return self.s

You don't need both of __repr__ and __str__ if they just do the same thing.

>     def __setitem__(self, key, item):
>         self.s = self[:key] + item + self[key+1:]

You should probably also have __setslice__, __getslice__, __getitem__,
and so on.

> # produce results as intended
> mstr = MutableStr("01234X678")
> mstr[5] = "&"
> print str(mstr)
> print unicode(mstr)
> mstr = MutableStr("01234X678")
> mstr[8] = "&"
> print str(mstr)
> print unicode(mstr)

What about "print mstr[8]" (you need to override __getitem__)?

> # output:
> 01234&678
> 01234&678
> 01234X67&
> 01234X67&

By the way since you're using Python 2 your strings are all byte
strings so you can get a mutable string easily using a bytearray:

>>> b = bytearray('hello')
>>> b
bytearray(b'hello')
>>> b[2] = 'r'
>>> b
bytearray(b'herlo')
>>> bytearray
<type 'bytearray'>

Note that bytearray already implements all of the string methods:

>>> dir(bytearray)
['__add__', '__alloc__', '__class__', '__contains__', '__delattr__',
'__delitem__', '__doc__', '__eq__', '__format__', '__ge__',
'__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__',
'__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__',
'__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__',
'__str__', '__subclasshook__', 'append', 'capitalize', 'center',
'count', 'decode', 'endswith', 'expandtabs', 'extend', 'find',
'fromhex', 'index', 'insert', 'isalnum', 'isalpha', 'isdigit',
'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower',
'lstrip', 'partition', 'pop', 'remove', 'replace',
'reverse', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit',
'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase',
'title', 'translate', 'upper', 'zfill']

All you need to do is override __str__ and this will look just like a
mutable string.


Oscar


More information about the Tutor mailing list