Alternative to python string item assignment - python

What is the best / correct way to use item assignment for python string ?
i.e s = "ABCDEFGH" s[1] = 'a' s[-1]='b' ?
Normal way will throw : 'str' object does not support item assignment

Strings are immutable. That means you can't assign to them at all. You could use formatting:
>>> s = 'abc{0}efg'.format('d')
>>> s
'abcdefg'
Or concatenation:
>>> s = 'abc' + 'd' + 'efg'
>>> s
'abcdefg'
Or replacement (thanks Odomontois for reminding me):
>>> s = 'abc0efg'
>>> s.replace('0', 'd')
'abcdefg'
But keep in mind that all of these methods create copies of the string, rather than modifying it in-place. If you want in-place modification, you could use a bytearray -- though that will only work for plain ascii strings, as alexis points out.
>>> b = bytearray('abc0efg')
>>> b[3] = 'd'
>>> b
bytearray(b'abcdefg')
Or you could create a list of characters and manipulate that. This is probably the most efficient and correct way to do frequent, large-scale string manipulation:
>>> l = list('abc0efg')
>>> l[3] = 'd'
>>> l
['a', 'b', 'c', 'd', 'e', 'f', 'g']
>>> ''.join(l)
'abcdefg'
And consider the re module for more complex operations.
String formatting and list manipulation are the two methods that are most likely to be correct and efficient IMO -- string formatting when only a few insertions are required, and list manipulation when you need to frequently update your string.

Since strings are "immutable", you get the effect of editing by constructing a modified version of the string and assigning it over the old value. If you want to replace or insert to a specific position in the string, the most array-like syntax is to use slices:
s = "ABCDEFGH"
s = s[:3] + 'd' + s[4:] # Change D to d at position 3
It's more likely that you want to replace a particular character or string with another. Do that with re, again collecting the result rather than modifying in place:
import re
s = "ABCDEFGH"
s = re.sub("DE", "--", s)

I guess this Object could help:
class Charray(list):
def __init__(self, mapping=[]):
"A character array."
if type(mapping) in [int, float, long]:
mapping = str(mapping)
list.__init__(self, mapping)
def __getslice__(self,i,j):
return Charray(list.__getslice__(self,i,j))
def __setitem__(self,i,x):
if type(x) <> str or len(x) > 1:
raise TypeError
else:
list.__setitem__(self,i,x)
def __repr__(self):
return "charray['%s']" % self
def __str__(self):
return "".join(self)
For example:
>>> carray = Charray("Stack Overflow")
>>> carray
charray['Stack Overflow']
>>> carray[:5]
charray['Stack']
>>> carray[-8:]
charray['Overflow']
>>> str(carray)
'Stack Overflow'
>>> carray[6] = 'z'
>>> carray
charray['Stack zverflow']

s = "ABCDEFGH" s[1] = 'a' s[-1]='b'
you can use like this
s=s[0:1]+'a'+s[2:]
this is very simple than other complex ways

Related

Why does a call to `str(someList)` sometimes create an "array" of spaces?

playerList contains two Player objects (with str attributes called, respectively, "a" and "b"), and Player implements __str__ and __repr__. When I concatenate str(playerList) onto another string, I expect the string to be appended by something of the form "[a, b]". Instead, the resulting string is appended by "[ , ]". What mistake am I making that gives this result?
Here's what I wrote
prompt = "And then choose the opponent you would like to attack from " + str(playerList)
def __str__ (self):
return self.name
def __repr__ (self):
return str()
What I get on stdout:
"And then choose the opponent you would like to attack from [, ]"
What I want:
"And then choose the opponent you would like to attack from [a,b]"
Your __repr__ method returns an empty string:
def __repr__(self):
return str()
str() with no arguments is an empty string:
>>> str()
''
If you wanted to call __str__ do so directly, or pass self to str():
return self.__str__()
or
return str(self)
Note that converting a list to a string will include all strings in that list as their representation; the output of repr(stringobject), which uses the same notation you'd use when creating such a string. The list ['a', 'b'] is going to be converted to a string using exactly that notation:
>>> l = ['a', 'b']
>>> l
['a', 'b']
>>> str(l)
"['a', 'b']"
>>> print str(l)
['a', 'b']
If you really wanted to include those strings without quotes, you'll need to do your own formatting:
>>> '[{}]'.format(', '.join([str(elem) for elem in l]))
'[a, b]'
>>> print '[{}]'.format(', '.join([str(elem) for elem in l]))
[a, b]

