객체가 숫자인지 확인하는 가장 비단뱀적인 방법은 무엇입니까?


114

임의의 파이썬 객체가 주어지면 그것이 숫자인지 결정하는 가장 좋은 방법은 무엇입니까? 여기에서 is로 정의됩니다 acts like a number in certain circumstances.

예를 들어 벡터 클래스를 작성한다고 가정 해보십시오. 다른 벡터가 주어지면 내적을 찾고 싶습니다. 스칼라가 주어지면 전체 벡터를 스케일링하려고합니다.

어떤 경우 검사 int, float, long, bool성가신 숫자처럼 행동 수있는 사용자 정의 개체에 적용되지 않습니다. 그러나 __mul__예를 들어를 확인하는 것은 내가 방금 설명한 벡터 클래스가를 정의 할 것이기 때문에 충분하지 __mul__않지만 내가 원하는 종류의 숫자는 아닙니다.

답변:


135

모듈 Number에서 사용 numbers하여 테스트합니다 isinstance(n, Number)(2.6부터 사용 가능).

>>> from numbers import Number
... from decimal import Decimal
... from fractions import Fraction
... for n in [2, 2.0, Decimal('2.0'), complex(2, 0), Fraction(2, 1), '2']:
...     print(f'{n!r:>14} {isinstance(n, Number)}')
              2 True
            2.0 True
 Decimal('2.0') True
         (2+0j) True
 Fraction(2, 1) True
            '2' False

물론 이것은 덕 타이핑과는 반대입니다. 객체 가 무엇인지보다 객체가 작동 하는 방식에 더 관심 있다면 숫자가있는 것처럼 작업을 수행하고 예외를 사용하여 달리 알려주십시오.


3
벡터에 X를 곱할 때는 오리보다는 똑똑한 일을하는 것이 좋습니다.이 경우 X 무엇인지에 따라 다른 일을하고 싶습니다 . (그것은 곱셈 뭔가 역할을 할 수 있지만, 결과는 무의미 수 있습니다.)
예브게니 Sergeev에게

3
이 대답은 True가 숫자라고 말할 것입니다. 아마 항상 원하는 것은 아닙니다. 부울을 제외하고 (검증 fe를 생각하십시오), 나는 말할 것입니다isinstance(value, Number) and type(value) != bool
Yo Ludke

32

어떤 물체가 있는지 확인하고 싶습니다.

특정 상황에서 숫자처럼 행동

Python 2.5 또는 이전 버전을 사용하는 경우 유일한 실제 방법은 이러한 "특정 상황"중 일부를 확인하고 확인하는 것입니다.

