Python disable list string "breaking" - python

Is there a way to disable breaking string with list. For example:
>>> a = "foo"
>>> b = list()
>>> b.append(list(a))
>>> b
>>>[['f', 'o', 'o']]
Is there a way to have a list inside of a list with string that is not "broken", for example [["foo"],["bar"]]?

Very esay:
>>> a = "foo"
>>> b = list()
>>> b.append([a])
>>> b
[['foo']]

Do this:
>>> a = "foo"
>>> b = list()
>>> b.append([a])
>>> b
[["foo"]]

The reason this happens is that the list function works by taking each element of the sequence you pass it and putting them in a list. A string in Python is a sequence, the elements of the sequence are the individual characters.
Having this abstract concept of a "sequence" means that a lot of Python functions can work on multiple data types, as long as they accept a sequence. Once you get used to this idea, hopefully you'll start finding this concept more useful than surprising.

you sound like you want to break on word boundaries instead of on each letter.
Try something like
a = "foo bar"
b = list()
b.append(a.split(' ')) # [['foo', 'bar']]
Example with RegEx (to support multiple consecutive spaces) :
import re
a = "foo bar"
b.append(re.split(r'\s+', a)) # [['foo', 'bar']]

Related

Python function to modify string

I was asked once to create a function that given a string, remove a few characters from the string.
Is it possible to do this in Python?
This can be done for lists, for example:
def poplist(l):
l.pop()
l1 = ['a', 'b', 'c', 'd']
poplist(l1)
print l1
>>> ['a', 'b', 'c']
What I want is to do this function for strings.
The only way I can think of doing this is to convert the string to a list, remove the characters and then join it back to a string. But then I would have to return the result.
For example:
def popstring(s):
copys = list(s)
copys.pop()
s = ''.join(copys)
s1 = 'abcd'
popstring(s1)
print s1
>>> 'abcd'
I understand why this function doesn't work. The question is more if it is possible to do this in Python or not? If it is, can I do it without copying the string?
Strings are immutable, that means you can not alter the str object. You can of course construct a new string that is some modification of the old string. But you can thus not alter the s object in your code.
A workaround could be to use a container:
class Container:
def __init__(self,data):
self.data = data
And then the popstring thus is given a contain, it inspect the container, and puts something else into it:
def popstring(container):
container.data = container.data[:-1]
s1 = Container('abcd')
popstring(s1)
But again: you did not change the string object itself, you only have put a new string into the container.
You can not perform call by reference in Python, so you can not call a function:
foo(x)
and then alter the variable x: the reference of x is copied, so you can not alter the variable x itself.
Strings are immutable, so your only main option is to create a new string by slicing and assign it back.
#removing the last char
>>> s = 'abcd'
>>> s = s[:-1]
=> 'abc'
Another easy to go method maybe to use list and then join the elements in it to create your string. Ofcourse, it all depends on your preference.
>>> l = ['a', 'b', 'c', 'd']
>>> ''.join(l)
=> 'abcd'
>>> l.pop()
=> 'd'
>>> ''.join(l)
=> 'abc'
Incase you are looking to remove char at a certain index given by pos (index 0 here), you can slice the string as :
>>> s='abcd'
>>> s = s[:pos] + s[pos+1:]
=> 'abd'
You could use bytearray instead:
s1 = bytearray(b'abcd') # NB: must specify encoding if coming from plain string
s1.pop() # now, s1 == bytearray(b'abc')
s1.decode() # returns 'abc'
Caveats:
if you plan to filter arbitrary text (i.e. non pure ASCII), this is a very bad idea to use bytearray
in this age of concurrency and parallelism, it might be a bad idea to use mutation
By the way, perhaps it is an instance of the XY problem. Do you really need to mute strings in the first place?
You can remove parts of a strings and assign it to another string:
s = 'abc'
s2 = s[1:]
print(s2)
You wont do that.. you can still concatenate but you wont pop until its converted into a list..
>>> s = 'hello'
>>> s+='world'
>>> s
'helloworld'
>>> s.pop()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'pop'
>>> list(s).pop()
'd'
>>>
But still You can play with Slicing
>>> s[:-1]
'helloworl'
>>> s[1:]
'elloworld'
>>>

String not in string