Replace multiple elements in string with str methods

I am trying to write a function that takes a string of DNA and returns the compliment. I have been trying to solve this for a while now and looked through the Python documentation but couldn't work it out. I have written the docstring for the function so you can see what the answer should look like. I have seen a similar question asked on this forum but I could not understand the answers. I would be grateful if someone can explain this using only str formatting and loops / if statements, as I have not yet studied dictionaries/lists in detail.
I tried str.replace but could not get it to work for multiple elements, tried nested if statements and this didn't work either. I then tried writing 4 separate for loops, but to no avail.
def get_complementary_sequence(dna):
""" (str) -> str
Return the DNA sequence that is complementary
to the given DNA sequence.
>>> get_complementary_sequence('AT')
TA
>>> get_complementary_sequence('GCTTAA')
CGAATT
"""
for char in dna:
if char == A:
dna = dna.replace('A', 'T')
elif char == T:
dna = dna.replace('T', 'A')
# ...and so on
For a problem like this, you can use string.maketrans (str.maketrans in Python 3) combined with str.translate:
import string
table = string.maketrans('CGAT', 'GCTA')
print 'GCTTAA'.translate(table)
# outputs CGAATT
You can map each letter to another letter.
You probably need not create translation table with all possible combination.
>>> M = {'A':'T', 'T':'A', 'C':'G', 'G':'C'}
>>> STR = 'CGAATT'
>>> S = "".join([M.get(c,c) for c in STR])
>>> S
'GCTTAA'
How this works:
# this returns a list of char according to your dict M
>>> L = [M.get(c,c) for c in STR]
>>> L
['G', 'C', 'T', 'T', 'A', 'A']
The method join() returns a string in which the string elements of sequence have been joined by str separator.
>>> str = "-"
>>> L = ['a','b','c']
>>> str.join(L)
'a-b-c'

Value Incrementation Confusion

