Bagaimana saya bisa memeriksa apakah string mewakili int, tanpa menggunakan coba / kecuali?

467

Apakah ada cara untuk mengetahui apakah string mewakili integer (misalnya '3',, '-17'tetapi tidak '3.14'atau 'asfasfas') Tanpa menggunakan mekanisme coba / kecuali?

is_int('3.14') = False
is_int('-7')   = True
Adam Matan
sumber
23
Mengapa keduanya berusaha melakukan ini "dengan cara yang sulit?" Apa yang salah dengan coba / kecuali?
S.Lott
5
Ya, apa yang salah dengan coba / kecuali? Lebih baik meminta pengampunan daripada izin.
mk12
53
Saya akan bertanya mengapa hal sederhana ini perlu dicoba / kecuali? Sistem pengecualian adalah binatang yang kompleks, tetapi ini adalah masalah yang sederhana.
Aivar
13
@Aivar berhenti menyebarkan FUD. Satu percobaan / kecuali blok bahkan tidak mendekati "kompleks".
Triptych
47
Tapi itu tidak benar-benar FUD. Anda akan secara efektif menulis 4 baris kode, mengharapkan sesuatu meledak, menangkap pengecualian itu dan melakukan default Anda, daripada menggunakan satu liner.
andersonvom

Jawaban:

398

Jika Anda benar-benar kesal menggunakan try/excepts di semua tempat, harap tulis fungsi pembantu:

def RepresentsInt(s):
    try: 
        int(s)
        return True
    except ValueError:
        return False

>>> print RepresentsInt("+123")
True
>>> print RepresentsInt("10.0")
False

Ini akan menjadi kode WAY lebih banyak untuk menutupi semua string yang oleh Python dianggap integer. Saya katakan hanya pythonic yang satu ini.

Triptych
sumber
124
Jadi itu pythonic untuk memecahkan masalah sederhana dengan mekanisme yang kompleks? Ada algoritma untuk mendeteksi int yang ditulis di dalam fungsi "int" - Saya tidak melihat mengapa ini tidak diekspos sebagai fungsi boolean.
Aivar
79
@Aivar: Fungsi 5 baris ini bukan mekanisme yang kompleks.
Triptych
34
Kecuali:>>> print RepresentsInt(10.0) True >>> print RepresentsInt(10.06) True
Dannid
5
Saya kira itu "pythonic" dalam arti bahwa jika Python berpikir string adalah int, begitu juga program Anda. Jika Python berubah, begitu juga program Anda, dan tanpa mengubah satu baris kode pun. Ada beberapa nilai dalam hal itu. Ini mungkin hal yang benar untuk dilakukan tergantung pada keadaan.
Shavais
57
Saya tidak tahu mengapa ini adalah jawaban yang diterima atau memiliki begitu banyak upvotes, karena ini persis kebalikan dari apa yang diminta OP.
FearlessFuture
757

dengan bilangan bulat positif yang dapat Anda gunakan .isdigit:

>>> '16'.isdigit()
True

itu tidak bekerja dengan bilangan bulat negatif. misalkan Anda dapat mencoba yang berikut:

>>> s = '-17'
>>> s.startswith('-') and s[1:].isdigit()
True

itu tidak akan berfungsi dengan '16.0'format, yang mirip dengan intcasting dalam pengertian ini.

edit :

def check_int(s):
    if s[0] in ('-', '+'):
        return s[1:].isdigit()
    return s.isdigit()
SilentGhost
sumber
6
ini tidak menangani "+17" tanpa case khusus tambahan.
Bryan Oakley
1
Anda harus menguji untuk KEDUA kasus: lambda s: s.isdigit () atau (s.startswith ('-') dan s [1:]. Isdigit ())
rob
4
@Roberto: tentu saja Anda harus! dan saya yakin Anda mampu melakukannya!
SilentGhost
22
catatan: u'²'.isdigit()benar tetapi int(u'²')meningkatkan ValueError. Gunakan u.isdecimal()sebagai gantinya. str.isdigit()bergantung pada lokal pada Python 2.
jfs
4
check_int('')akan menaikkan pengecualian alih-alih kembaliFalse
wordbug
97