So I have two strings
a = "abc" and b = "ab"
Now I am trying to find the characters in a which are not present in b
The code that I have is :
for element in t:
if element not in s:
print element
This is giving some error for large strings. I have not looked into that error yet but I was wondering that another way to do the same thing would be something similar to :
if a not in b:
//further code to identify the element that is not in string b
The piece of code above gives me False when I run it, I don't know how to identify the element which is not present in the second string.
How do I go about this?
This is the sort of thing that a set is really good for:
>>> a = "abc"
>>> b = "abd"
>>> set(a).difference(b)
set(['c'])
This gives you items in a that aren't in b. If you want the items that only appear in one or the other, you can use symmetric_difference:
>>> a = "abc"
>>> b = "abd"
>>> set(a).symmetric_difference(b)
set(['c', 'd'])
Note that your code should work too given proper inputs:
>>> for element in a:
... if element not in b:
... print element
...
c
However, if you're dealing with large sequences, this is much less efficient and it's a bunch more code to write so I don't really recommend it.

Splitting words into variables

I have been doing some Python coding recntly and wanted to do the following:
import shlex
shlex.split("this is a test")
print (shlex.split("this is a test"))
It works, but I want to store the split phrase into different variables, if anyone can help me that would be awesome. Thanks!
Like this?
>>> str = "this is a test"
>>> arr = str.split(" ")
>>> arr
['this', 'is', 'a', 'test']
>>> arr[0]
'this'
>>> a = arr[0]
>>> b = arr[1]
>>> c = arr[2]
>>> d = arr[3]
>>> a
'this'
split() returns a list. Since you probably don't know how many words there will be, you can't declare all the individual variables you will need. Instead, you should use the returned list and use it as appropriate:
words = shlex.split("this is a test");
Note that this stores the list of words in a single variable, rather than trying to store each word in its own variable. I suggest you study more about how to manipulate lists in Python.

Get the first character of the first string in a list?

How would I get the first character from the first string in a list in Python?
It seems that I could use mylist[0][1:] but that does not give me the first character.
>>> mylist = []
>>> mylist.append("asdf")
>>> mylist.append("jkl;")
>>> mylist[0][1:]
'sdf'
You almost had it right. The simplest way is
mylist[0][0] # get the first character from the first item in the list
but
mylist[0][:1] # get up to the first character in the first item in the list
would also work.
You want to end after the first character (character zero), not start after the first character (character zero), which is what the code in your question means.
Get the first character of a bare python string:
>>> mystring = "hello"
>>> print(mystring[0])
h
>>> print(mystring[:1])
h
>>> print(mystring[3])
l
>>> print(mystring[-1])
o
>>> print(mystring[2:3])
l
>>> print(mystring[2:4])
ll
Get the first character from a string in the first position of a python list:
>>> myarray = []
>>> myarray.append("blah")
>>> myarray[0][:1]
'b'
>>> myarray[0][-1]
'h'
>>> myarray[0][1:3]
'la'
Numpy operations are very different than python list operations.
Python has list slicing, indexing and subsetting. Numpy has masking, slicing, subsetting, indexing.
These two videos cleared things up for me.
"Losing your Loops, Fast Numerical Computing with NumPy" by PyCon 2015:
https://youtu.be/EEUXKG97YRw?t=22m22s
"NumPy Beginner | SciPy 2016 Tutorial" by Alexandre Chabot LeClerc:
https://youtu.be/gtejJ3RCddE?t=1h24m54s
Indexing in python starting from 0. You wrote [1:] this would not return you a first char in any case - this will return you a rest(except first char) of string.
If you have the following structure:
mylist = ['base', 'sample', 'test']
And want to get fist char for the first one string(item):
myList[0][0]
>>> b
If all first chars:
[x[0] for x in myList]
>>> ['b', 's', 't']
If you have a text:
text = 'base sample test'
text.split()[0][0]
>>> b
Try mylist[0][0]. This should return the first character.
If your list includes non-strings, e.g. mylist = [0, [1, 's'], 'string'], then the answers on here would not necessarily work. In that case, using next() to find the first string by checking for them via isinstance() would do the trick.
next(e for e in mylist if isinstance(e, str))[:1]
Note that ''[:1] returns '' while ''[0] spits IndexError, so depending on the use case, either could be useful.
The above results in StopIteration if there are no strings in mylist. In that case, one possible implementation is to set the default value to None and take the first character only if a string was found.
first = next((e for e in mylist if isinstance(e, str)), None)
first_char = first[0] if first else None

How to insert a character after every 2 characters in a string

