How to save an index in lambda, python - python

I'm trying to do the following:
>>>func = lambda string,i=0: string[index]
>>> func('HEY')
H
>>> func('HEY')
E
>>> func('HEY')
Y
How can i save and increment index each time(without creating index as global)
thanks

Solution 1
You can create a generator function, like this
def get_next_char(actual_string):
for char in actual_string:
yield char
Then, you need to create a generator object, like this
next_char = get_next_char("HEY")
That's it. You can now get the next character with next function, like this
>>> next(next_char)
H
>>> next(next_char)
E
>>> next(next_char)
Y
Solution 2
You can simply use the String's iterator, like this
get_char = iter("HEY")
>>> next(get_char)
H
>>> next(get_char)
E
>>> next(get_char)
Y

Related

Is there a way to immediately use a single of multiple return values?

I'm splitting a string into two of which I know for certain, the 2nd one will be the representation of an integer. I want to unpack both values and immediately cast the 2nd value to int. Basically I want this code in one line:
a, b = x.split('foo')
b = int(b)
I searched the web among SO, but looking for something containing python multiple return values only returns sites explaining how to return multiple values at once. Then I came up with that:
a, b = [value if i == 0 else int(value) for i, value in enumerate(f())]
but that's an abomination I'd like to avoid.
So is there a way to actually cast one of multiple return values of a function on the fly?
If you want a short one-liner to make your code readable, compact, and neat, defining your own function is a good option. The definition of the function can take up space elsewhere (even in a module), while your main script stays uncluttered.
def str_int(my_string):
a, b = my_string.split()
b = int(b)
return a, b
Then the call to this function is a short one-liner.
a, b = str_int(my_string)
One more option with list comprehension:
x = 'test 1'
a, b = [(a, int(b)) for a, b in [x.split()]][0]
print(type(a), a, type(b), b)
# output: <class 'str'> test <class 'int'> 1
You can use map function as below:
my_str = 'item 1'
a, b = map(lambda x : int(x) if x.isdigit() else x, my_str.split())
With Python 3.8's assignment expressions you can use this abomination:
x = '4foobar'
a, b = int((y := x.split('foo'))[0]), y[1]
# a = 4, b = 'bar'
Though obviously this is convoluted and prone to error. Just use a two liner.

What happens if you want to type a list() but python thinks it is a function?

So in my code there is a list called l. I then typed l(b) = l(b), X(a). That was supposed to make the item b in l turn into item b in l joined with the letter a in X. But python thinks l(b) is a function. It thinks b is the parameter and l is the function name. How do I fix it?
Switching the sides.
a = 0
b = 0
l = list()
while a <= len(X):
while not X(a)==' ':
l(b) = l(b),X(a)
l.append()
b = b + 1
To get element b in list l, use brackets [] instead of parens ().
So that means l[b], not l(b).

Replacing strings, not characters without the use of .replace and joining the strings the characters