Anda tahu, saya telah menemukan (dan saya telah menguji ini berulang-ulang) yang mencoba / kecuali tidak melakukan semua itu dengan baik, untuk alasan apa pun. Saya sering mencoba beberapa cara melakukan hal-hal, dan saya tidak berpikir saya pernah menemukan metode yang menggunakan try / kecuali untuk melakukan yang terbaik dari yang diuji, pada kenyataannya tampaknya bagi saya metode-metode itu biasanya keluar dekat dengan terburuk, kalau bukan yang terburuk. Tidak dalam setiap kasus, tetapi dalam banyak kasus. Saya tahu banyak orang mengatakan itu adalah cara "Pythonic", tapi itu satu area di mana saya berpisah dengan mereka. Bagi saya, itu tidak terlalu berkinerja atau sangat elegan, jadi, saya cenderung hanya menggunakannya untuk menjebak dan melaporkan kesalahan.

Saya akan mengeluh bahwa PHP, perl, ruby, C, dan bahkan shell freaking memiliki fungsi sederhana untuk menguji string untuk integer-hood, tetapi karena ketekunan dalam memverifikasi asumsi-asumsi itu membuat saya tersandung! Rupanya kekurangan ini adalah penyakit yang umum.

Berikut ini edit tulisan Bruno yang cepat dan kotor:

import sys, time, re

g_intRegex = re.compile(r"^([+-]?[1-9]\d*|0)$")

testvals = [
    # integers
    0, 1, -1, 1.0, -1.0,
    '0', '0.','0.0', '1', '-1', '+1', '1.0', '-1.0', '+1.0', '06',
    # non-integers
    'abc 123',
    1.1, -1.1, '1.1', '-1.1', '+1.1',
    '1.1.1', '1.1.0', '1.0.1', '1.0.0',
    '1.0.', '1..0', '1..',
    '0.0.', '0..0', '0..',
    'one', object(), (1,2,3), [1,2,3], {'one':'two'},
    # with spaces
    ' 0 ', ' 0.', ' .0','.01 '
]

def isInt_try(v):
    try:     i = int(v)
    except:  return False
    return True

def isInt_str(v):
    v = str(v).strip()
    return v=='0' or (v if v.find('..') > -1 else v.lstrip('-+').rstrip('0').rstrip('.')).isdigit()

def isInt_re(v):
    import re
    if not hasattr(isInt_re, 'intRegex'):
        isInt_re.intRegex = re.compile(r"^([+-]?[1-9]\d*|0)$")
    return isInt_re.intRegex.match(str(v).strip()) is not None

def isInt_re2(v):
    return g_intRegex.match(str(v).strip()) is not None

def check_int(s):
    s = str(s)
    if s[0] in ('-', '+'):
        return s[1:].isdigit()
    return s.isdigit()    


def timeFunc(func, times):
    t1 = time.time()
    for n in range(times):
        for v in testvals: 
            r = func(v)
    t2 = time.time()
    return t2 - t1

def testFuncs(funcs):
    for func in funcs:
        sys.stdout.write( "\t%s\t|" % func.__name__)
    print()
    for v in testvals:
        if type(v) == type(''):
            sys.stdout.write("'%s'" % v)
        else:
            sys.stdout.write("%s" % str(v))
        for func in funcs:
            sys.stdout.write( "\t\t%s\t|" % func(v))
        sys.stdout.write("\r\n") 

if __name__ == '__main__':
    print()
    print("tests..")
    testFuncs((isInt_try, isInt_str, isInt_re, isInt_re2, check_int))
    print()

    print("timings..")
    print("isInt_try:   %6.4f" % timeFunc(isInt_try, 10000))
    print("isInt_str:   %6.4f" % timeFunc(isInt_str, 10000)) 
    print("isInt_re:    %6.4f" % timeFunc(isInt_re, 10000))
    print("isInt_re2:   %6.4f" % timeFunc(isInt_re2, 10000))
    print("check_int:   %6.4f" % timeFunc(check_int, 10000))

Berikut adalah hasil perbandingan kinerja:

timings..
isInt_try:   0.6426
isInt_str:   0.7382
isInt_re:    1.1156
isInt_re2:   0.5344
check_int:   0.3452

Metode AC dapat memindai Sekali Melalui, dan dilakukan. Metode AC yang memindai string sekali melalui akan menjadi Hal yang Benar untuk dilakukan, saya pikir.

EDIT:

Saya telah memperbarui kode di atas untuk bekerja di Python 3.5, dan untuk memasukkan fungsi check_int dari jawaban yang paling banyak dipilih saat ini, dan untuk menggunakan regex paling populer saat ini yang dapat saya temukan untuk pengujian integer-hood. Regex ini menolak string seperti 'abc 123'. Saya telah menambahkan 'abc 123' sebagai nilai tes.

