86

Say I have a string that's of the same form a tuple should be, for example, "(1,2,3,4,5)". What's the easiest way to convert that into an actual tuple? An example of what I want to do is:

tup_string = "(1,2,3,4,5)"
tup = make_tuple(tup_string)

Just running tuple() on the string make the whole thing one big tuple, whereas what I'd like to do is comprehend the string as a tuple. I know I can use a regex for this, but I was hoping there's a less costly way. Ideas?

Niklas B.
  • 92,950
  • 18
  • 194
  • 224
Eli
  • 36,793
  • 40
  • 144
  • 207

5 Answers5

174

It already exists!

>>> from ast import literal_eval as make_tuple
>>> make_tuple("(1,2,3,4,5)")
(1, 2, 3, 4, 5)

Be aware of the corner-case, though:

>>> make_tuple("(1)")
1
>>> make_tuple("(1,)")
(1,)

If your input format works different than Python here, you need to handle that case separately or use another method like tuple(int(x) for x in tup_string[1:-1].split(',')).

Niklas B.
  • 92,950
  • 18
  • 194
  • 224
  • 8
    It's surprising how many questions on SO can be answered with ast.literal_eval, itertools.product, and only a handful of library functions.. – DSM Mar 18 '12 at 23:07
  • @DSM: I'm always glad if it's at least something interesting like `groupby` or `bisect` :) – Niklas B. Mar 18 '12 at 23:08
  • nice one! didn't know about that one XD – Don Question Mar 18 '12 at 23:08
  • in my case I could handle the corner-case for strings with: `not isinstance(make_tuple("('any-string')"), basestring)` and pad make_tuple's output with the extra comma when required. – thadk Mar 27 '17 at 03:50
  • you can use `literal_eval()` to parse `dict()` strings as well! – OHM Jun 19 '23 at 14:02
9

I would recommend using literal_eval.

If you are not comfortable with literal_eval or want to have more control on what gets converted you can also disassemble the string, convert the values and recreate the tuple.

Sounds more complicated than it is, really, it's a one-liner:

eg = '(102,117,108)'
eg_tuple = map(int, eg.replace('(','').replace(')','').split(',')))

This would throw a ValueError if any element (string) in the tuple is not convertible to int, like, for example the '1.2' in the string: '(1.2, 3, 4)'.


The same can be achieved with regex:

import re
eg = '(102,117,108)'
et_tuple = tuple(map(int, re.findall(r'[0-9]+', eg)))
j-i-l
  • 10,281
  • 3
  • 53
  • 70
  • 1
    Since you know the parenthesis will be at both ends of the string, using strip() instead of replace(). Then it becomes `eg_tuple = eg.strip('()').split(',')` – R. Navega Aug 27 '18 at 10:27
1

You can parse your string without SyntaxError

def parse_tuple(string):
    try:
        s = eval(string)
        if type(s) == tuple:
            return s
        return
    except:
        return

This function return the Tuple if parse is success. Otherwise return None.

print parse_tuple("('A', 'B', 'C')")
Shameem
  • 2,664
  • 17
  • 21
-1

Simplest way to do this (without ast):

def _make_tuple(self, val: str) -> tuple:
    """
    Convert a string representation of a tuple to a tuple.

    Example:
        "('item1', 'item2')" -> ('item1', 'item2')
    """
    return tuple(v.lstrip("('").rstrip("')") for v in val.split(", "))
trybek_4
  • 29
  • 3
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – shamnad sherief Jan 24 '23 at 18:13
  • This will only work in very limited cases. Tuples can contain more than just strings; a tuple of strings can represent the strings with double-quotes rather than single-quotes depending on the string contents; and escape sequences will also cause a problem. – Karl Knechtel Jan 25 '23 at 21:56
-3

We can also parse it by ourself. Let's say we have tuple returned by Python like below:

((2, 'C/C++', 0, 'clang_cpp'), (3, 'Python相关', 0, 'python'))

Here're how we do it

First, we keep reading the characters in the tuple string but stores the last left semicolon's position and how many semicolons we have meet (we can call it left semicolon level, as so for right semicolons), whenever we meet a right semicolon, we do things below:

  1. Take a substring from last semicolon to current right semicolon.(In this substring, there is no more semicolons, we just split it into array by ",". Let's say the new array is M)
  2. Then we append M to our result array, which array will stores allM.
  3. Thirdly, delete the substring we taken from the original string. Finally, do the same things like step 1 till the right and left semicolon's level comes to 0.

JavaScript code is like below:

function parseTuple(t){
    var lc = "(";
    var rc = ")";
    var lc_level = 0;
    var rc_level = 0;
    var last_lc = 0;
    var last_rc = 0;
    var result = [];
    for(i=0;i<t.length;i++){
        if(t[i] == lc){
            lc_level++;
            last_lc = i;
        }else if(t[i] == rc){
            rc_level++;
            last_rc = i;
        }
        if(rc_level == 1){
            var substr = t.slice(last_lc+1,last_rc);
            var data = substr.split(",");
            result.push(data);
            lc_level--;
            rc_level--;
            i = 0;
            t = t.slice(0,last_lc) + t.substring(last_rc+1);
        }
        if(lc_level == rc_level && lc_level==0){
            break;
        }
    }
    return result;
}
hcnak
  • 428
  • 7
  • 18
  • The question is specifically about Python. Javascript doesn't have tuples in the first place, so there would be no demand for such code. – Karl Knechtel Jan 25 '23 at 21:57