Value Incrementation Confusion - python

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]

Related

Is i = i + n truly the same as i += n? [duplicate]

This question already has answers here:
When is "i += x" different from "i = i + x" in Python?
(3 answers)
Closed 4 years ago.
One block of code works but the other does not. Which would make sense except the second block is the same as the first only with an operation written in shorthand. They are practically the same operation.
l = ['table']
i = []
Version 1
for n in l:
i += n
print(i)
Output: ['t', 'a', 'b', 'l', 'e']
Version 2
for n in l:
i = i + n
print(i)
Output:
TypeError: can only concatenate list (not "str") to list
What is causing this strange error?
They don't have to be the same.
Using the + operator calls the method __add__ while using the += operator calls __iadd__. It is completely up to the object in question what happens when one of these methods is called.
If you use x += y but x does not provide an __iadd__ method (or the method returns NotImplemented), __add__ is used as a fallback, meaning that x = x + y happens.
In the case of lists, using l += iterable actually extends the list l with the elements of iterable. In your case, every character from the string (which is an iterable) is appended during the extend operation.
Demo 1: using __iadd__
>>> l = []
>>> l += 'table'
>>> l
['t', 'a', 'b', 'l', 'e']
Demo 2: using extend does the same
>>> l = []
>>> l.extend('table')
>>> l
['t', 'a', 'b', 'l', 'e']
Demo 3: adding a list and a string raises a TypeError.
>>> l = []
>>> l = l + 'table'
[...]
TypeError: can only concatenate list (not "str") to list
Not using += gives you the TypeError here because only __iadd__ implements the extending behavior.
Demo 4: common pitfall: += does not build a new list. We can confirm this by checking for equal object identities with the is operator.
>>> l = []
>>> l_ref = l # another name for l, no data is copied here
>>> l += [1, 2, 3] # uses __iadd__, mutates l in-place
>>> l is l_ref # confirm that l and l_ref are names for the same object
True
>>> l
[1, 2, 3]
>>> l_ref # mutations are seen across all names
[1, 2, 3]
However, the l = l + iterable syntax does build a new list.
>>> l = []
>>> l_ref = l # another name for l, no data is copied here
>>> l = l + [1, 2, 3] # uses __add__, builds new list and reassigns name l
>>> l is l_ref # confirm that l and l_ref are names for different objects
False
>>> l
[1, 2, 3]
>>> l_ref
[]
In some cases, this can produce subtle bugs, because += mutates the original list, while
l = l + iterable builds a new list and reassigns the name l.
BONUS
Ned Batchelder's challenge to find this in the docs
No.
7.2.1. Augmented assignment statements:
An augmented assignment expression like x += 1 can be rewritten as x = x + 1 to achieve a similar, but not exactly equal effect. In the augmented version, x is only evaluated once. Also, when possible, the
actual operation is performed in-place, meaning that rather than
creating a new object and assigning that to the target, the old object
is modified instead.
If in the second case, you wrap a list around n to avoid errors:
for n in l:
i = i + [n]
print(i)
you get
['table']
So they are different operations.

Python: Create list from variables as long as they are not None

How do I add multiple variables to a list as long as they are not None?
If either one of them is None, then only the other one should be added to the list.
a = "A"
b = None
list_items = [a + b]
Gives:
TypeError: ("cannot concatenate 'str' and 'NoneType' objects",
u'occurred at index 0')
In the above example, the components of the list will always be a and b. I suspect a solution that can handle an arbitrary number of variables would make use of .extend() to empty list [] as long as the next added variable is not None. That could be useful, but what is the simplest solution?
You could create a function that takes arbitrary number of arguments and filters out the ones which are None:
def create_list(*args):
return [a for a in args if a is not None]
print create_list(1, 4, None, 'a', None, 'b')
Output:
[1, 4, 'a', 'b']
You can query the variable directly for None:
a = "A"
b = None
if b != None:
list_items = [a + b]
Another method would be a list comprehension:
a = "A"
b = None
lst = [e for e in [a, b] if e]
It's a little arcane, but this should neatly return you a list of only elements that are "truthy" - i.e. not None, non-empty string, integer greater than 0, True boolean etc.

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'

Alternative to python string item assignment

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

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