Sangat menarik bagi saya untuk mencatat, pada titik ini, bahwa TIDAK ada fungsi yang diuji, termasuk metode coba, fungsi check_int yang populer, dan regex paling populer untuk pengujian integer-hood, mengembalikan jawaban yang benar untuk semua nilai tes (baik, tergantung pada apa yang Anda pikirkan jawaban yang benar; lihat hasil tes di bawah).

Fungsi built-in int () secara diam-diam memotong bagian fraksional dari angka titik mengambang dan mengembalikan bagian bilangan bulat sebelum desimal, kecuali jika angka titik mengambang pertama kali dikonversi ke string.

Fungsi check_int () mengembalikan false untuk nilai-nilai seperti 0,0 dan 1.0 (yang secara teknis bilangan bulat) dan mengembalikan true untuk nilai-nilai seperti '06'.

Berikut adalah hasil tes (Python 3.5) saat ini:

                  isInt_try |       isInt_str       |       isInt_re        |       isInt_re2       |   check_int   |
    0               True    |               True    |               True    |               True    |       True    |
    1               True    |               True    |               True    |               True    |       True    |
    -1              True    |               True    |               True    |               True    |       True    |
    1.0             True    |               True    |               False   |               False   |       False   |
    -1.0            True    |               True    |               False   |               False   |       False   |
    '0'             True    |               True    |               True    |               True    |       True    |
    '0.'            False   |               True    |               False   |               False   |       False   |
    '0.0'           False   |               True    |               False   |               False   |       False   |
    '1'             True    |               True    |               True    |               True    |       True    |
    '-1'            True    |               True    |               True    |               True    |       True    |
    '+1'            True    |               True    |               True    |               True    |       True    |
    '1.0'           False   |               True    |               False   |               False   |       False   |
    '-1.0'          False   |               True    |               False   |               False   |       False   |
    '+1.0'          False   |               True    |               False   |               False   |       False   |
    '06'            True    |               True    |               False   |               False   |       True    |
    'abc 123'       False   |               False   |               False   |               False   |       False   |
    1.1             True    |               False   |               False   |               False   |       False   |
    -1.1            True    |               False   |               False   |               False   |       False   |
    '1.1'           False   |               False   |               False   |               False   |       False   |
    '-1.1'          False   |               False   |               False   |               False   |       False   |
    '+1.1'          False   |               False   |               False   |               False   |       False   |
    '1.1.1'         False   |               False   |               False   |               False   |       False   |
    '1.1.0'         False   |               False   |               False   |               False   |       False   |
    '1.0.1'         False   |               False   |               False   |               False   |       False   |
    '1.0.0'         False   |               False   |               False   |               False   |       False   |
    '1.0.'          False   |               False   |               False   |               False   |       False   |
    '1..0'          False   |               False   |               False   |               False   |       False   |
    '1..'           False   |               False   |               False   |               False   |       False   |
    '0.0.'          False   |               False   |               False   |               False   |       False   |
    '0..0'          False   |               False   |               False   |               False   |       False   |
    '0..'           False   |               False   |               False   |               False   |       False   |
    'one'           False   |               False   |               False   |               False   |       False   |
    <obj..>         False   |               False   |               False   |               False   |       False   |
    (1, 2, 3)       False   |               False   |               False   |               False   |       False   |
    [1, 2, 3]       False   |               False   |               False   |               False   |       False   |
    {'one': 'two'}  False   |               False   |               False   |               False   |       False   |
    ' 0 '           True    |               True    |               True    |               True    |       False   |
    ' 0.'           False   |               True    |               False   |               False   |       False   |
    ' .0'           False   |               False   |               False   |               False   |       False   |
    '.01 '          False   |               False   |               False   |               False   |       False   |

Baru saja saya mencoba menambahkan fungsi ini:

def isInt_float(s):
    try:
        return float(str(s)).is_integer()
    except:
        return False

Berkinerja hampir sama baiknya dengan check_int (0.3486) dan mengembalikan nilai true seperti 1.0 dan 0.0 dan +1.0 dan 0. dan .0 dan seterusnya. Tetapi juga mengembalikan true untuk '06', jadi. Pilih racunmu, kurasa.