Hey am new to python development and i am fully filled with a lots of doubts since am a newbie.Suppose
s = 'something'
for something in s:
something = something + 1
print something
I know here something act as an index and it would print out the whole elements in s.
And in
s = 'something'
for something in s:
s[something] = s[something] + 1
print something
I didnt understand the correct meaning of the second part of the code..Is it possible in python??..
'
Sorry for low grade question and any help would be appreciated ..
When you loop through a string like this:
for c in 'something':
print(c)
c does not act as an index, it acts as character of the string, so the output would be:
s
o
m
e
t
h
i
n
g
If you want to loop through the indices you can do:
s = 'something'
for i in range(len(s)):
print(i)
And the output would be:
0
1
2
3
4
5
6
7
8
You can access a character from the string by indexing like this:
s = 'something'
for i in range(len(s)):
print(s[i])
And the output of that would be:
s
o
m
e
t
h
i
n
g
If you want to loop through a string so that you get the characters as well as the indices, you can use the enumerate() function:
s = 'something'
for i, c in enumerate(s):
print(i, c)
The output:
0 s
1 o
2 m
3 e
4 t
5 h
6 i
7 n
8 g
Note that strings are immutable, so you can't change them:
>>> s = 'something'
>>> s[0] = 'a'
TypeError: 'str' object does not support item assignment
When you do string concatenation, you are not actually changing the string, you are creating a new one.
EDIT 1
Strings have methods that can be called on them to do certain tasks, such as the .split() method:
>>> s = 'something'
>>> s.split('e')
['som', 'thing']
They also have some special methods like __getitem__. The following two are equivalent:
>>> s = 'something'
>>> s[0]
's'
>>> s.__getitem__(0)
's'
Other sequences like lists are mutable, so they also have a __setitem__ method:
>>> s = ['s', 'o', 'm', 'e', 't', 'h', 'i', 'n', 'g']
>>> s[0] = 't'
>>> s
['t', 'o', 'm', 'e', 't', 'h', 'i', 'n', 'g']
>>> s.__setitem__(0, 's')
>>> s
['s', 'o', 'm', 'e', 't', 'h', 'i', 'n', 'g']
EDIT 2
This is what happens when you try to do this s[something] = s[something] + 1:
>>> s = 'something'
>>> s[0] = s[0] + 1
TypeError: Can't convert 'int' object to str implicitly
The reason this happens is because s[0] is 's' so you are trying to add a number to a string, which doesn't make any sense. Then if you try and do s[something] = s[something] + 'a' you will get a TypeError because strings are immutable:
>>> s = 'something'
>>> s[0] = s[0] + 'a'
TypeError: 'str' object does not support item assignment
And this will definitely not work:
>>> s = 'something'
>>> s['a']
TypeError: string indices must be integers
s[something] = s[something] + 1 shouldn't work; string values are immutable.
Syntax like s += "foo" actually creates a new string value from s + "foo", then assigns it to s, releasing the original value of s to be garbage collected.
A key thing to remember about all variables in Python is that they're just references to values. There's no guarantee the values aren't pooled somewhere and have a copy-on-write semantic. Another example is that a like like x = 5 doesn't set x to 5, it creates (or otherwise obtains) the value 5 and sets x to refer to it.
For the most part this distinction really doesn't matter. In general, the Right Thing(TM) happens.
The code:
s = 'something'
for something in s:
# ...
treats s like a list of characters and sets something to each one in sequence through the loop. (This is unlike JavaScript.) If you want the indices and not just the characters, use:
s = 'something'
for i, something in enumerate(s):
# ...
so s[something] = s[something] + 1 is not possible in any situations ..right ?
It works fine for lists (e.g. [1, 2, 3]) and dictionaries (e.g. {"a": 1, "b": 2}). Just not for strings.
If you simply want to get a string where every character is replaced with the next one, first split the string with a list comprehension:
l = [c for c in s]
Replace each character with the next one:
l2 = [chr(ord(c) + 1) for c in l]
and glue them back together into a new string:
s2 = ''.join(l2)
Putting it all together:
s = 'something'
s2 = ''.join([chr(ord(c) + 1) for c in s])
The square brackets after a variable name invoke __getitem__ or __setitem__ on the variable, depending on the context. So for example, x[i] = x[i] + 1 is equivalent to x.__setitem__(i, x.__getitem__(i) + 1). You can read up about this in the docs here:
https://docs.python.org/2/reference/datamodel.html
There are several built-in types that implement one or both of these, for example strings, tuples, lists, and dictionaries. For the sequence types (strings, tuples, lists) the "item" being accessed or set is an index, so for example print 'hello'[0] would print h because you are getting the character at the first index in the list.
In this case, it looks like the second piece of code would actually cause an error because strings are not mutable. This means that string objects can't be modified, so they won't have __setitem__ implemented and s[something] = s[something] + 1 would fail. This could work with a mutable type like list or dict though, for example:
s = [1, 1, 1]
s[0] = s[0] + 1
# s is now [2, 1, 1]

How to get the first 2 letters of a string in Python?