2.6 이상에서는 numbers.Numberisinstance 와 함께 사용할 수 있습니다 . 이 목적을 위해 정확히 존재하는 추상 기본 클래스 (ABC) ( collections2.6부터 다시 시작하는 다양한 형태의 컬렉션 / 컨테이너 에 대해 모듈에 더 많은 ABC가 존재합니다 . 또한 해당 릴리스에서만 필요한 경우 자체 추상 기본 클래스를 쉽게 추가 할 수 있습니다.

Bach가 2.5 0이하인 경우 " 추가 할 수 있지만 반복 할 수 없음"은 경우에 따라 좋은 정의가 될 수 있습니다. 그러나, 당신은 정말 당신이 당신이 고려해야 할 것 "숫자"확실히 할 수 있어야한다는 요구하고 있다는 것입니다 무엇인지, 자신을 요청해야 , 그리고 절대적으로 무엇을해야 할 수없는 및 확인 - 할.

이것은 2.6 이상에서 필요할 수 있습니다. 아직 등록되지 않은 관심있는 유형을 추가하기 위해 자신의 등록을 할 목적으로 numbers.Numbers필요할 수 있습니다. 이는 숫자라고 주장하는 일부 유형 을 제외 하려는 경우 ABC에는 unregister방법 이 없기 때문에 처리 할 수 ​​없습니다. 더 많은주의가 필요 합니다. [[예를 들어 자신 만의 ABC를 만들어서 WeirdNum그런 모든 이상한 유형을 등록한 다음 isinstance진행하기 전에 먼저 구제를받을 수 있는지 확인하십시오. 성공적으로 계속 isinstance하려면 정상 을 확인합니다 numbers.Number.

BTW, x무언가를 할 수 있는지 여부를 확인해야하는 경우 일반적으로 다음과 같이 시도해야합니다.

try: 0 + x
except TypeError: canadd=False
else: canadd=True

__add__예를 들어 모든 시퀀스에는 다른 시퀀스와 연결하기위한 목적으로 사용되기 때문에 그 자체가 존재 한다고해서 유용한 것은 없습니다. 예를 들어,이 검사는 "숫자는 그러한 것의 시퀀스가 ​​내장 함수에 대한 유효한 단일 인수가되는 것입니다"라는 정의와 동일합니다 sum. 완전히 이상한 유형 (예 : a ZeroDivisionError또는 ValueError& c 와 같이 0으로 합산 될 때 "잘못된"예외를 발생시키는 유형 )은 예외를 전파하지만 괜찮습니다. 사용자에게 그러한 미친 유형은 좋은 상태에서 허용되지 않는다는 것을 최대한 빨리 알립니다. 회사;-); 그러나 스칼라로 합산 할 수있는 "벡터"(Python의 표준 라이브러리에는 없습니다. 물론 타사 확장으로 널리 사용됨)도 여기서 잘못된 결과를 제공하므로 (예 :하나 "반복자가 될 수 없습니다"(예를 들어, 그 확인 iter(x)인상을 TypeError, 또는 특별한 방법의 존재를 __iter__- 당신은 2.5에있어 이전함으로써 자신의 확인이 필요한 경우).

이러한 복잡성을 간단히 살펴보면 가능할 때마다 추상 기본 클래스에 의존하도록 동기를 부여하기에 충분할 수 있습니다 ... ;-).


그러나 숫자 모듈에는 숫자에 대한 ABC가 있습니다. 이것이 문서가 주장하는 바입니다. "숫자 모듈 (PEP 3141)은 더 많은 연산을 점진적으로 정의하는 숫자 추상 기본 클래스의 계층을 정의합니다."
Steven Rumbalski 2010 년

17

이것은 예외가 실제로 빛나는 좋은 예입니다. 숫자 유형으로 수행 할 작업을 수행하고 TypeError다른 모든 것에서를 잡으십시오 .

그러나 분명히 이것은 작동이 작동 하는지 확인하는 것이지 의미가 있는지 여부 는 확인 하지 않습니다 ! 이에 대한 유일한 해결책은 유형을 혼합하지 않고 항상 값이 속한 유형 클래스를 정확히 아는 것입니다.


1
덕 타이핑의 경우 +1 : 내 데이터가 어떤 유형인지는 중요하지 않습니다. 내가 원하는 것을 할 수 있는지 여부 만 중요합니다.
systempuntoout

12
이것은 전통적인 접근 방식 이었지만 ABC는 순수한 오리 타이핑에서 벗어나isinstance 실제로 많은 경우에 유용 할 수 있는 세계로 어느 정도 거리를 이동 하기 위해 도입되었습니다 (== "이치에 맞는지 확인"및 공식 적용 가능성 운영). 오랫동안 파이썬 만 사용하는 사람들에게는 어려운 변화이지만, 무시하는 것은 심각한 오류가 될 것이라는 파이썬 철학의 매우 중요한 미묘한 경향입니다.
Alex Martelli

@Alex : True와 나는 typeclasses (대부분 collections.Sequence과 친구들)를 좋아 합니다. 그러나 아파 익, 숫자, 벡터 또는 기타 수학적 개체에 대한 클래스는 없습니다.
Jochen Ritzel 2010 년

1
오리 타이핑에 반대하는 것은 없습니다. 내가 할 일입니다. 그러나 숫자에 대한 추상 기본 클래스가 있습니다 : numbers.Number.
Steven Rumbalski 2010 년

4

개체에 0을 곱합니다. 0을 곱한 숫자는 0입니다. 다른 결과는 객체가 숫자가 아님을 의미합니다 (예외 포함).

def isNumber(x):
    try:
        return bool(0 == x*0)
    except:
        return False

따라서 isNumber를 사용하면 다음과 같은 출력이 제공됩니다.

class A: pass 

def foo(): return 1

for x in [1,1.4, A(), range(10), foo, foo()]:
    answer = isNumber(x)
    print('{answer} == isNumber({x})'.format(**locals()))

산출:

True == isNumber(1)
True == isNumber(1.4)
False == isNumber(<__main__.A instance at 0x7ff52c15d878>)
False == isNumber([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
False == isNumber(<function foo at 0x7ff52c121488>)
True == isNumber(1)

__mul__0을 곱하면 0을 반환 하도록 정의 하는 숫자가 아닌 개체가있을 수 있지만 이는 극단적 인 예외입니다. 이 솔루션은 생성 / 암호화하는 모든 정상정상 코드를 포함해야합니다.

numpy.array 예 :

import numpy as np

def isNumber(x):
    try:
        return bool(x*0 == 0)
    except:
        return False

x = np.array([0,1])

answer = isNumber(x)
print('{answer} == isNumber({x})'.format(**locals()))

산출:

False == isNumber([0 1])

5
True * 0 == 0
endolith

4
함수가 부울이 숫자라고 잘못 표시합니다
endolith

1
@endolith, Booleans는 숫자와 똑같이 작동합니다. True는 항상 == 1이고 False는 항상 == 0입니다. 이것은 정확히 질문자가 "여기서 'is'는 '특정 상황에서 숫자처럼 행동하는 것'으로 정의됩니다."라고 묻습니다.
shrewmouse

1
@endolith, 실제로 Booleans는 숫자입니다. 부울은에서 파생 int되므로 내 함수는 부울이 숫자라고 올바르게 말할 것입니다.
shrewmouse

1
@NicolasAbril, 0 * x == 0을 isNumber 내부의 부울로 변환합니다.
shrewmouse

3

질문을 바꾸려면 무언가가 컬렉션인지 단일 값인지 확인하려고합니다. 어떤 것이 벡터인지 숫자인지 비교하려고하면 사과와 오렌지를 비교하는 것입니다. 저는 문자열 또는 숫자의 벡터를 가질 수 있으며 단일 문자열 또는 단일 숫자를 가질 수 있습니다. 당신은 당신이 실제로 가지고있는 유형이 아니라 얼마나 많은 (1 개 이상) 가지고 있는지에 관심 이 있습니다.

이 문제에 대한 내 해결책은 입력이 단일 값인지 또는 컬렉션인지 확인하여 __len__. 예를 들면 :

def do_mult(foo, a_vector):
    if hasattr(foo, '__len__'):
        return sum([a*b for a,b in zip(foo, a_vector)])
    else:
        return [foo*b for b in a_vector]

또는 덕 타이핑 방식의 경우 foo먼저 반복을 시도 할 수 있습니다 .

def do_mult(foo, a_vector):
    try:
        return sum([a*b for a,b in zip(foo, a_vector)])
    except TypeError:
        return [foo*b for b in a_vector]

궁극적으로, 무언가가 스칼라와 같은지 테스트하는 것보다 벡터와 같은지 테스트하는 것이 더 쉽습니다. 다른 유형 (예 : 문자열, 숫자 등)의 값이 들어 오면 프로그램 논리에 약간의 작업이 필요할 수 있습니다. 처음에 문자열에 숫자 벡터를 곱하려고 시도한 이유는 무엇입니까?


3

기존 방법을 요약 / 평가하려면 :

Candidate    | type                      | delnan | mat | shrewmouse | ant6n
-------------------------------------------------------------------------
0            | <type 'int'>              |      1 |   1 |          1 |     1
0.0          | <type 'float'>            |      1 |   1 |          1 |     1
0j           | <type 'complex'>          |      1 |   1 |          1 |     0
Decimal('0') | <class 'decimal.Decimal'> |      1 |   0 |          1 |     1
True         | <type 'bool'>             |      1 |   1 |          1 |     1
False        | <type 'bool'>             |      1 |   1 |          1 |     1
''           | <type 'str'>              |      0 |   0 |          0 |     0
None         | <type 'NoneType'>         |      0 |   0 |          0 |     0
'0'          | <type 'str'>              |      0 |   0 |          0 |     1
'1'          | <type 'str'>              |      0 |   0 |          0 |     1
[]           | <type 'list'>             |      0 |   0 |          0 |     0
[1]          | <type 'list'>             |      0 |   0 |          0 |     0
[1, 2]       | <type 'list'>             |      0 |   0 |          0 |     0
(1,)         | <type 'tuple'>            |      0 |   0 |          0 |     0
(1, 2)       | <type 'tuple'>            |      0 |   0 |          0 |     0

( 이 질문으로 여기에 왔습니다 )

암호

#!/usr/bin/env python

"""Check if a variable is a number."""

import decimal


def delnan_is_number(candidate):
    import numbers
    return isinstance(candidate, numbers.Number)


def mat_is_number(candidate):
    return isinstance(candidate, (int, long, float, complex))


def shrewmouse_is_number(candidate):
    try:
        return 0 == candidate * 0
    except:
        return False


def ant6n_is_number(candidate):
    try:
        float(candidate)
        return True
    except:
        return False

# Test
candidates = (0, 0.0, 0j, decimal.Decimal(0),
              True, False, '', None, '0', '1', [], [1], [1, 2], (1, ), (1, 2))

methods = [delnan_is_number, mat_is_number, shrewmouse_is_number, ant6n_is_number]

print("Candidate    | type                      | delnan | mat | shrewmouse | ant6n")
print("-------------------------------------------------------------------------")
for candidate in candidates:
    results = [m(candidate) for m in methods]
    print("{:<12} | {:<25} | {:>6} | {:>3} | {:>10} | {:>5}"
          .format(repr(candidate), type(candidate), *results))

나 자신을위한 TODO : float('nan'), 'nan', '123.45', '42', '42a', '0x8', '0xa', 추가math.isnan
Martin Thoma

2

아마도 다른 방법으로하는 것이 더 낫습니다. 벡터인지 확인합니다. 그렇다면 내적을 수행하고 다른 모든 경우에는 스칼라 곱셈을 시도합니다.

벡터는 벡터 클래스 유형 (또는 상 속됨)이어야하므로 벡터를 확인하는 것은 쉽습니다. 먼저 내적을 시도하고 실패하면 (= 실제로 벡터가 아님) 스칼라 곱셈으로 돌아갈 수 있습니다.


1

추가하기 위해. 값이 숫자 (int, float 등)인지 확인하기 위해 다음과 같이 isinstance와 isdigit의 조합을 사용할 수 있습니다.

isinstance (num1, int) 또는 isinstance (num1, float) 또는 num1.isdigit () :


0

가상 벡터 클래스의 경우 :

v벡터 라고 가정 하고 우리는 그것을 x. 그 말이 경우의 각 구성 요소 번식하기 v로를x , 우리는 아마, 그래서 첫 번째 시도 것을 의미했다. 그렇지 않다면 점을 찍을 수 있습니까? 그렇지 않으면 유형 오류입니다.

편집 - 코드 아래 때문에,하지 작업을 수행 2*[0]==[0,0]하는 대신 인상의 TypeError. 댓글로 남겨 두었습니다.

def __mul__( self, x ):
    try:
        return [ comp * x for comp in self ]
    except TypeError:
        return [ x * y for x, y in itertools.zip_longest( self, x, fillvalue = 0 )

만약 x벡터가 다음 [comp * x for comp in self]의 외적 수득한다 xv. 이것은 스칼라가 아니라 랭크 2 텐서입니다.
aaronasterling 2010 년

"not a scalar"를 "not a vector"로 변경합니다. 최소한 원래 벡터 공간에는 없습니다.
aaronasterling 2010 년

허, 사실 우리 둘 다 틀렸어. 당신은 그 가정하고 comp*x확장 할 수 x에 의해 comp나는 그것이 형식 오류를 올릴 것이라고 가정했다. 불행히도 실제로 시간 x과 연결 됩니다 comp. 죄송합니다.
Katriel 2010 년

meh. 만약 x벡터 인 다음이 있어야 __rmul__방법 ( __rmul__ = __mul__그 정도) comp * x으로 확장한다 x같은 방식으로 x * comp명백하게하기위한 것이다.
aaronasterling 2010 년

0

일종의 벡터 클래스를 구현할 때 비슷한 문제가 발생했습니다. 숫자를 확인하는 한 가지 방법은 1로 변환하는 것입니다.

float(x)

이것은 x를 숫자로 변환 할 수없는 경우를 거부해야합니다. 그러나 유효 할 수있는 다른 종류의 숫자 유사 구조 (예 : 복소수)를 거부 할 수도 있습니다.


0

인수 유형에 따라 다른 메서드를 호출하려면 multipledispatch.

예를 들어 벡터 클래스를 작성한다고 가정 해보십시오. 다른 벡터가 주어지면 내적을 찾고 싶습니다. 스칼라가 주어지면 전체 벡터를 스케일링하려고합니다.

from multipledispatch import dispatch

class Vector(list):

    @dispatch(object)
    def __mul__(self, scalar):
        return Vector( x*scalar for x in self)

    @dispatch(list)
    def __mul__(self, other):
        return sum(x*y for x,y in zip(self, other))


>>> Vector([1,2,3]) * Vector([2,4,5])   # Vector time Vector is dot product
25
>>> Vector([1,2,3]) * 2                 # Vector times scalar is scaling
[2, 4, 6]

불행히도 (내가 아는 한) @dispatch(Vector)유형을 아직 정의하고 Vector있으므로 유형 이름이 아직 정의되지 않았으므로 작성할 수 없습니다 . 대신, lista Vector와 a 의 내적을 찾을 수 있는 기본 유형 을 사용 하고 있습니다 list.


0

짧고 간단한 방법 :

obj = 12345
print(isinstance(obj,int))

출력 :

True

객체가 문자열이면 'False'가 반환됩니다.

obj = 'some string'
print(isinstance(obj,int))

출력 :

False

0

데이터 항목 rec_day이 있습니다 float. 파일에 쓸 때 . 그러나 프로그램 처리 중에 float, int또는 str유형일 수 있습니다 ( str새 레코드를 초기화 할 때 사용되며 더미 플래그 값을 포함 함).

그런 다음 이것으로 번호가 있는지 확인할 수 있습니다.

                type(rec_day) != str 

나는 이런 식으로 파이썬 프로그램을 구조화하고 이것을 숫자 검사로 사용하여 '유지 보수 패치'에 넣었습니다. Pythonic 방식입니까? COBOL로 프로그래밍 한 이후로는 아닐 가능성이 큽니다.


-1

isdigit () 함수를 사용할 수 있습니다.

>>> x = "01234"
>>> a.isdigit()
True
>>> y = "1234abcd"
>>> y.isdigit()
False

"01234"는 숫자가 아니라 문자열입니다.
Alexey
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.