Shavais
sumber
Mungkin sebagian darinya berasal dari fakta bahwa bilangan bulat sedikit berubah-ubah sendiri. Sebuah sistem pemrograman tidak dapat mengambil asumsi mewah bahwa itu akan selalu menjadi representasi desimal. 0x4df, bilangan bulat yang valid di beberapa tempat, dan 0891 tidak di tempat lain. Saya takut memikirkan apa yang mungkin timbul karena adanya unicode dalam jenis cek ini.
PlexQ
3
+1 untuk waktu. Saya setuju bahwa seluruh bisnis pengecualian ini tidak benar-benar elegan untuk pertanyaan sederhana seperti itu. Anda akan mengharapkan metode build in helper untuk masalah umum seperti itu ...
RickyA
9
Saya tahu utas ini pada dasarnya tidak aktif, tetapi +1 untuk mempertimbangkan run-time. Panjang garis tidak selalu menunjukkan kompleksitas yang mendasarinya; dan tentu saja, coba / kecuali mungkin terlihat sederhana (dan mudah dibaca, yang juga penting), tetapi ini adalah operasi yang mahal. Saya berpendapat hierarki preferensi harus selalu terlihat seperti berikut: 1. Solusi eksplisit yang mudah dibaca (milik SilentGhost). 2. Solusi implisit yang mudah dibaca (Triptych's). 3. Tidak ada tiga.
Eric Humphrey
1
Terima kasih atas investigasi tourough Anda mengenai topik yang tampaknya tidak penting ini. Saya akan menggunakan isInt_str (), pythonic atau tidak. Yang mengganggu saya adalah bahwa saya belum menemukan apa pun tentang arti v.find ('..'). Apakah itu semacam sintaks find khusus atau case-edge dari numeric-string?
JackLeEmmerdeur
3
Ya, agak ketinggalan jaman tapi analisisnya masih sangat bagus dan relevan. Dalam Python 3.5 trylebih efisien: isInt_try: 0.6552 / isInt_str: 0.6396 / isInt_re: 1.0296 / isInt_re2: 0.5168.
Dave
40

str.isdigit() harus melakukan trik.

Contoh:

str.isdigit("23") ## True
str.isdigit("abc") ## False
str.isdigit("23.4") ## False

EDIT : Seperti yang ditunjukkan @BuzzMoschetti, cara ini akan gagal untuk angka minus (mis., "-23" ). Jika input_num Anda kurang dari 0, gunakan re.sub (regex_search, regex_replace, content) sebelum menerapkan str.isdigit () . Sebagai contoh:

import re
input_num = "-23"
input_num = re.sub("^-", "", input_num) ## "^" indicates to remove the first "-" only
str.isdigit(input_num) ## True
Catbuilt
sumber
1
Karena -23 menghasilkan false.
Buzz Moschetti
1
@BuzzMoschetti Anda benar. Cara cepat untuk memperbaikinya adalah menghapus tanda minus dengan re.replace (regex_search, regex_replace, content) sebelum menerapkan str.isdigit ()
Catbuilts
27

Gunakan ekspresi reguler:

import re
def RepresentsInt(s):
    return re.match(r"[-+]?\d+$", s) is not None

Jika Anda harus menerima pecahan desimal juga:

def RepresentsInt(s):
    return re.match(r"[-+]?\d+(\.0*)?$", s) is not None

Untuk peningkatan kinerja jika Anda sering melakukan ini, kompilasi ekspresi reguler hanya sekali menggunakan re.compile().

Greg Hewgill
sumber
19
+1: mengungkapkan bahwa ini sangat rumit dan mahal jika dibandingkan dengan coba / kecuali.
S.Lott
2
Saya merasa ini pada dasarnya lebih lambat, versi kustom dari solusi 'isnumerik' yang ditawarkan oleh @SilentGhost.
Greg
@Greg: Karena @SilentGhost tidak menutupi tanda-tanda dengan benar, versi ini benar-benar berfungsi.
S.Lott
1
@ S.Lott: tentu saja, siapa pun yang dapat memposting di SO, akan dapat memperluas contoh saya untuk menutupi tanda-tanda.
SilentGhost
2
ekspresi reguler adalah tentang hal yang paling kompleks dan tidak jelas yang ada, saya menemukan bahwa pemeriksaan sederhana di atas secara substansial lebih jelas, bahkan jika saya pikir itu masih jelek, ini lebih buruk.
PlexQ
18

Solusi RegEx yang tepat akan menggabungkan ide-ide Greg Hewgill dan Nowell, tetapi tidak menggunakan variabel global. Anda dapat melakukannya dengan melampirkan atribut ke metode ini. Juga, saya tahu bahwa itu tidak disukai untuk memasukkan impor dalam suatu metode, tetapi yang saya maksudkan adalah efek "modul malas" seperti http://peak.telecommunity.com/DevCenter/Importing#lazy-imports

sunting: Teknik favorit saya sejauh ini adalah menggunakan metode eksklusif objek String.

#!/usr/bin/env python

# Uses exclusively methods of the String object
def isInteger(i):
    i = str(i)
    return i=='0' or (i if i.find('..') > -1 else i.lstrip('-+').rstrip('0').rstrip('.')).isdigit()

# Uses re module for regex
def isIntegre(i):
    import re
    if not hasattr(isIntegre, '_re'):
        print("I compile only once. Remove this line when you are confident in that.")
        isIntegre._re = re.compile(r"[-+]?\d+(\.0*)?$")
    return isIntegre._re.match(str(i)) is not None

# When executed directly run Unit Tests
if __name__ == '__main__':
    for obj in [
                # integers
                0, 1, -1, 1.0, -1.0,
                '0', '0.','0.0', '1', '-1', '+1', '1.0', '-1.0', '+1.0',
                # non-integers
                1.1, -1.1, '1.1', '-1.1', '+1.1',
                '1.1.1', '1.1.0', '1.0.1', '1.0.0',
                '1.0.', '1..0', '1..',
                '0.0.', '0..0', '0..',
                'one', object(), (1,2,3), [1,2,3], {'one':'two'}
            ]:
        # Notice the integre uses 're' (intended to be humorous)
        integer = ('an integer' if isInteger(obj) else 'NOT an integer')
        integre = ('an integre' if isIntegre(obj) else 'NOT an integre')
        # Make strings look like strings in the output
        if isinstance(obj, str):
            obj = ("'%s'" % (obj,))
        print("%30s is %14s is %14s" % (obj, integer, integre))

Dan untuk anggota kelas yang kurang berani, inilah hasilnya:

I compile only once. Remove this line when you are confident in that.
                             0 is     an integer is     an integre
                             1 is     an integer is     an integre
                            -1 is     an integer is     an integre
                           1.0 is     an integer is     an integre
                          -1.0 is     an integer is     an integre
                           '0' is     an integer is     an integre
                          '0.' is     an integer is     an integre
                         '0.0' is     an integer is     an integre
                           '1' is     an integer is     an integre
                          '-1' is     an integer is     an integre
                          '+1' is     an integer is     an integre
                         '1.0' is     an integer is     an integre
                        '-1.0' is     an integer is     an integre
                        '+1.0' is     an integer is     an integre
                           1.1 is NOT an integer is NOT an integre
                          -1.1 is NOT an integer is NOT an integre
                         '1.1' is NOT an integer is NOT an integre
                        '-1.1' is NOT an integer is NOT an integre
                        '+1.1' is NOT an integer is NOT an integre
                       '1.1.1' is NOT an integer is NOT an integre
                       '1.1.0' is NOT an integer is NOT an integre
                       '1.0.1' is NOT an integer is NOT an integre
                       '1.0.0' is NOT an integer is NOT an integre
                        '1.0.' is NOT an integer is NOT an integre
                        '1..0' is NOT an integer is NOT an integre
                         '1..' is NOT an integer is NOT an integre
                        '0.0.' is NOT an integer is NOT an integre
                        '0..0' is NOT an integer is NOT an integre
                         '0..' is NOT an integer is NOT an integre
                         'one' is NOT an integer is NOT an integre
<object object at 0x103b7d0a0> is NOT an integer is NOT an integre
                     (1, 2, 3) is NOT an integer is NOT an integre
                     [1, 2, 3] is NOT an integer is NOT an integre
                {'one': 'two'} is NOT an integer is NOT an integre
Bruno Bronosky
sumber
4
Saya akan setuju bahwa suite pengujian saya berlebihan. Saya suka membuktikan bahwa kode saya berfungsi saat saya menulisnya. Tapi apakah Anda pikir fungsi isInteger saya berlebihan? Tentunya tidak.
Bruno Bronosky
1
Saya baru saja mendapatkan suara tanpa komentar. Ada apa dengan orang? Saya mengerti bahwa milenium sekarang menggunakan "Suka" sebagai "baca tanda terima". Tetapi apakah mereka sekarang menggunakan down vote sebagai penanda "bukan metode yang saya pilih"? Mungkin mereka tidak menyadari itu mengurangi 2 poin dari reputasi ANDA SENDIRI untuk memilih suara. SO / SE melakukan itu untuk mendorong turunnya pemungutan suara hanya karena informasi yang salah, dalam hal ini saya harap Anda akan meninggalkan komentar .
Bruno Bronosky
5
>>> "+7".lstrip("-+").isdigit()
True
>>> "-7".lstrip("-+").isdigit()
True
>>> "7".lstrip("-+").isdigit()
True
>>> "13.4".lstrip("-+").isdigit()
False

Jadi fungsi Anda adalah:

def is_int(val):
   return val[1].isdigit() and val.lstrip("-+").isdigit()
alkos333
sumber
1
is_int ("2") memunculkan IndexError.
anttikoo
4

Pendekatan Greg Hewgill kehilangan beberapa komponen: "^" yang memimpin hanya cocok dengan awal string, dan menyusun ulang sebelumnya. Tetapi pendekatan ini akan memungkinkan Anda untuk menghindari mencoba: kecuali:

import re
INT_RE = re.compile(r"^[-]?\d+$")
def RepresentsInt(s):
    return INT_RE.match(str(s)) is not None

Saya akan tertarik mengapa Anda mencoba untuk menghindari mencoba: kecuali?

Sekarang
sumber
1
Soal gaya. Saya pikir "coba / kecuali" harus digunakan hanya dengan kesalahan aktual, bukan dengan aliran program normal.
Adam Matan
2
@Udi Pasmon: Python menggunakan cukup banyak coba / kecuali untuk aliran program "normal". Misalnya, setiap iterator berhenti dengan pengecualian yang dimunculkan.
S.Lott
3
-1: Meskipun petunjuk Anda untuk menyusun regex benar, Anda salah dalam mengkritik Greg dalam hal lain: re.match cocok dengan permulaan string, sehingga ^ dalam pola sebenarnya berlebihan. (Ini berbeda ketika Anda menggunakan penelitian ulang).
ThomasH
S.Lott - Apakah aliran ini dianggap masuk akal dalam python? Apa bedanya dengan bahasa lain? Mungkin itu layak untuk pertanyaan terpisah.
Adam Matan
1
Penggunaan / kecuali Python yang berat telah dibahas di sini pada SO. Coba cari '[python] kecuali'
S.Lott
4

Saya harus melakukan ini sepanjang waktu, dan saya memiliki keengganan yang ringan tetapi diakui tidak rasional untuk menggunakan pola coba / kecuali. Saya menggunakan ini:

all([xi in '1234567890' for xi in x])

Itu tidak mengakomodasi angka negatif, jadi Anda bisa menghilangkan satu tanda minus (jika ada), dan kemudian memeriksa apakah hasilnya terdiri dari angka 0-9:

all([xi in '1234567890' for xi in x.replace('-', '', 1)])

Anda juga bisa meneruskan x ke str () jika Anda tidak yakin inputnya berupa string:

all([xi in '1234567890' for xi in str(x).replace('-', '', 1)])

Setidaknya ada dua (tepi?) Kasus di mana ini berantakan:

  1. Ini tidak berfungsi untuk berbagai notasi ilmiah dan / atau eksponensial (mis. 1.2E3, 10 ^ 3, dll.) - keduanya akan menghasilkan False. Saya tidak berpikir jawaban lain mengakomodasi ini baik, dan bahkan Python 3.8 memiliki pendapat yang tidak konsisten, karena type(1E2)memberi <class 'float'>sedangkan type(10^2)memberi<class 'int'> .
  2. Input string kosong memberi True.

Jadi itu tidak akan berfungsi untuk setiap input yang mungkin, tetapi jika Anda dapat mengecualikan notasi ilmiah, notasi eksponensial, dan string kosong, ini merupakan pemeriksaan satu-baris OK yang mengembalikan Falsejika x bukan bilangan bulat danTrue jika x adalah bilangan bulat.

Saya tidak tahu apakah ini pythonic, tetapi hanya satu baris, dan relatif jelas apa yang dilakukan oleh kode tersebut.

mRotten
sumber
Coba / kecuali sepertinya berjalan di atas rumput seseorang (coba) dan kemudian jika / ketika mereka melihat dan menjadi marah (pengecualian) Anda meminta maaf (menangani pengecualian), sedangkan all(xi in '1234567890' for xi in x])pola saya sepertinya lebih seperti meminta izin untuk berjalan melintasi halaman. Saya tidak senang menjadi penanya izin, tapi di sini kita.
mRotten
3

