Python, более умный способ преобразования строки в целое - PullRequest
2 голосов
/ 23 марта 2010

Я написал этот код для преобразования строки в таком формате "0 (532) 222 22 22" в целое число, как 05322222222.

class Phone():
    def __init__(self,input):
        self.phone = input
    def __str__(self):
        return self.phone
    #convert to integer.
    def to_int(self):
        return int((self.phone).replace(" ","").replace("(","").replace(")",""))

test = Phone("0(532) 222 22 22")
print test.to_int()

Чувствовать себя неуклюже, используя 3 метода замены, чтобы решить эту проблему. Мне интересно, есть ли лучшее решение?

Ответы [ 4 ]

8 голосов
/ 23 марта 2010
p = "0(532) 222 22 22"
print ''.join([x for x in p if x.isdigit()])

Обратите внимание, что вы потеряете ведущий ноль, если захотите преобразовать его в int (как вы предложили в заголовке).Если вы хотите сделать это, просто оберните вышеупомянутое в int() вызов.Телефонный номер имеет больше смысла как строка (на мой взгляд).

6 голосов
/ 23 марта 2010

В Python 2.6 или 2.7
(self.phone).translate(None,' ()')
удалит все пробелы или ( или ) из телефонной строки.Подробности смотрите в Документе Python 2.6 по str.translate .

В Python 3.x str.translate () принимает отображение (а не две строки, как показано выше).Таким образом, соответствующий фрагмент кода выглядит примерно так, используя str.maketrans () для создания сопоставления.
'(self.phone).translate(str.maketrans('','', '()-/ '))
Подробнее см. Документ Python 3.1 по str.translate .

1 голос
/ 24 марта 2010

Как насчет использования регулярных выражений?

Пример:

>>> import re
>>> num = '0(532) 222 22 22'
>>> re.sub('[\D]', '', num) # Match all non-digits ([\D]), replace them with empty string, where found in the `num` variable.
'05322222222'

Предложение, сделанное ChristopheD, будет прекрасно работать, но не так эффективно.

Ниже приведена тестовая программа, демонстрирующая это с использованием модуля dis (см. PyMOTW Дуга Хеллмана о модуле здесь для получения более подробной информации).

TEST_PHONE_NUM = '0(532) 222 22 22'

def replace_method():
    print (TEST_PHONE_NUM).replace(" ","").replace("(","").replace(")","")

def list_comp_is_digit_method():
    print ''.join([x for x in TEST_PHONE_NUM if x.isdigit()])

def translate_method():
    print (TEST_PHONE_NUM).translate(None,' ()')

import re
def regex_method():
    print re.sub('[\D]', '', TEST_PHONE_NUM)

if __name__ == '__main__':
    from dis import dis

    print 'replace_method:'
    dis(replace_method)
    print
    print

    print 'list_comp_is_digit_method:'
    dis(list_comp_is_digit_method)

    print
    print

    print 'translate_method:'
    dis(translate_method)

    print
    print
    print "regex_method:"
    dis(phone_digit_strip_regex)
    print

Выход:

replace_method:
  5       0 LOAD_GLOBAL              0 (TEST_PHONE_NUM)
          3 LOAD_ATTR                1 (replace)
          6 LOAD_CONST               1 (' ')
          9 LOAD_CONST               2 ('')
         12 CALL_FUNCTION            2
         15 LOAD_ATTR                1 (replace)
         18 LOAD_CONST               3 ('(')
         21 LOAD_CONST               2 ('')
         24 CALL_FUNCTION            2
         27 LOAD_ATTR                1 (replace)
         30 LOAD_CONST               4 (')')
         33 LOAD_CONST               2 ('')
         36 CALL_FUNCTION            2
         39 PRINT_ITEM          
         40 PRINT_NEWLINE       
         41 LOAD_CONST               0 (None)
         44 RETURN_VALUE   

phone_digit_strip_list_comp:
  3           0 LOAD_CONST               1 ('0(532) 222 22 22')
              3 STORE_FAST               0 (phone)

  4           6 LOAD_CONST               2 ('')
              9 LOAD_ATTR                0 (join)
             12 BUILD_LIST               0
             15 DUP_TOP             
             16 STORE_FAST               1 (_[1])
             19 LOAD_GLOBAL              1 (test_phone_num)
             22 GET_ITER            
             23 FOR_ITER                30 (to 56)
             26 STORE_FAST               2 (x)
             29 LOAD_FAST                2 (x)
             32 LOAD_ATTR                2 (isdigit)
             35 CALL_FUNCTION            0
             38 JUMP_IF_FALSE           11 (to 52)
             41 POP_TOP             
             42 LOAD_FAST                1 (_[1])
             45 LOAD_FAST                2 (x)
             48 LIST_APPEND         
             49 JUMP_ABSOLUTE           23
             52 POP_TOP             
             53 JUMP_ABSOLUTE           23
             56 DELETE_FAST              1 (_[1])
             59 CALL_FUNCTION            1
             62 PRINT_ITEM          
             63 PRINT_NEWLINE       
             64 LOAD_CONST               0 (None)
             67 RETURN_VALUE   

translate_method:
  11           0 LOAD_GLOBAL              0 (TEST_PHONE_NUM)
               3 LOAD_ATTR                1 (translate)
               6 LOAD_CONST               0 (None)
               9 LOAD_CONST               1 (' ()')
              12 CALL_FUNCTION            2
              15 PRINT_ITEM          
              16 PRINT_NEWLINE       
              17 LOAD_CONST               0 (None)
              20 RETURN_VALUE      