Is there a pythonic way to insert an element into every 2nd element in a string?
I have a string: 'aabbccdd' and I want the end result to be 'aa-bb-cc-dd'.
I am not sure how I would go about doing that.
>>> s = 'aabbccdd'
>>> '-'.join(s[i:i+2] for i in range(0, len(s), 2))
'aa-bb-cc-dd'
Assume the string's length is always an even number,
>>> s = '12345678'
>>> t = iter(s)
>>> '-'.join(a+b for a,b in zip(t, t))
'12-34-56-78'
The t can also be eliminated with
>>> '-'.join(a+b for a,b in zip(s[::2], s[1::2]))
'12-34-56-78'
The algorithm is to group the string into pairs, then join them with the - character.
The code is written like this. Firstly, it is split into odd digits and even digits.
>>> s[::2], s[1::2]
('1357', '2468')
Then the zip function is used to combine them into an iterable of tuples.
>>> list( zip(s[::2], s[1::2]) )
[('1', '2'), ('3', '4'), ('5', '6'), ('7', '8')]
But tuples aren't what we want. This should be a list of strings. This is the purpose of the list comprehension
>>> [a+b for a,b in zip(s[::2], s[1::2])]
['12', '34', '56', '78']
Finally we use str.join() to combine the list.
>>> '-'.join(a+b for a,b in zip(s[::2], s[1::2]))
'12-34-56-78'
The first piece of code is the same idea, but consumes less memory if the string is long.
If you want to preserve the last character if the string has an odd length, then you can modify KennyTM's answer to use itertools.izip_longest:
>>> s = "aabbccd"
>>> from itertools import izip_longest
>>> '-'.join(a+b for a,b in izip_longest(s[::2], s[1::2], fillvalue=""))
'aa-bb-cc-d'
or
>>> t = iter(s)
>>> '-'.join(a+b for a,b in izip_longest(t, t, fillvalue=""))
'aa-bb-cc-d'
I tend to rely on a regular expression for this, as it seems less verbose and is usually faster than all the alternatives. Aside from having to face down the conventional wisdom regarding regular expressions, I'm not sure there's a drawback.
>>> s = 'aabbccdd'
>>> '-'.join(re.findall('..', s))
'aa-bb-cc-dd'
This version is strict about actual pairs though:
>>> t = s + 'e'
>>> '-'.join(re.findall('..', t))
'aa-bb-cc-dd'
... so with a tweak you can be tolerant of odd-length strings:
>>> '-'.join(re.findall('..?', t))
'aa-bb-cc-dd-e'
Usually you're doing this more than once, so maybe get a head start by creating a shortcut ahead of time:
PAIRS = re.compile('..').findall
out = '-'.join(PAIRS(in))
Or what I would use in real code:
def rejoined(src, sep='-', _split=re.compile('..').findall):
return sep.join(_split(src))
>>> rejoined('aabbccdd', sep=':')
'aa:bb:cc:dd'
I use something like this from time to time to create MAC address representations from 6-byte binary input:
>>> addr = b'\xdc\xf7\x09\x11\xa0\x49'
>>> rejoined(addr[::-1].hex(), sep=':')
'49:a0:11:09:f7:dc'
Here is one list comprehension way with conditional value depending of modulus of enumeration, odd last character will be in group alone:
for s in ['aabbccdd','aabbccdde']:
print(''.join([ char if not ind or ind % 2 else '-' + char
for ind,char in enumerate(s)
]
)
)
""" Output:
aa-bb-cc-dd
aa-bb-cc-dd-e
"""
This one-liner does the trick. It will drop the last character if your string has an odd number of characters.
"-".join([''.join(item) for item in zip(mystring1[::2],mystring1[1::2])])
As PEP8 states:
Do not rely on CPython's efficient implementation of in-place string concatenation for statements in the form a += b or a = a + b. This optimization is fragile even in CPython (it only works for some types) and isn't present at all in implementations.
A pythonic way of doing this that avoids this kind of concatenation, and allows you to join iterables other than strings could be:
':'.join(f'{s[i:i+2]}' for i in range(0, len(s), 2))
And another more functional-like way could be:
':'.join(map('{}{}'.format, *(s[::2], s[1::2])))
This second approach has a particular feature (or bug) of only joining pairs of letters. So:
>>> s = 'abcdefghij'
'ab:cd:ef:gh:ij'
and:
>>> s = 'abcdefghi'
'ab:cd:ef:gh'

Categories

Resources