kupikir

s.startswith('-') and s[1:].isdigit()

akan lebih baik untuk menulis ulang ke:

s.replace('-', '').isdigit()

karena s [1:] juga membuat string baru

Tetapi solusi yang jauh lebih baik adalah

s.lstrip('+-').isdigit()
Vladyslav Savchenko
sumber
3
Coba tebak apa yang replaceterjadi? Juga, ini akan salah menerima 5-2, misalnya.
Ry-
Akan melempar IndexError jikas='-'
Anti Earth
s = '-'; s.replace ('-', '') .isdigit () -> False
Vladyslav Savchenko
2

Saya sangat menyukai pos Shavais, tetapi saya menambahkan satu lagi test case (& fungsi isdigit () bawaan):

def isInt_loop(v):
    v = str(v).strip()
    # swapping '0123456789' for '9876543210' makes nominal difference (might have because '1' is toward the beginning of the string)
    numbers = '0123456789'
    for i in v:
        if i not in numbers:
            return False
    return True

def isInt_Digit(v):
    v = str(v).strip()
    return v.isdigit()

dan secara signifikan mengalahkan waktu sisanya:

timings..
isInt_try:   0.4628
isInt_str:   0.3556
isInt_re:    0.4889
isInt_re2:   0.2726
isInt_loop:   0.1842
isInt_Digit:   0.1577