Question has been asked that is similar but all post on here refer to replacing single characters. I'm trying to replace a whole word in a string. I've replaced it but I cant print it with spaces in between.
Here is the function replace that replaces it:
def replace(a, b, c):
new = b.split()
result = ''
for x in new:
if x == a:
x = c
result +=x
print(' '.join(result))
Calling it with:
replace('dogs', 'I like dogs', 'kelvin')
My result is this:
i l i k e k e l v i n
What I'm looking for is:
I like kelvin
The issue here is that result is a string and when join is called it will take each character in result and join it on a space.
Instead, use a list , append to it (it's also faster than using += on strings) and print it out by unpacking it.
That is:
def replace(a, b, c):
new = b.split(' ')
result = []
for x in new:
if x == a:
x = c
result.append(x)
print(*result)
print(*result) will supply the elements of the result list as positional arguments to print which prints them out with a default white space separation.
"I like dogs".replace("dogs", "kelvin") can of course be used here but I'm pretty sure that defeats the point.
Substrings and space preserving method:
def replace(a, b, c):
# Find all indices where 'a' exists
xs = []
x = b.find(a)
while x != -1:
xs.append(x)
x = b.find(a, x+len(a))
# Use slice assignment (starting from the last index)
result = list(b)
for i in reversed(xs):
result[i:i+len(a)] = c
return ''.join(result)
>>> replace('dogs', 'I like dogs dogsdogs and hotdogs', 'kelvin')
'I like kelvin kelvinkelvin and hotkelvin'
Just make result a list, and the joining will work:
result = []
You are just generating one long string and join its chars.

Cycle through string

I need to be able to cycle through a string of characters using the modulo operator so that each character can be passed to a function. This is a simple question, I know, but I am seriously confused as to how to do it. Here is what I have but it gives me the error "TypeError: not all arguments converted during string formatting". Any suggestions would be appreciated.
key = 'abc'
def encrypt(key,string):
c = ''
for i in range(0,len(string)):
t = (key)%3
a = XOR(ord(string[i]),ord(t))
b = chr(a)
c = c + b
return(c)
Ingredients
Here are some ingredients which help you write your encrypt function in a concise way:
You can directly iterate over the characters of a string:
>>> my_string = 'hello'
>>> for c in my_string:
... print(c)
...
h
e
l
l
o
You can cycle through any iterable (like, for example, a string) using the cycle function from the itertools module of the standard library:
>>> from itertools import cycle
>>> for x in cycle('abc'):
... print(x)
...
a
b
c
a
b
c
a
# goes on infinitely, abort with Ctrl-C
You can use the zip function to iterate over two sequences at the same time:
>>> for a, b in zip('hello', 'world'):
... print(a, b)
...
h w
e o
l r
l l
o d
Edit: as kichik suggests, you can also use itertools.izip which is beneficial if you deal with very large input strings.
You calculate the xor of two numbers by using the ^ operator:
>>> 5 ^ 3
6
You can concatenate a sequence of individual strings to a single string using the join function:
>>> ''.join(['hello', 'how', 'are', 'you'])
'hellohowareyou'
You can feed join with a so-called generator expression, which is similar to a for loop, but as a single expression:
>>> ''.join(str(x+5) for x in range(3))
'567'
Putting it all together
from itertools import cycle, izip
def encrypt(key, string):
return ''.join(chr(ord(k) ^ ord(c))
for k, c in izip(cycle(key), string))
You can (probably should) iterate over the characters without using range() and you'll want to run ord() on each character before you run mod on it, as the % operator means something else for a string. This should work:
key = 'abc'
for c in key:
print XOR(ord(c), ord(c) % 3)
You can use itertools.cycle() to cycle through the key and itertools.izip() for an easy combination of the two.
import itertools
def encrypt(key, string):
keyi = itertools.cycle(key)
result = ''
for k, v in itertools.izip(keyi, string):
a = ord(v) ^ ord(k)
result += chr(a)
return result
And then use it like this:
>>> encrypt('\x00', 'abc')
'abc'
>>> encrypt('\x01', 'abc')
'`cb'
You got an error about formatting because % is not a modulo operator for strings. It's used for formatting strings. You probably meant to use something like this:
key[i%3]

Init method; len() of self object

def __init__(self,emps=str(""),l=[">"]):
self.str=emps
self.bl=l
def fromFile(self,seqfile):
opf=open(seqfile,'r')
s=opf.read()
opf.close()
lisst=s.split(">")
if s[0]==">":
lisst.pop(0)
nlist=[]
for x in lisst:
splitenter=x.split('\n')
splitenter.pop(0)
splitenter.pop()
splitstring="".join(splitenter)
nlist.append(splitstring)
nstr=">".join(nlist)
nstr=nstr.split()
nstr="".join(nstr)
for i in nstr:
self.bl.append(i)
self.str=nstr
return nstr
def getSequence(self):
print self.str
print self.bl
return self.str
def GpCratio(self):
pgenes=[]
nGC=[]
for x in range(len(self.lb)):
if x==">":
pgenes.append(x)
for i in range(len(pgenes)):
if i!=len(pgenes)-1:
c=krebscyclus[pgenes[i]:pgenes[i+1]].count('c')+0.000
g=krebscyclus[pgenes[i]:pgenes[i+1]].count('g')+0.000
ratio=(c+g)/(len(range(pgenes[i]+1,pgenes[i+1])))
nGC.append(ratio)
return nGC
s = Sequence()
s.fromFile('D:\Documents\Bioinformatics\sequenceB.txt')
print 'Sequence:\n', s.getSequence(), '\n'
print "G+C ratio:\n", s.GpCratio(), '\n'
I dont understand why it gives the error:
in GpCratio for x in range(len(self.lb)): AttributeError: Sequence instance has no attribute 'lb'.
When i print the list in def getSequence it prints the correct DNA sequenced list, but i can not use the list for searching for nucleotides. My university only allows me to input 1 file and not making use of other arguments in definitions, but "self"
btw, it is a class, but it refuses me to post it then.. class called Sequence
Looks like a typo. You define self.bl in your __init__() routine, then try to access self.lb.
(Also, emps=str("") is redundant - emps="" works just as well.)
But even if you correct that typo, the loop won't work:
for x in range(len(self.bl)): # This iterates over a list like [0, 1, 2, 3, ...]
if x==">": # This condition will never be True
pgenes.append(x)
You probably need to do something like
pgenes=[]
for x in self.bl:
if x==">": # Shouldn't this be != ?
pgenes.append(x)
which can also be written as a list comprehension:
pgenes = [x for x in self.bl if x==">"]
In Python, you hardly ever need len(x) or for n in range(...); you rather iterate directly over the sequence/iterable.
Since your program is incomplete and lacking sample data, I can't run it here to find all its other deficiencies. Perhaps the following can point you in the right direction. Assuming a string that contains the characters ATCG and >:
>>> gene = ">ATGAATCCGGTAATTGGCATACTGTAG>ATGATAGGAGGCTAG"
>>> pgene = ''.join(x for x in gene if x!=">")
>>> pgene
'ATGAATCCGGTAATTGGCATACTGTAGATGATAGGAGGCTAG'
>>> ratio = float(pgene.count("G") + pgene.count("C")) / (pgene.count("A") + pgene.count("T"))
>>> ratio
0.75
If, however, you don't want to look at the entire string but at separate genes (where > is the separator), use something like this:
>>> gene = ">ATGAATCCGGTAATTGGCATACTGTAG>ATGATAGGAGGCTAG"
>>> genes = [g for g in gene.split(">") if g !=""]
>>> genes
['ATGAATCCGGTAATTGGCATACTGTAG', 'ATGATAGGAGGCTAG']
>>> nGC = [float(g.count("G")+g.count("C"))/(g.count("A")+g.count("T")) for g in genes]
>>> nGC
[0.6875, 0.875]
However, if you want to calculate GC content, then of course you don't want (G+C)/(A+T) but (G+C)/(A+T+G+C) --> nGC = [float(g.count("G")+g.count("C"))/len(g)].

Categories

Resources