You are not logged in.

#1 2008-07-13 06:54:34

vsk
Member
Registered: 2008-05-27
Posts: 70

[Solved] Mutating Strings Within A Class (Python)

[Edit: Finished code at the end of this post.]
Hey guys!,

I'm in the middle of writing a class that allows for more flexible string usage in Python (yes, it's possible).
Although Python implements strings extremely well, it's missing string mutation.
It seems as though the developers chose not to implement it (even in py3k), though for what reason, I do not know.

My problem is this; how do you re-assign the value of an object (from within the scope of a function) in a class (that inherits string properties)?
To simplify;

(class) -> string object
(class) - (function) -> modify string object

The goal is not to mutate the object (that's impossible), the goal is to re-assign the value attributed to the object.

Here is an outline;

class semi_mutable_str (str):

# define a class that inherits native str properties

    def __init__(self, value):
        self.str = value

    def rm(self, pos):
        '''Remove the character in self.str at pos.'''
        new = self.str[0:pos]
        new += self.str[pos+1:len(self.str)]
        self.str = new
        del new
        return self.str

>>> variable = semi_mutable_str ("aaa aba abb bbb b a")
>>> variable
"aaa aba abb bbb b a"
>>> variable.rm(0)
>>> variable.str
"aa aba abb bbb b a"
>>> variable
"aaa aba abb bbb b a"

Note that variable.str has been appropriately changed, but when I call variable, I get what I started with.
How do I change the value that is returned when I call 'variable'?
That is my question.

Thank you very much for reading, and your time.
Regards

EDIT
-> Finished Class:

#!/usr/bin/env python
# Mutable strings meet Python 2.x.

# update;
# Operator Overloading
# Faster Mutation
# More Readable/Documentated Code
# Error Handling
# More Functionality
# str Inheritance

class mstr (str):
    
    def __init__ (self, val):
        if 'zfill' not in dir(val):
            self.str = repr(val)
        else:
            self.str = val

    def __div__ (self, new):
        ''' / Overloading:
           Input can be an array of len 2, or an int.
           Case array; splice. Case int; rm.
        '''
        try:
            if 'zfill' in dir(new):
                raise SyntaxError
            if len(new) != 2:
                raise SyntaxError
            return self.splice(new[0], new[1])
        except TypeError:
            if 'zfill' in dir(new):
                raise SyntaxError
            return self.rm(new)
    
    def __add__ (self, new):
        ''' + Overloading.
            Add strings.
        '''
        self.bak = self.str
        self.str += new    
        return self.str
        
    def __sub__ (self, new):
        ''' - Overloading.
           rm_phr
        '''
        return self.rm_phr(new)    
    
    def __pow__ (self, new):
        ''' ^ Overloading.
           Input can be a str or an array of len 2.
           Case str; asgn. Case (int, str); ch. Case (str, int); asgn.
        '''
        if '__rand__' in dir(new):
            raise SyntaxError
        elif 'zfill' in dir(new):
            return self.asgn(new)
        else:
            if '__rand__' in dir(new[0]):
                return self.ch(new[0], new[1])
            else:
                return self.asgn(new[0], new[1])    
    
    def __mul__ (self, new):
        ''' * Overloading.
           Multiply strings.
        '''
        self.bak = self.str
        tmp = self.str
        for iteration in range(0,new-1):
            self.str += tmp
        del tmp, new
        return self.str
    
    def rm (self, pos): # /
        '''Remove the character in self.str at pos (position).
           Note: m_str(s) start indexing at 0.
        '''
        if 0 > pos > len(self.str)-1:
            raise IndexError
        self.bak = self.str
        new = self.str[0:pos]+self.str[pos+1:]
        self.str = new
        del new, pos
        return self.str
    
    def rm_phr(self, phr, new="", all=True): # -
        '''Remove ALL INSTANCES of phr or char (phrase) from self.str. Leaves whitespace.
           To remove whitespace, simply obj.rm_phr(' ')
           (If you want to remove only the first instance, specify so by;
           
           self.rm_phr(phr='phr', new='new', all=False)
        '''
        self.bak = self.str
        lphr=len(phr)
        if phr in self.str:
            loc = self.str.find(phr)
            self.str = self.purge(loc, lphr)
            if all == True:
                loc = self.str.find(phr)
                self.rm_phr(phr=phr, new=new)
            else:
                del lphr, phr, new, all
                return self.str
        else:
            del lphr, phr, new, all
            return self.str

    def purge(self, loc, lphr):
        '''Helpher function to the recursive self.rm_phr.
           Removes a chunk of self.str that
           starts at loc (location) is lphr (lenght of phrase) long.
        '''
        if loc == 0:
            new = self.str[lphr:]
        else:
            new = self.str[0:loc]+self.str[loc+lphr:]
        return new
            
    def asgn(self, phr, pos=0): # ^
        '''Assign char/phr to self.str at pos.
           The existing char will be SCOOTED TO THE RIGHT.'''
        if 0 > pos > len(self.str):
            raise IndexError
        self.bak = self.str
        if pos == 0:
            new = phr+self.str
        else:
            new = self.str[0:pos]+phr+self.str[pos:]
        self.str = new
        del new, phr, pos
        return self.str

    def asgn_r(self, phr):
        '''Assign char/phr to self.str to the right side of the string.'''
        self.asgn(phr, pos=len(self.str))
        del phr
        return self.str

    def ch(self, pos, phr): # ^
        '''Change pos:lphr in self.str to phr.
           The existing phr will be deleted.
        '''
        if 0 > pos > len(self.str)-1:
            raise IndexError
        lphr = len(phr)
        self.bak = self.str
        if pos == 0:
            new = phr+self.str[lphr:]
        if pos > 0:
            new = self.str[0:pos]+phr+self.str[pos+lphr:]
        self.str = new
        del new, pos, phr, lphr
        return self.str

    def str_dict (self, s):
        self.dict = {}
        field = range(0, len(s))
        for key in field:
            self.dict[key] = s[key]
        del field, s
        return self.dict
    
    def dict_str (self):
        new = ""
        for key,char in self.dict.iteritems():
            new += char
        self.str = new
        del new
        return self.str
        
    def splice(self, beg, end): # -
        '''Splice the chunk of the string specified in the params.'''
        self.bak = self.str
        self.str_dict(self.str)
        blacklist = range(beg+1, end)
        new = ""
        for key,char in self.dict.iteritems():
            if key not in blacklist and key != beg and key != end:
                new += char
        self.str_dict(new)
        self.str = new
        del new, beg, end
        return self.str
        
    def undo(self):
        self.str = self.bak
        return self.str

    def __str__(self): # self
        '''Overload str method __str__. '''
        return self.str

Last edited by vsk (2008-07-20 02:27:10)

Offline

#2 2008-07-16 22:39:21

gnud
Member
Registered: 2005-11-27
Posts: 182

Re: [Solved] Mutating Strings Within A Class (Python)

I think you ought to type out the words "phrase", "assign" and "remove" in the method names.

Offline

#3 2008-07-20 02:29:45

vsk
Member
Registered: 2008-05-27
Posts: 70

Re: [Solved] Mutating Strings Within A Class (Python)

Thank you for your interest.
I have added operator overloading, so you can now do;

import str

obj = str.mstr("0123456789 ab ab")
print obj

obj / (4,6) # splice: removes obj[4:6]
print obj

obj + "new" # append
print obj

print obj / 0 # removes obj[0]

obj ** ">>>" # asgn
print obj

obj ** (3,"ooo") # change obj[3:len("ooo")] to "ooo", i.e: obj[3:6] = "ooo"
print obj

obj ** (0,"#") # change: obj[0] = "#" type of thing

obj ** ("vks", 3) # asgn: inject "vks" at obj[3] 
print obj

obj - "a" # remove all instances of 'a' in obj 
print obj

obj.rm_phr(phr='b', all=False) # rm first instance of 'b' in obj
print obj

obj * 2 # multiply
print obj

obj.undo()
print obj

# normal strings are left unchanged
a = "012345"
try:
    a / 3
    a ** "-2 -1"
except TypeError:
    print;print
    print "Normal string operators are left unchanged."
    print "Some of the operators (+,*) are available in Python's str class."
    print "However, they don't actually make the changes permanent."

Cool stuff big_smile.

In any case - my teacher says that string immutability is a security feature to prevent users from committing memory evils.
Oh well.

My code is pretty safe.

Offline

Board footer

Powered by FluxBB