menggunakan normal 2,7 python:

$ python --version
Python 2.7.10

Kedua dua test case yang saya tambahkan (isInt_loop dan isInt_digit) lulus test case yang sama persis (mereka berdua hanya menerima bilangan bulat yang tidak ditandatangani), tetapi saya pikir orang bisa lebih pandai dengan memodifikasi implementasi string (isInt_loop) yang bertentangan dengan isdigit built in () berfungsi, jadi saya memasukkannya, meskipun ada sedikit perbedaan dalam waktu eksekusi. (dan kedua metode ini mengalahkan semuanya, tetapi jangan menangani hal-hal tambahan: "./+/-")

Juga, saya menemukan itu menarik untuk dicatat bahwa regex (metode isInt_re2) mengalahkan perbandingan string dalam tes yang sama yang dilakukan oleh Shavais pada 2012 (saat ini 2018). Mungkin perpustakaan regex telah diperbaiki?

brw59
sumber
1

Ini mungkin cara yang paling mudah dan pythonic untuk mendekatinya menurut saya. Saya tidak melihat solusi ini dan pada dasarnya sama dengan regex, tetapi tanpa regex.

def is_int(test):
    import string
    return not (set(test) - set(string.digits))
Xenlyte
sumber
set(input_string) == set(string.digits)jika kita lewati '-+ 'di awal dan .0, E-1di akhir.
jfs
1