Let's say I have a string
str1 = "TN 81 NZ 0025"
two = first2(str1)
print(two) # -> TN
How do I get the first two letters of this string? I need the first2 function for this.
It is as simple as string[:2]. A function can be easily written to do it, if you need.
Even this, is as simple as
def first2(s):
return s[:2]
In general, you can get the characters of a string from i until j with string[i:j].
string[:2] is shorthand for string[0:2]. This works for lists as well.
Learn about Python's slice notation at the official tutorial
t = "your string"
Play with the first N characters of a string with
def firstN(s, n=2):
return s[:n]
which is by default equivalent to
t[:2]
Heres what the simple function would look like:
def firstTwo(string):
return string[:2]
In python strings are list of characters, but they are not explicitly list type, just list-like (i.e. it can be treated like a list). More formally, they're known as sequence (see http://docs.python.org/2/library/stdtypes.html#sequence-types-str-unicode-list-tuple-bytearray-buffer-xrange):
>>> a = 'foo bar'
>>> isinstance(a, list)
False
>>> isinstance(a, str)
True
Since strings are sequence, you can use slicing to access parts of the list, denoted by list[start_index:end_index] see Explain Python's slice notation . For example:
>>> a = [1,2,3,4]
>>> a[0]
1 # first element, NOT a sequence.
>>> a[0:1]
[1] # a slice from first to second, a list, i.e. a sequence.
>>> a[0:2]
[1, 2]
>>> a[:2]
[1, 2]
>>> x = "foo bar"
>>> x[0:2]
'fo'
>>> x[:2]
'fo'
When undefined, the slice notation takes the starting position as the 0, and end position as len(sequence).
In the olden C days, it's an array of characters, the whole issue of dynamic vs static list sounds like legend now, see Python List vs. Array - when to use?
All previous examples will raise an exception in case your string is not long enough.
Another approach is to use
'yourstring'.ljust(100)[:100].strip().
This will give you first 100 chars.
You might get a shorter string in case your string last chars are spaces.
For completeness: Instead of using def you could give a name to a lambda function:
first2 = lambda s: s[:2]

Why doesn't list have safe "get" method like dictionary?