phone_digit_strip_regex:
  8       0 LOAD_CONST               1 ('0(532) 222 22 22')
          3 STORE_FAST               0 (phone)

  9       6 LOAD_GLOBAL              0 (re)
          9 LOAD_ATTR                1 (sub)
         12 LOAD_CONST               2 ('[\\D]')
         15 LOAD_CONST               3 ('')
         18 LOAD_GLOBAL              2 (test_phone_num)
         21 CALL_FUNCTION            3
         24 PRINT_ITEM          
         25 PRINT_NEWLINE       
         26 LOAD_CONST               0 (None)
         29 RETURN_VALUE        

Метод перевода будет наиболее эффективным, хотя и полагается на py2.6 +. Регулярное выражение немного менее эффективно, но более совместимо (что я считаю необходимым для вас). Исходный метод замены добавит 6 дополнительных инструкций для каждой замены, в то время как все остальные будут оставаться постоянными.

В примечании, храните свои номера телефонов в виде строк, чтобы иметь дело с ведущими нулями, и используйте форматер телефона, где это необходимо. Поверь мне, это укусило меня раньше.

0 голосов
/ 25 марта 2010

SilentGhost: dis.dis действительно демонстрирует базовую концептуальную / исполнительную сложность. В конце концов, ФП пожаловался на то, что оригинальная цепь замены была слишком «неуклюжей», но не слишком «медленной».

я рекомендую не использовать регулярные выражения там, где это не является неизбежным; в противном случае они просто добавляют концептуальные накладные расходы и снижение скорости. использовать translate() здесь, ИМХО, просто неправильный инструмент, и нигде не так концептуально просто и универсально, как оригинальная цепочка замены.

так что вы говорите тамайто, а я говорю томахто: оригинальное решение довольно хорошо с точки зрения ясности и универсальности. это совсем не коряво. чтобы сделать его немного более плотным и более параметризованным, рассмотрите возможность его изменения на

phone_nr_translations = [ 
    ( ' ', '', ), 
    ( '(', '', ), 
    ( ')', '', ), ]

def sanitize_phone_nr( phone_nr ):
  R = phone_nr
  for probe, replacement in phone_nr_translations:
    R = R.replace( probe, replacement )
  return R

в этом специальном приложении, конечно, вы действительно хотите просто удалить любые нежелательные символы, так что вы можете упростить это:

probes = ' ()'

def sanitize_phone_nr( phone_nr ):
  R = phone_nr
  for probe in probes:
    R = R.replace( probe, '' )
  return R

Если подумать, мне не совсем понятно, почему вы хотите превратить номер телефона в целое число - это просто неправильный тип данных. это может быть продемонстрировано тем фактом, что, по крайней мере, в мобильных сетях + и # и, возможно, больше являются допустимыми символами в строке набора (dial, string - видите?).

но кроме этого, дезинфекция телефонного номера пользователя, чтобы получить нормализованное и безопасное представление, является очень и очень серьезной проблемой - только я чувствую, что ваша методология слишком конкретна. почему бы не переписать метод очистки на что-то очень общее, не становясь более сложным? в конце концов, как вы можете быть уверены, что ваши пользователи никогда не будут вводить другие девиантные символы в это поле веб-формы?

так что вы на самом деле хотите не dis - мелкие специфические символы (в юникоде 5.1 определено около ста тысяч определенных кодовых точек, так как их догнать?), Но разрешить те самые символы, которые считаются допустимыми в строках набора. и вы можете сделать это с помощью регулярного выражения ...

from re import compile as _new_regex
illegal_phone_nr_chrs_re = _new_regex( r"[^0-9#+]" )

def sanitize_phone_nr( phone_nr ):
  return illegal_phone_nr_chrs_re.sub( '', phone_nr )

... или с набором:

legal_phone_nr_chrs = set( '0123456789#+' )

def sanitize_phone_nr( phone_nr ):
    return ''.join( 
        chr for chr in phone_nr 
            if chr in legal_phone_nr_chrs )

эта последняя строфа вполне может быть записана в одну строку. недостаток этого решения состоит в том, что вы перебираете входные символы из Python, не используя потенциальный обход C-спидера, предложенный str.replace() или даже регулярным выражением. тем не менее, производительность в любом случае будет зависеть от ожидаемой схемы использования (я уверен, что вы сначала усекаете свои номера телефонов, верно? так что это будет много небольших строк для обработки, а не несколько больших).

обратите внимание на несколько моментов: я стремлюсь к ясности, поэтому стараюсь избегать чрезмерного использования сокращений. chr для character, nr для number и R для возвращаемого значения (скорее всего, тьфу, retval, где используется в стандартной библиотеке) находятся в моей книге стилей. программирование - это то, что нужно понимать и делать, а не программисты, пишущие код, который приближается к пространственной эффективности gzip. теперь посмотрите, последнее решение делает довольно многое из того, что удалось сделать ОП (и даже больше), в ...

legal_phone_nr_chrs = set( '0123456789#+' )
def sanitize_phone_nr( phone_nr ): return ''.join( chr for chr in phone_nr if chr in legal_phone_nr_chrs )

... две строки кода, если необходимо, тогда как код ОП ...

class Phone():
    def __init__  ( self, input ): self.phone = self._sanitize( input )
    def __str__   ( self        ): return self.phone
    def _sanitize ( self, input ): return input.replace( ' ', '' ).replace( '(', '' ).replace( ')', '' )

... вряд ли можно сжать ниже четырех строк. Посмотрите, какой дополнительный багаж дает вам решение строго ООП? я верю, что это может быть оставлено вне изображения большую часть времени.

...