Как я могу упростить это преобразование с подчеркивания на camelcase в Python?
Я написал нижеприведенную функцию, которая преобразует подчеркивание в верблюд с первым словом в нижнем регистре, то есть "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
Я чувствую код выше, как написано в непитоновом стиле, хотя он работает так, как ожидалось, поэтому, глядя, как упростить код и написать его с помощью методов списка и т.д.
Ответы
Ответ 1
Ваш код в порядке. Проблема, которую я пытаюсь решить, заключается в том, что if first_word_passed
выглядит немного уродливо.
Один из вариантов для этого - генератор. Мы можем легко сделать это, возвращая одну вещь для первой записи, а другую для всех последующих записей. Поскольку Python имеет первоклассные функции, мы можем заставить генератор вернуть функцию, которую мы хотим использовать для обработки каждого слова.
Затем нам просто нужно использовать условный оператор, чтобы мы могли обрабатывать пустые записи, возвращаемые двойными символами подчеркивания, в понимании списка.
Итак, если у нас есть слово, мы вызываем генератор, чтобы заставить функцию использовать для установки случая, и если мы этого не делаем, мы просто используем _
, оставляя генератор нетронутым.
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
Этот файл работает, за исключением того, что первое слово имеет нижний регистр.
def convert(word):
return ''.join(x.capitalize() or '_' for x in word.split('_'))
(Я знаю, что это не совсем то, о чем вы просили, и эта тема довольно старая, но поскольку она довольно заметна при поиске таких конверсий в Google, я думал, что добавлю свое решение, если это поможет кому-то еще).
Ответ 3
Я предпочитаю регулярное выражение лично. Вот тот, который делает трюк для меня:
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
Ответ 4
Здесь более простой. Не может быть идеальным для всех ситуаций, но он соответствует моим требованиям, поскольку я просто конвертирую переменные 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('_')))
Ответ 5
Я думаю, что код в порядке. У вас довольно сложная спецификация, поэтому, если вы настаиваете на том, чтобы раздавить ее в прокрустово ложе из понимания списка, тогда вы, вероятно, повредите ясность кода.
Единственные изменения, которые я внес бы, будут:
- Использовать метод
join
для построения результата в O (n) пространстве и времени, а не для повторных приложений +=
, которые являются O (n²).
- Чтобы добавить docstring.
Вот так:
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('_')))
В зависимости от приложения другое изменение, которое я бы рассмотрел, было бы для memoize функции. Я предполагаю, что вы автоматически переводите исходный код каким-то образом, и вы ожидаете, что одни и те же имена будут встречаться много раз. Таким образом, вы можете хранить конверсию, а не переучитывать ее каждый раз. Простым способом сделать это будет использование декоратора @memoized
из библиотеки декораторов Python.
Ответ 6
Я согласен с Гаретом в том, что код в порядке. Однако, если вам действительно нужен более короткий, но читаемый подход, вы можете попробовать что-то вроде этого:
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
Проблема вызывает функцию, которая возвращает первое строчное слово в первый раз, но потом заглавные слова. Вы можете сделать это с помощью предложения if
, но тогда предложение if
должно оцениваться для каждого слова. Привлекательной альтернативой является использование генератора. Он может вернуть одну вещь при первом вызове и что-то еще при последовательных вызовах, и для этого не требуется столько if
s.
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
Этот алгоритм хорошо работает с цифрой:
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'
Ответ 9
Вот выражение генератора стиля выражения.
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 )
Ответ 10
Здесь моя, полагаясь главным образом на понимание списка, разделение и объединение. Плюс дополнительный параметр для использования разного разделителя:
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()
Ответ 11
Это самый компактный способ сделать это:
def underscore_to_camelcase(value):
words = [word.capitalize() for word in value.split('_')]
words[0]=words[0].lower()
return "".join(words)
Ответ 12
Для regexp sake!
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]
Ответ 13
Другое регулярное выражение:
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()
Он работает для ваших примеров, но может быть неудачным для имен, содержащих цифры - это зависит от того, как вы могли бы их использовать.
Ответ 14
Немного измененная версия:
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)