Berikut adalah fungsi yang mem-parsing tanpa memunculkan kesalahan. Ini menangani kasus yang jelas pengembalian Nonekegagalan (menangani tanda-tanda 2000 '- / +' secara default di CPython!):

#!/usr/bin/env python

def get_int(number):
    splits = number.split('.')
    if len(splits) > 2:
        # too many splits
        return None
    if len(splits) == 2 and splits[1]:
        # handle decimal part recursively :-)
        if get_int(splits[1]) != 0:
            return None

    int_part = splits[0].lstrip("+")
    if int_part.startswith('-'):
        # handle minus sign recursively :-)
        return get_int(int_part[1:]) * -1
    # successful 'and' returns last truth-y value (cast is always valid)
    return int_part.isdigit() and int(int_part)

Beberapa tes:

tests = ["0", "0.0", "0.1", "1", "1.1", "1.0", "-1", "-1.1", "-1.0", "-0", "--0", "---3", '.3', '--3.', "+13", "+-1.00", "--+123", "-0.000"]

for t in tests:
    print "get_int(%s) = %s" % (t, get_int(str(t)))

Hasil:

get_int(0) = 0
get_int(0.0) = 0
get_int(0.1) = None
get_int(1) = 1
get_int(1.1) = None
get_int(1.0) = 1
get_int(-1) = -1
get_int(-1.1) = None
get_int(-1.0) = -1
get_int(-0) = 0
get_int(--0) = 0
get_int(---3) = -3
get_int(.3) = None
get_int(--3.) = 3
get_int(+13) = 13
get_int(+-1.00) = -1
get_int(--+123) = 123
get_int(-0.000) = 0