Why doesn't list have a safe "get" method like dictionary?
>>> d = {'a':'b'}
>>> d['a']
'b'
>>> d['c']
KeyError: 'c'
>>> d.get('c', 'fail')
'fail'
>>> l = [1]
>>> l[10]
IndexError: list index out of range
Ultimately it probably doesn't have a safe .get method because a dict is an associative collection (values are associated with names) where it is inefficient to check if a key is present (and return its value) without throwing an exception, while it is super trivial to avoid exceptions accessing list elements (as the len method is very fast). The .get method allows you to query the value associated with a name, not directly access the 37th item in the dictionary (which would be more like what you're asking of your list).
Of course, you can easily implement this yourself:
def safe_list_get (l, idx, default):
try:
return l[idx]
except IndexError:
return default
You could even monkeypatch it onto the __builtins__.list constructor in __main__, but that would be a less pervasive change since most code doesn't use it. If you just wanted to use this with lists created by your own code you could simply subclass list and add the get method.
This works if you want the first element, like my_list.get(0)
>>> my_list = [1,2,3]
>>> next(iter(my_list), 'fail')
1
>>> my_list = []
>>> next(iter(my_list), 'fail')
'fail'
I know it's not exactly what you asked for but it might help others.
Probably because it just didn't make much sense for list semantics. However, you can easily create your own by subclassing.
class safelist(list):
def get(self, index, default=None):
try:
return self.__getitem__(index)
except IndexError:
return default
def _test():
l = safelist(range(10))
print l.get(20, "oops")
if __name__ == "__main__":
_test()
Instead of using .get, using like this should be ok for lists. Just a usage difference.
>>> l = [1]
>>> l[10] if 10 < len(l) else 'fail'
'fail'
Credits to jose.angel.jimenez and Gus Bus.
For the "oneliner" fans…
If you want the first element of a list or if you want a default value if the list is empty try:
liste = ['a', 'b', 'c']
value = (liste[0:1] or ('default',))[0]
print(value)
returns a
and
liste = []
value = (liste[0:1] or ('default',))[0]
print(value)
returns default
Examples for other elements…
liste = ['a', 'b', 'c']
print(liste[0:1]) # returns ['a']
print(liste[1:2]) # returns ['b']
print(liste[2:3]) # returns ['c']
print(liste[3:4]) # returns []
With default fallback…
liste = ['a', 'b', 'c']
print((liste[0:1] or ('default',))[0]) # returns a
print((liste[1:2] or ('default',))[0]) # returns b
print((liste[2:3] or ('default',))[0]) # returns c
print((liste[3:4] or ('default',))[0]) # returns default
Possibly shorter:
liste = ['a', 'b', 'c']
value, = liste[:1] or ('default',)
print(value) # returns a
It looks like you need the comma before the equal sign, the equal sign and the latter parenthesis.
More general:
liste = ['a', 'b', 'c']
f = lambda l, x, d: l[x:x+1] and l[x] or d
print(f(liste, 0, 'default')) # returns a
print(f(liste, 1, 'default')) # returns b
print(f(liste, 2, 'default')) # returns c
print(f(liste, 3, 'default')) # returns default
Tested with Python 3.6.0 (v3.6.0:41df79263a11, Dec 22 2016, 17:23:13)
Try this:
>>> i = 3
>>> a = [1, 2, 3, 4]
>>> next(iter(a[i:]), 'fail')
4
>>> next(iter(a[i + 1:]), 'fail')
'fail'
A reasonable thing you can do is to convert the list into a dict and then access it with the get method:
>>> my_list = ['a', 'b', 'c', 'd', 'e']
>>> my_dict = dict(enumerate(my_list))
>>> print my_dict
{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e'}
>>> my_dict.get(2)
'c'
>>> my_dict.get(10, 'N/A')
So I did some more research into this and it turns out there isn't anything specific for this. I got excited when I found list.index(value), it returns the index of a specified item, but there isn't anything for getting the value at a specific index. So if you don't want to use the safe_list_get solution which I think is pretty good. Here are some 1 liner if statements that can get the job done for you depending on the scenario:
>>> x = [1, 2, 3]
>>> el = x[4] if len(x) > 4 else 'No'
>>> el
'No'
You can also use None instead of 'No', which makes more sense.:
>>> x = [1, 2, 3]
>>> i = 2
>>> el_i = x[i] if len(x) == i+1 else None
Also if you want to just get the first or last item in the list, this works
end_el = x[-1] if x else None
You can also make these into functions but I still liked the IndexError exception solution. I experimented with a dummied down version of the safe_list_get solution and made it a bit simpler (no default):
def list_get(l, i):
try:
return l[i]
except IndexError:
return None
Haven't benchmarked to see what is fastest.
Dictionaries are for look ups. It makes sense to ask if an entry exists or not. Lists are usually iterated. It isn't common to ask if L[10] exists but rather if the length of L is 11.
If you
want a one liner,
prefer not having try / except in your happy code path where you needn't, and
want the default value to be optional,
you can use this:
list_get = lambda l, x, d=None: d if not l[x:x+1] else l[x]
Usage looks like:
>>> list_get(['foo'], 4) == None
True
>>> list_get(['hootenanny'], 4, 'ho down!')
'ho down!'
>>> list_get([''], 0)
''
For small index values you can implement
my_list.get(index, default)
as
(my_list + [default] * (index + 1))[index]
If you know in advance what index is then this can be simplified, for example if you knew it was 1 then you could do
(my_list + [default, default])[index]
Because lists are forward packed the only fail case we need to worry about is running off the end of the list. This approach pads the end of the list with enough defaults to guarantee that index is covered.
This isn't an extremely general-purpose solution, but I had a case where I expected a list of length 3 to 5 (with a guarding if), and I was breaking out the values to named variables. A simple and concise way I found for this involved:
foo = (argv + [None, None])[3]
bar = (argv + [None, None])[4]
Now foo and bar are either the 4th and 5th values in the list, or None if there weren't that many values.
Your usecase is basically only relevant for when doing arrays and matrixes of a fixed length, so that you know how long they are before hand. In that case you typically also create them before hand filling them up with None or 0, so that in fact any index you will use already exists.
You could say this: I need .get() on dictionaries quite often. After ten years as a full time programmer I don't think I have ever needed it on a list. :)

Categories