python list indices must be integers not string - python

I'm trying to write a basic algorithm for encrypting a file. It takes the ASCII value of each character in a string and moves it up or down an amount depending on how long the password is, then you can layer more passwords on top.
def encrypt(s):
lenStr=s.__len__() #used later for working how far the int is moved
s=list(s) #converts the string to a list
for x in s:
s[x]=ord(s[x]) #the same index of the list is = to the value of the string
s[x]=chr(s[x])#is where it eventualy gets changed back to a str
s=ord(s) is the line which is throwing the error, i added int() around it but didnt help, same error

You're getting theTypeErrorexception because the value ofxin thes[x]=ord(s[x]) statement is one of the elements of the s list, so it's an individual character from the string argument passed toencrypt(). To fix that, just loop through all the possible indices of the s list which happens to be the same as the length as the original string:
def encrypt(s):
lenStr=len(s)
s=list(s) # convert the string to a list
for i in range(lenStr):
s[i]=ord(s[i])
s[i]=chr(s[i])
This will allow your code to run without getting that error. From your description of the encryption algorithm you're going to implement, one thing to watch out for is producing illegal 8-bit character values out of the range of 0-255. You can avoid that problem by simply applying the mod operator % to the intermediate results to keep the values in the proper range. Here's what I mean:
def encrypt(s):
lenStr = len(s)
s = list(s) # convert the string to a list
for i in range(lenStr):
s[i] = chr((ord(s[i]) + lenStr) % 256)
return ''.join(s) # convert list back into a string
Likewise, you'll have to do the same thing when you decrypt a string:
def decrypt(s):
lenStr = len(s)
s = list(s) # convert the string to a list
for i in range(lenStr):
s[i] = chr((ord(s[i]) - lenStr) % 256)
return ''.join(s) # convert list back into a string
enc = encrypt('Gnomorian')
print('encrypted:', enc)
dec = decrypt(enc)
print('decrypted:', dec)
Output:
encrypted: Pwxvx{rjw
decrypted: Gnomorian
Also note that not all the characters whose ord() values are in the range of 0-255 are printable, so you may want to restrict the encryption transformation even more if that's a requirement (that the encrypted version be printable).

x is a character from the string, not an integer. Let me illustrate:
>>> s = list('abcd')
>>> for x in s:
... print(x)
...
a
b
c
d
>>>
You want x to be integer values from 0 to the length of the string, like this:
>>> for x in range(len(s)):
... print(x)
...
0
1
2
3
>>>
So, your function should probably look like this (untested):
def encrypt(s):
lenStr=s.__len__() #used later for working how far the int is moved
s=list(s) #converts the string to a list
for x in range(len(s)):
s[x]=ord(s[x]) #the same index of the list is = to the value of the string
s[x]=chr(s[x])#is where it eventualy gets changed back to a str

I am guessing this is what you are aiming for:
def encrypt(s):
offset = len(s)
return ''.join(chr(ord(c) + offset) for c in s)
def decrypt(s):
offset = len(s)
return ''.join(chr(ord(c) - offset) for c in s)
Some tips:
Use len(s) instead of lenStr=s.__len__()
Naming values near their first use in the code improves readability.
Choose names that describe the use of the value.
Strings are iterable, same as lists. No need to convert a string into a list.
Learn and use list comprehensions and generators whenever possible, they are usually much faster, simpler, easier to read and less error prone to create.
Remember to accept and/or upvote answers that are helpful.

Related

How to swap Character of string with the given condition?

def password(passlist):
listt = []
for i in range(0, len(passlist)):
temp = passlist[i]
for j in range(0, len(temp)/2):
if((j+2)%2 == 0) :
t = temp[j]
temp.replace(temp[j], temp[j+2])
temp.replace(temp[j+2], t)
listt.append(temp)
I am passing a list of string
example ["abcd", "bcad"]. for each string i will swap ith character with j character if (i+j)%2 == 0.
My code is going out of the boundary of string.
Please suggest me a better approach to this problem
Here's how I'd do it:
def password(passlist):
def password_single(s):
temp = list(s)
for j in range(0, len(temp) // 2, 2):
temp[j], temp[j+2] = temp[j+2], temp[j]
return ''.join(temp)
return [password_single(s) for s in passlist]
print(password(["abcd", "bcad"]))
Define a function that operates on a single list element (password_single). It's easier to develop and debug that way. In this case, I made it an inner function but it doesn't have to be.
Use three-argument range calls, since it's the same as doing the two-argument + if(index%2 == 0)
Convert strings to lists, perform the swapping and convert back.
Use a "swap" type operation instead of two replaces.
Strings are immutable in python, therefore you cannot swap the characters in place. You have to build a new string.
Moreover, your code does not work for each string in passlist. You iterate through the string in passlist in the first for block, but then you use the temp variable outside that block. This means that the second for loop only iterates on the last string.
Now, a way to do what you want, might be:
for i in range(len(passlist)):
pass_ = passlist[i]
new_pass = [c for c in pass_] # convert the string in a list of chars
for j in range(len(pass_) / 2):
new_pass[j], new_pass[j+2] = new_pass[j+2], new_pass[j] # swap
listt.append(''.join(new_pass)) # convert the list of chars back to string

Pythonic way to convert an integer into a hex-escaped string

What is the most pythonic way to convert an integer into a hex-escaped string?
0 -> '\x00'
15 -> '\x0f'
0xdeadbeef -> '\xde\xad\xbe\xef'
There's no way to do what you want directly, but it's not that hard to do yourself.
For example, you can convert the whole int to hex, then insert a \x character before every pair of characters:
n = 0xdeadbeef
x = format(n, 'x')
s = re.sub('(..)', r'\\x\1', x)
# or s = ''.join(r'\x{}{}'.format(*b) for b in pairwise(x))
# or s = ''.join(r'\x' + x[i:i+2] for i in range(0, len(x), 2))
Or you could pull off a byte at a time and format them yourself:
x = []
while n:
x.append(n % 256)
n //= 256
s = ''.join(r'\x{:02x}'.format(b) for b in reversed(x))
Which of those is most Pythonic? I think they're all simple enough that if you wrapped them up in a function with an appropriate name, nobody would complain, but all complicated enough that if you just inlined them into your code, it wouldn't be very pythonic. (I'd personally use the pairwise version, just because I always think of itertools first, but that's just me.)
It's a bit ambiguous whether you want the 1-byte string '\x0f' or the 6-byte string "'\\x0f'" (ie. a string which looks like '\x0f' when printed). In case you meant the former, here's one way to do that:
s = '%x' % 0xdeadbeef
binascii.unhexlify('0' + s if len(s) % 2 else s)
Not sure how 'pythonic' that is, but here's a solution using a recursive function:
def uint_tuple_to_bytes(t):
if t[0] < 0x100:
return t
else:
return uint_tuple_to_bytes(divmod(t[0], 0x100) + t[1:])
def uint_to_bytes(i):
return uint_tuple_to_bytes((i,))
def uint_to_binary_string(i):
return ''.join(chr(byte) for byte in uint_to_bytes(i))
(Inspired by the second part ("pull off a byte at a time") of abarnert's answer and by msvalkon's answer to 'How to split 16-bit unsigned integer into array of bytes in python?'.)

How to return (x) number of characters in a given string?

I've been at this for a while now, and I've gotten so close.
I've had two problems to complete for an assignment. The first one I finished. It's supposed to return the first three indices of the given string, and if the string is less than 3 letters, it returns nothing. It looks like this:
The second one is similar, but a little more involved. The problem is:
Hopefully that explains my issue. I can't figure out how to get the inputted number to correspond to the number of indices I'm trying to print. In the first problem, it was simple, because it was always just the first three indices that were used. Now, it's (n) number of indices.
Any help is much appreciated (I'm using Python 3.4.2)
Strings support sub-stringing in Python.
def returnN(string, length):
return string[:length] if len(string) >= length else ''
In action:
>>> returnN('hello', 2)
'he'
>>> returnN('hello', 5)
'hello'
>>> returnN('BYE', 1)
'B'
>>> returnN('BYE', 10)
''
Use len and slice method of string method like:
def returnN(string, length):
length_string = len(string)
if length > length_string:
return ''
return string[0:length]
print(returnN('hello', 5))
print(returnN('hello', 2))
print(returnN('Nye', 1))
print(returnN('OKOK', 10))
or simple way:
def returnN_S(string, length):
return string[: length] if length <= len(string) else ''
print(returnN_S('hello', 5))
print(returnN_S('hello', 2))
print(returnN_S('Nye', 1))
print(returnN_S('OKOK', 10))
or one line way:
returnN_H = lambda string, length: string[: length] if length <= len(string) else ''
print(returnN_H('hello', 5))
print(returnN_H('hello', 2))
print(returnN_H('Nye', 1))
print(returnN_H('OKOK', 10))
Hope helps.
Unicode strings in Python are immutable Unicode codepoint sequences.
Unicode codepoint is just a number from 0 to sys.maxunicode e.g., Nº128516: 😄('\U0001f604').
A substring/slice of a Unicode string s such as s[:2] returns a new Unicode string that contains 2 Unicode codepoints.
len(s) returns the number of Unicode codepoints in the string s.
An empty string could be represented using '' string literal in Python source code.
To return a given number of user-perceived characters from a string or an empty string if the text size is too small, you could use \X regular expression (it matches an eXtended grapheme cluster):
#!/usr/bin/env python3
import regex # $ pip install regex
def returnN(text, n):
chars = regex.findall(r'\X', text)
return ''.join(chars[:n]) if len(chars) >= n else ''
text = 'a\u0300 biento\u0302t'
print(text) # -> à bientôt
print(returnN(text, 4)) # -> à bi
A single user-perceived character such as à can span several Unicode codepoints such as U+0061, U+0300.
The term character is ambiguous. It can mean a byte, Unicode codepoint, grapheme cluster in different circumstances.
input_string = "hello"
def returnN(string, n):
return string[:n]
output = returnN(input_string, 3)
that's all

List entries to variables: ttk python

I am trying to shift the letters of a string over by 1 each time a while loop runs(so "A" becomes "B", "B" becomes "C" and so on each time through).
I need to have each result displayed so I can compare them. What I don't want to do but know how to do is create 26 different functions each one shifting the letters one letter farther but this seems a bit ridiculous.
How do I assign a variable to ttk each time the while loop goes through?
I thought this would assign "ls" plus whatever the count was on (ls1, ls2, ls3...) to each variable but it does not. It throws an error every time.
def shift1(*args):
s = code.get()
storage = []
count = 1
while (count <= 26):
l = [ord(i) for i in s]
sh = ([chr(i + count) for i in l])
storage.append(sh)
("ls"+str(count)).set(storage(count - 1))
count += 1
It give me an error that says
AttributeError: 'str' object has no attribute 'set'
Like I said I could just use this function 26 times each time assigning a diff ttk variable.
def shift1(*args):
s = code.get()
l = [ord(i) for i in s]
sh1.set(''.join([chr(i + 1) for i in l]))
This will essentially bypass the loop, but I know there has to be a better way.
Very new to python and ttk so any help is appreciated.
You don't need to use a while loop, you can just iterate using a for loop instead. As with Bemmu's answer this forces the characters to be all upper case, as it makes it easier. But you can modified a little more so it checks based on upper or lower case characters.
def shift(str):
str =str.upper()
for i in range(26):
print "".join([chr((ord(x)-65+i)%26+65) for x in str])
shift("hello")
You can see this in operation here: http://codepad.org/OaBXM4s2
Here is a way to rotate the characters in a string around, assuming there are only A-Z letters in your string.
string = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
for i in range(10):
string = "".join([chr((ord(letter) - ord('A') + 1) % 26 + ord('A')) for letter in string])
print string
The idea is that each letter has an ASCII code difference from the letter A. So letter A would be 0, letter B is 1. When letter Z is shifted forward, it needs to go back to A. This is where the modulo (%) comes in, it shifts the letter Z back to A if needed.
Output:
BCDEFGHIJKLMNOPQRSTUVWXYZA
CDEFGHIJKLMNOPQRSTUVWXYZAB
DEFGHIJKLMNOPQRSTUVWXYZABC
EFGHIJKLMNOPQRSTUVWXYZABCD
FGHIJKLMNOPQRSTUVWXYZABCDE
GHIJKLMNOPQRSTUVWXYZABCDEF
HIJKLMNOPQRSTUVWXYZABCDEFG
IJKLMNOPQRSTUVWXYZABCDEFGH
JKLMNOPQRSTUVWXYZABCDEFGHI
KLMNOPQRSTUVWXYZABCDEFGHIJ

Swapping every second character in a string in Python

I have the following problem: I would like to write a function in Python which, given a string, returns a string where every group of two characters is swapped.
For example given "ABCDEF" it returns "BADCFE".
The length of the string would be guaranteed to be an even number.
Can you help me how to do it in Python?
To add another option:
>>> s = 'abcdefghijkl'
>>> ''.join([c[1] + c[0] for c in zip(s[::2], s[1::2])])
'badcfehgjilk'
import re
print re.sub(r'(.)(.)', r'\2\1', "ABCDEF")
from itertools import chain, izip_longest
''.join(chain.from_iterable(izip_longest(s[1::2], s[::2], fillvalue = '')))
You can also use islices instead of regular slices if you have very large strings or just want to avoid the copying.
Works for odd length strings even though that's not a requirement of the question.
While the above solutions do work, there is a very simple solution shall we say in "layman's" terms. Someone still learning python and string's can use the other answers but they don't really understand how they work or what each part of the code is doing without a full explanation by the poster as opposed to "this works". The following executes the swapping of every second character in a string and is easy for beginners to understand how it works.
It is simply iterating through the string (any length) by two's (starting from 0 and finding every second character) and then creating a new string (swapped_pair) by adding the current index + 1 (second character) and then the actual index (first character), e.g., index 1 is put at index 0 and then index 0 is put at index 1 and this repeats through iteration of string.
Also added code to ensure string is of even length as it only works for even length.
string = "abcdefghijklmnopqrstuvwxyz123"
# use this prior to below iteration if string needs to be even but is possibly odd
if len(string) % 2 != 0:
string = string[:-1]
# iteration to swap every second character in string
swapped_pair = ""
for i in range(0, len(string), 2):
swapped_pair += (string[i + 1] + string[i])
# use this after above iteration for any even or odd length of strings
if len(swapped_pair) % 2 != 0:
swapped_adj += swapped_pair[-1]
print(swapped_pair)
badcfehgjilknmporqtsvuxwzy21 # output if the "needs to be even" code used
badcfehgjilknmporqtsvuxwzy213 # output if the "even or odd" code used
Here's a nifty solution:
def swapem (s):
if len(s) < 2: return s
return "%s%s%s"%(s[1], s[0], swapem (s[2:]))
for str in ("", "a", "ab", "abcdefgh", "abcdefghi"):
print "[%s] -> [%s]"%(str, swapem (str))
though possibly not suitable for large strings :-)
Output is:
[] -> []
[a] -> [a]
[ab] -> [ba]
[abcdefgh] -> [badcfehg]
[abcdefghi] -> [badcfehgi]
If you prefer one-liners:
''.join(reduce(lambda x,y: x+y,[[s[1+(x<<1)],s[x<<1]] for x in range(0,len(s)>>1)]))
Here's a another simple solution:
"".join([(s[i:i+2])[::-1]for i in range(0,len(s),2)])

Categories

Resources