Untuk kebutuhan Anda, Anda dapat menggunakan:

def int_predicate(number):
     return get_int(number) is not None
Reut Sharabani
sumber
1

Saya menyarankan yang berikut ini:

import ast

def is_int(s):
    return isinstance(ast.literal_eval(s), int)

Dari dokumen :

Mengevaluasi simpul ekspresi atau string yang berisi tampilan Python literal atau wadah dengan aman. String atau simpul yang disediakan hanya dapat terdiri dari struktur literal Python berikut: string, byte, angka, tupel, daftar, dikte, set, boolean, dan Tidak ada.

Saya harus mencatat bahwa ini akan menimbulkan ValueErrorpengecualian ketika dipanggil terhadap apa pun yang bukan merupakan Python literal. Karena pertanyaannya meminta solusi tanpa mencoba / kecuali, saya punya solusi tipe Kobayashi-Maru untuk itu:

from ast import literal_eval
from contextlib import suppress

def is_int(s):
    with suppress(ValueError):
        return isinstance(literal_eval(s), int)
    return False

¯ \ _ (ツ) _ / ¯

Jesko Hüttenhain
sumber
0

Saya punya satu kemungkinan yang tidak menggunakan int sama sekali, dan tidak boleh menimbulkan pengecualian kecuali string tidak mewakili angka

float(number)==float(number)//1

Ini harus bekerja untuk semua jenis string yang mengapung menerima, positif, negatif, notasi teknik ...

agoma
sumber
0

Saya kira pertanyaannya terkait dengan kecepatan karena percobaan / kecuali memiliki penalti waktu:

 data uji

Pertama, saya membuat daftar 200 string, 100 string gagal dan 100 string numerik.

from random import shuffle
numbers = [u'+1'] * 100
nonumbers = [u'1abc'] * 100
testlist = numbers + nonumbers
shuffle(testlist)
testlist = np.array(testlist)

 solusi numpy (hanya bekerja dengan array dan unicode)

np.core.defchararray.isnumeric juga dapat bekerja dengan string unicode np.core.defchararray.isnumeric(u'+12')tetapi ia kembali dan array. Jadi, ini solusi yang bagus jika Anda harus melakukan ribuan konversi dan kehilangan data atau data non numerik.

import numpy as np
%timeit np.core.defchararray.isnumeric(testlist)
10000 loops, best of 3: 27.9 µs per loop # 200 numbers per loop

coba / kecuali

def check_num(s):
  try:
    int(s)
    return True
  except:
    return False

def check_list(l):
  return [check_num(e) for e in l]

%timeit check_list(testlist)
1000 loops, best of 3: 217 µs per loop # 200 numbers per loop

Tampaknya solusi numpy jauh lebih cepat.

Carlos Vega
sumber
0

Jika Anda hanya ingin menerima digit ascii rendah, berikut ini beberapa tes untuk melakukannya:

Python 3.7+: (u.isdecimal() and u.isascii())

Python <= 3.6: (u.isdecimal() and u == str(int(u)))

Jawaban lain menyarankan menggunakan .isdigit()atau .isdecimal()tetapi keduanya termasuk beberapa karakter kode-atas seperti '٢'( u'\u0662'):

u = u'\u0662'     # '٢'
u.isdigit()       # True
u.isdecimal()     # True
u.isascii()       # False (Python 3.7+ only)
u == str(int(u))  # False
Krubo
sumber
Ini tidak akan menangani nilai negatif atau nilai padded spasi putih, yang keduanya ditangani dengan baik oleh int().
ShadowRanger
-6

Uh .. Coba ini:

def int_check(a):
    if int(a) == a:
        return True
    else:
        return False

Ini berfungsi jika Anda tidak meletakkan string yang bukan angka.

Dan juga (saya lupa meletakkan nomor cek bagian.), Ada fungsi memeriksa apakah string adalah angka atau tidak. Ini str.isdigit (). Ini sebuah contoh:

a = 2
a.isdigit()

Jika Anda memanggil a.isdigit (), itu akan mengembalikan True.

HaulCozen
sumber
Saya pikir Anda perlu mengutip sekitar nilai yang 2diberikan a.
Luke Woodward
1
Mengapa ini bukan jawaban teratas? Itu menjawab pertanyaan dengan tepat.
Belalang
6
-1 pertanyaan: "Periksa apakah string mewakili int, Tanpa menggunakan Coba / Kecuali?" untuk @Caroline Alexiou
jfs