如何在Python中简化从下划线到camelcase的转换?

时间:2010-11-29 12:00:44

标签: python

我编写了下面的函数,它将下划线转换为camelcase,第一个单词为小写,即“get_this_value” - > “getThisValue”。我还要求保留前导和尾随下划线以及双重(三重等)下划线,如果有的话,即

"_get__this_value_" -> "_get_ThisValue_".

代码:

def underscore_to_camelcase(value):
    output = ""
    first_word_passed = False
    for word in value.split("_"):
        if not word:
            output += "_"
            continue
        if first_word_passed:
            output += word.capitalize()
        else:
            output += word.lower()
        first_word_passed = True
    return output

我感觉上面的代码是以非Pythonic风格编写的,虽然它按预期工作,所以看看如何简化代码并使用列表推导等编写它。

16 个答案:

答案 0 :(得分:41)

除了将第一个单词保留为小写之外,这个有效。

def convert(word):
    return ''.join(x.capitalize() or '_' for x in word.split('_'))

(我知道这不是你要求的,这个帖子已经很老了,但是因为在Google上搜索这样的转换时它非常突出我以为我会添加我的解决方案,以防它帮助其他人)

答案 1 :(得分:29)

你的代码很好。我认为你要解决的问题是if first_word_passed看起来有点难看。

解决此问题的一个选择是生成器。我们可以轻松地将此返回一个用于第一个条目,另一个用于所有后续条目。由于Python具有一流的功能,我们可以让生成器返回我们想要用来处理每个单词的函数。

然后我们只需要使用the conditional operator,这样我们就可以处理列表解析中双下划线返回的空白条目。

因此,如果我们有一个单词,我们调用生成器来获取用于设置大小写的函数,如果我们不这样做,我们只使用_保持生成器不受影响。

def underscore_to_camelcase(value):
    def camelcase(): 
        yield str.lower
        while True:
            yield str.capitalize

    c = camelcase()
    return "".join(c.next()(x) if x else '_' for x in value.split("_"))

答案 2 :(得分:18)

我个人更喜欢正则表达式。这是为我做的诀窍:

import re
def to_camelcase(s):
    return re.sub(r'(?!^)_([a-zA-Z])', lambda m: m.group(1).upper(), s)

使用unutbu的测试:

tests = [('get__this_value', 'get_ThisValue'),
         ('_get__this_value', '_get_ThisValue'),
         ('_get__this_value_', '_get_ThisValue_'),
         ('get_this_value', 'getThisValue'),
         ('get__this__value', 'get_This_Value')]

for test, expected in tests:
    assert to_camelcase(test) == expected

答案 3 :(得分:7)

这是一个更简单的。可能并不适合所有情况,但它符合我的要求,因为我只是将具有特定格式的python变量转换为驼峰式。除了第一个单词之外,这确实很有用。

def underscore_to_camelcase(text):
"""
Converts underscore_delimited_text to camelCase.
Useful for JSON output
"""
    return ''.join(word.title() if i else word for i, word in enumerate(text.split('_')))

答案 4 :(得分:3)

我认为代码很好。你有一个相当复杂的规范,所以如果你坚持将它压缩到列表理解的Procrustean床上,那么你可能会损害代码的清晰度。

我所做的唯一改变是:

  1. 使用join方法在O( n )空间和时间中构建结果,而不是重复+=的应用程序,即O( n ²)。
  2. 添加文档字符串。
  3. 像这样:

    def underscore_to_camelcase(s):
        """Take the underscore-separated string s and return a camelCase
        equivalent.  Initial and final underscores are preserved, and medial
        pairs of underscores are turned into a single underscore."""
        def camelcase_words(words):
            first_word_passed = False
            for word in words:
                if not word:
                    yield "_"
                    continue
                if first_word_passed:
                    yield word.capitalize()
                else:
                    yield word.lower()
                first_word_passed = True
        return ''.join(camelcase_words(s.split('_')))
    

    根据应用程序的不同,我会考虑进行另一项更改,即记住该功能。我假设您以某种方式自动翻译源代码,并且您希望多次出现相同的名称。所以你不妨存储转换,而不是每次都重新计算它。一种简单的方法是使用Python decorator library中的@memoized装饰器。

答案 5 :(得分:3)

该算法在数字上表现良好:

import re

PATTERN = re.compile(r'''
    (?<!\A) # not at the start of the string
    _
    (?=[a-zA-Z]) # followed by a letter
    ''', re.X)

def camelize(value):
    tokens = PATTERN.split(value)
    response = tokens.pop(0).lower()
    for remain in tokens:
        response += remain.capitalize()
    return response

示例:

>>> camelize('Foo')
'foo'
>>> camelize('_Foo')
'_foo'
>>> camelize('Foo_')
'foo_'
>>> camelize('Foo_Bar')
'fooBar'
>>> camelize('Foo__Bar')
'foo_Bar'
>>> camelize('9')
'9'
>>> camelize('9_foo')
'9Foo'
>>> camelize('foo_9')
'foo_9'
>>> camelize('foo_9_bar')
'foo_9Bar'
>>> camelize('foo__9__bar')
'foo__9_Bar'

答案 6 :(得分:2)

我同意Gareth认为代码没问题。但是,如果你真的想要一个更简短但可读的方法,你可以尝试这样的事情:

def underscore_to_camelcase(value):
    # Make a list of capitalized words and underscores to be preserved
    capitalized_words = [w.capitalize() if w else '_' for w in value.split('_')]

    # Convert the first word to lowercase
    for i, word in enumerate(capitalized_words):
        if word != '_':
            capitalized_words[i] = word.lower()
            break

    # Join all words to a single string and return it
    return "".join(capitalized_words)

答案 7 :(得分:2)

该问题需要一个第一次返回小写单词的函数,但之后会返回大写单词。您可以使用if子句执行此操作,但必须针对每个单词评估if子句。一个有吸引力的替代方案是使用发电机。它可以在第一次调用时返回一个东西,在连续调用时返回其他内容,并且它不需要if个。{/ p>

def lower_camelcase(seq):
    it=iter(seq)
    for word in it:
        yield word.lower()
        if word.isalnum(): break
    for word in it:
        yield word.capitalize()

def underscore_to_camelcase(text):
    return ''.join(lower_camelcase(word if word else '_' for word in text.split('_')))

以下是一些测试代码,表明它有效:

tests=[('get__this_value','get_ThisValue'),
       ('_get__this_value','_get_ThisValue'),
       ('_get__this_value_','_get_ThisValue_'),
       ('get_this_value','getThisValue'),        
       ('get__this__value','get_This_Value'),        
       ]
for test,answer in tests:
    result=underscore_to_camelcase(test)
    try:
        assert result==answer
    except AssertionError:
        print('{r!r} != {a!r}'.format(r=result,a=answer))

答案 8 :(得分:1)

这是列表理解样式生成器表达式。

from itertools import count
def underscore_to_camelcase(value):
    words = value.split('_')
    counter = count()
    return ''.join('_' if w == '' else w.capitalize() if counter.next() else w for w in words )

答案 9 :(得分:1)

这是我的,主要依靠列表理解,分裂和加入。加上可选参数使用不同的分隔符:

def underscore_to_camel(in_str, delim="_"):
    chunks = in_str.split(delim)
    chunks[1:] = [_.title() for _ in chunks[1:]]
    return "".join(chunks)

另外,为了完整起见,包括之前作为反向解释的另一个问题的解决方案(不是我自己的代码,只是重复以便于参考):

first_cap_re = re.compile('(.)([A-Z][a-z]+)')
all_cap_re = re.compile('([a-z0-9])([A-Z])')
def camel_to_underscore(in_str):
    s1 = first_cap_re.sub(r'\1_\2', name)
    return all_cap_re.sub(r'\1_\2', s1).lower()

答案 10 :(得分:0)

这是最简洁的方法:

def underscore_to_camelcase(value):
    words = [word.capitalize() for word in value.split('_')]
    words[0]=words[0].lower()
    return "".join(words)

答案 11 :(得分:0)

为了regexp!

import re

def underscore_to_camelcase(value):
    def rep(m):
        if m.group(1) != None:
            return m.group(2) + m.group(3).lower() + '_'
        else:
            return m.group(3).capitalize()

    ret, nb_repl = re.subn(r'(^)?(_*)([a-zA-Z]+)', rep, value)
    return ret if (nb_repl > 1) else ret[:-1]

答案 12 :(得分:0)

另一种正则表达式解决方案:

import re

def conv(s):
    """Convert underscore-separated strings to camelCase equivalents.

    >>> conv('get')
    'get'
    >>> conv('_get')
    '_get'
    >>> conv('get_this_value')
    'getThisValue'
    >>> conv('__get__this_value_')
    '_get_ThisValue_'
    >>> conv('_get__this_value__')
    '_get_ThisValue_'
    >>> conv('___get_this_value')
    '_getThisValue'

    """
    # convert case:
    s = re.sub(r'(_*[A-Z])', lambda m: m.group(1).lower(), s.title(), count=1)
    # remove/normalize underscores:
    s = re.sub(r'__+|^_+|_+$', '|', s).replace('_', '').replace('|', '_')
    return s

if __name__ == "__main__":
    import doctest
    doctest.testmod()

它适用于您的示例,但对于包含数字的名称可能会失败 - 这取决于您如何将它们大写。

答案 13 :(得分:0)

稍加修改的版本:

import re

def underscore_to_camelcase(value):
    first = True
    res = []

    for u,w in re.findall('([_]*)([^_]*)',value):
        if first:
            res.append(u+w)
            first = False
        elif len(w)==0:    # trailing underscores
            res.append(u)
        else:   # trim an underscore and capitalize
            res.append(u[:-1] + w.title())

    return ''.join(res)

答案 14 :(得分:0)

我知道这已经得到了解答,但我想出了一些语法糖来处理一个特殊情况,所选择的答案没有(带有dunders的词,即&#34; my_word__is _____丑陋&#34;到&#34; myWordIsUgly&#34)。显然,这可以分解成多行,但我喜欢将它放在一个上面的挑战。为清晰起见,我添加了换行符。

def underscore_to_camel(in_string):
return "".join(
    list(
        map(
            lambda index_word:
                index_word[1].lower() if index_word[0] == 0
                else index_word[1][0].upper() + (index_word[1][1:] if len(index_word[1]) > 0 else ""),
            list(enumerate(re.split(re.compile(r"_+"), in_string)
                           )
                 )
        )
    )
)

答案 15 :(得分:0)

    def convert(word):
        if not isinstance(word, str):
            return word
        if word.startswith("_"):
            word = word[1:]
        words = word.split("_")
        _words = []
        for idx, _word in enumerate(words):
            if idx == 0:
                _words.append(_word)
                continue
            _words.append(_word.capitalize())
        return ''.join(_words)