Bir nesnenin sayı olup olmadığını kontrol etmenin en pitonik yolu nedir?


114

Rasgele bir python nesnesi verildiğinde, bunun bir sayı olup olmadığını belirlemenin en iyi yolu nedir? İşte isolarak tanımlanır acts like a number in certain circumstances.

Örneğin, bir vektör sınıfı yazdığınızı varsayalım. Başka bir vektör verilirse, iç çarpımı bulmak istersiniz. Skaler verilirse, tüm vektörü ölçeklendirmek istersiniz.

Bir şeyse kontrol etme int, float, long, boolcan sıkıcı ve sayılar gibi hareket olabilir kullanıcı tanımlı nesneler kapsamaz. Ancak, __mul__örneğin , kontrol etmek yeterince iyi değil çünkü az önce tanımladığım vektör sınıfı tanımlayacaktı __mul__, ama istediğim türden bir sayı olmazdı.

Yanıtlar:


135

Kullanım Numberile ilgili numberstest modülü isinstance(n, Number)(2.6 mevcuttur).

>>> 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

Bu, elbette, ördek yazmanın tersidir. Daha bir nesne nasıl endişe varsa davranır ne ziyade olan bir numara varsa ve başka türlü söylemek istisnalar kullanmak sanki senin işlemleri gerçekleştirmek.


3
Eğer X dayalı farklı şeyler yapmak istiyorum Bu durumda X tarafından bir vektör çarpımı yaparken, tercih edilir ziyade ördek şey daha akıllı şey yapmak olduğunu . (Çoğalan bir şey gibi davranabilir, ancak sonuç saçma olabilir.)
Evgeni Sergeev

3
bu cevap, Doğru bir sayıdır diyebiliriz .. ki bu muhtemelen her zaman istediğiniz şey değildir. Boole'ları dışlamak için (doğrulamayı düşünün), şunu söyleyebilirimisinstance(value, Number) and type(value) != bool
Yo Ludke

32

Bir nesne olup olmadığını kontrol etmek istiyorsun

belirli durumlarda bir sayı gibi davranır

Python 2.5 veya daha eski bir sürümünü kullanıyorsanız, tek gerçek yol bu "belirli durumlardan" bazılarını kontrol etmek ve görmektir.

2.6 veya daha iyisi, sayılarla kullanabilirsiniz isinstance. Sayı - tam olarak bu amaç için var olan soyut bir temel sınıf (ABC) (içinde çok daha fazla ABC vardır)collections yine 2.6'dan başlayarak çeşitli koleksiyon / kap biçimleri modülde ; ve, ayrıca yalnızca bu sürümlerde, gerekirse kendi soyut temel sınıflarınızı kolayca ekleyebilirsiniz).

Bach 2.5 ve öncesi için "eklenebilir 0ve yinelenemez" bazı durumlarda iyi bir tanım olabilir. Ama, gerçekten sen dikkate almak istediğini "Bir numara" kesinlikle mümkün olması gerektiğini soruyoruz ne olduğunu, kendinize sormanız gerekir yapmak ve kesinlikle ne olmalıdır yapamaz ve check - yapmak.

Bu 2.6 veya sonraki sürümlerde de gerekli olabilir, belki de ilgilendiğiniz ancak henüz kaydedilmemiş türleri eklemek için kendi kayıtlarınızı yapmak amacıyla numbers.Numbers- sayı olduklarını iddia eden bazı türleri hariç tutmak istiyorsanız üstesinden gelemiyorum, bu daha da dikkat gerektiriyor, çünkü ABC'lerin hiçbir unregisteryöntemi yok [[örneğin kendi ABC'nizi oluşturabilir WeirdNumve sizin için bu tür tuhaf türleri orada kaydedebilirsiniz, sonra isinstancedevam etmeden önce bunun kurtarılmasını kontrol edin başarılı bir şekilde devam etmek için isinstancenormalin kontrol edilmesi numbers.Number.

BTW, bir şeyi yapıp xyapamayacağınızı kontrol etmeniz gerektiğinde , genellikle aşağıdaki gibi bir şey denemeniz gerekir:

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

Bunun varlığı __add__size yararlı hiçbir şey söylemez, çünkü örneğin tüm diziler diğer dizilerle birleştirme amacıyla ona sahiptir. Bu kontrol, örneğin "bir sayı, böyle şeylerin bir dizisi yerleşik işlev için geçerli tek bir argüman olacak şekilde bir şeydir" tanımına eşdeğerdir sum. Tamamen tuhaf türler (örneğin, 0'a toplandığında "yanlış" istisnayı ortaya çıkaranlar, örneğin, a ZeroDivisionErrorveya ValueError& c gibi) istisnayı yayacaktır, ancak sorun değil, kullanıcıya en kısa sürede bu tür çılgın türlerin iyi olarak kabul edilmediğini bildirin şirket;-); ancak, bir skalere toplanabilen bir "vektör" (Python'un standart kitaplığında bir tane yoktur, ancak elbette üçüncü taraf uzantıları olarak popülerdirler) burada da yanlış sonuç verir, yani (örn."yinelenebilir olmasına izin verilmez" olanı (örneğin, iter(x)yükseltmeleri kontrol edin TypeErrorveya özel yöntemin varlığı __iter__- 2.5 veya daha önceki sürümdeyseniz ve bu nedenle kendi çeklerinize ihtiyacınız varsa).

Bu tür karmaşıklıklara kısa bir bakış, sizi mümkün olduğunda soyut temel sınıflara güvenmeye motive etmek için yeterli olabilir ... ;-).


Ancak sayılar modülünde Numara için bir ABC var. Dokümanların iddia ettiği şey budur: "Sayılar modülü (PEP 3141), aşamalı olarak daha fazla işlemi tanımlayan sayısal soyut temel sınıfların bir hiyerarşisini tanımlar."
Steven Rumbalski

17

Bu, istisnaların gerçekten parladığı güzel bir örnek. Sadece sayısal türlerle ne yaparsanız yapın ve TypeErrordiğer her şeyden yakalayın.

Ancak açıkçası, bu yalnızca bir işlemin çalışıp çalışmadığını kontrol eder , mantıklı olup olmadığını değil ! Bunun için tek gerçek çözüm türleri asla karıştırmamak ve her zaman değerlerinin hangi tip sınıfa ait olduğunu bilmektir.


1
Ördek Yazma için +1: Verilerimin ne tür olduğu önemli değil, onunla istediğimi yapıp yapamayacağım.
systempuntoout

12
Bu geleneksel yaklaşımdı, ancak ABC'ler büyük ölçüde saf ördek yazımından uzaklaşmak ve birçok durumda isinstancegerçekten yararlı olabilecek bir dünyaya biraz mesafe kaydırmak için tanıtıldı (== "mantıklı olduğunu kontrol et" ve resmi uygulanabilirlik operasyonlar). Uzun zamandır sadece Python kullanan insanlar için zor bir geçiş, ancak Python'un felsefesinde görmezden gelinmesi gereken ciddi bir hata olacağı gibi ince bir eğilim.
Alex Martelli

@Alex: True ve ben tip sınıflarını seviyorum (çoğunlukla collections.Sequenceve arkadaşlarım). Ama afaik, sayılar, vektörler veya diğer matematiksel nesneler için böyle sınıflar yok.
Jochen Ritzel

1
Ördek yazmaya karşı bir şey yok. Ben öyle yapardım. Ancak sayılar için soyut bir temel sınıf vardır: sayılar.
Steven Rumbalski

4

Nesneyi sıfırla çarpın. Sıfırın herhangi bir sayısı sıfırdır. Diğer herhangi bir sonuç, nesnenin bir sayı olmadığı anlamına gelir (istisnalar dahil)

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

Bu nedenle isNumber kullanmak aşağıdaki çıktıyı verecektir:

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()))

Çıktı:

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)

Muhtemelen dünyada __mul__sıfırla çarpıldığında sıfır döndürmeyi tanımlayan sayı olmayan bazı nesneler vardır, ancak bu aşırı bir istisnadır. Bu çözüm, oluşturduğunuz / kodlayacağınız tüm normal ve mantıklı kodları kapsamalıdır .

numpy.array örneği:

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()))

çıktı:

False == isNumber([0 1])

5
True * 0 == 0
Endolit

4
Fonksiyonunuz yanlışlıkla boolelerin sayılar olduğunu söyleyecektir
endolith

1
@endolith, Booleanlar tam olarak sayılar gibi davranır. Doğru her zaman == 1 ve Yanlış her zaman == 0. Bu, soru soran kişinin tam olarak istediği şeydi, "Burada ',' belirli durumlarda bir sayı gibi davranır 'olarak tanımlanır."
shrewmouse

1
@endolith, Aslında Boole'lar sayılardır. Boolean türemiştir, intbu yüzden benim fonksiyonum boolelerin sayılar olduğunu doğru bir şekilde söyleyecektir.
shrewmouse

1
@NicolasAbril, 0 * x == 0 değerini isNumber içinde bool'a dönüştür.
shrewmouse

3

Sorunuzu yeniden ifade etmek için, bir şeyin bir koleksiyon mu yoksa tek bir değer mi olduğunu belirlemeye çalışıyorsunuz. Bir şeyin vektör mü yoksa sayı mı olduğunu karşılaştırmaya çalışmak, elmaları portakallarla karşılaştırmaktır - Bir dizi veya sayı vektörüne sahip olabilirim ve tek bir dizem veya tek bir sayı olabilir. Kaç tane (1 veya daha fazla) sahip olduğunuzla ilgileniyorsunuz, gerçekte hangi türe sahip olduğunuzla değil.

bu problem için benim çözümüm, girdinin varlığını kontrol ederek girdinin tek bir değer mi yoksa koleksiyon mu olduğunu kontrol etmektir __len__. Örneğin:

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]

Veya ördek yazma yaklaşımı için fooönce yinelemeyi deneyebilirsiniz :

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]

Nihayetinde, bir şeyin vektör benzeri olup olmadığını test etmek, bir şeyin skaler benzeri olup olmadığını test etmekten daha kolaydır. Eğer farklı türde değerlere sahipseniz (örn. Dizi, sayısal, vb.), Programınızın mantığı biraz çalışmaya ihtiyaç duyabilir - bir dizgeyi ilk etapta sayısal bir vektörle çarpmaya nasıl başladınız?


3

Mevcut yöntemleri özetlemek / değerlendirmek için:

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

(Buraya bu soruyla geldim )

kod

#!/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))

Kendim için YAPILACAKLAR:, float('nan'), 'nan', '123.45', '42', '42a', '0x8', '0xa'eklemath.isnan
Martin Thoma

2

Muhtemelen tersini yapmak daha iyidir: Bunun bir vektör olup olmadığını kontrol edersiniz. Eğer öyleyse, bir iç çarpım yaparsınız ve diğer tüm durumlarda skaler çarpmaya çalışırsınız.

Vektör sınıfınızın türünde olması gerektiği için (veya ondan miras alınması) vektörü kontrol etmek kolaydır. Ayrıca önce bir iç çarpım yapmayı deneyebilirsiniz ve bu başarısız olursa (= bu gerçekten bir vektör değilse), skaler çarpıma geri dönebilirsiniz.


1

Sadece eklemek için. Bir değerin bir sayı (int, float, vb.) Olup olmadığını bulmak için isinstance ve isdigit'in bir kombinasyonunu kullanabiliriz.

isinstance (num1, int) veya isinstance (num1, float) veya num1.isdigit () ise:


0

Varsayımsal vektör sınıfı için:

Diyelim ki vbir vektör ve onu çarpıyoruz x. O her bir bileşenini çoğalmaya mantıklı olursa vtarafından x, muhtemelen, böylece ilk denemede anlamına geliyordu. Değilse, belki noktalayabiliriz? Aksi takdirde bu bir tür hatasıdır.

DÜZENLE - aşağıdaki kod çalışmaz, çünkü 2*[0]==[0,0]a yükseltmek yerine TypeError. Üzerine yorum yapıldığı için bırakıyorum.

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 )

Eğer xbir vektör daha sonra [comp * x for comp in self]dış ürün elde edecek xbir v. Bu bir 2. seviye tensördür, skaler değil.
aaronasterling

"skaler değil" i "vektör değil" olarak değiştirin. en azından orijinal vektör uzayında değil.
aaronasterling

Heh, aslında ikimiz de yanılıyoruz. Sen olduğunu varsayıyoruz comp*xölçekleneceği xtarafından compben bir TypeError gündeme getireceğini varsayarak edildi. Ne yazık ki, aslında xkendi compzamanlarıyla birleşecek . Hata.
Katriel

meh. Eğer xbir vektör daha sonra bir olması gereken __rmul__bir yöntem ( __rmul__ = __mul__ki) comp * xölçekli olmalıdır xaynı şekilde x * compgörünüşte amaçlanmıştır.
aaronasterling

0

Bir tür vektör sınıfı uygularken benzer bir sorun yaşadım. Bir sayıyı kontrol etmenin bir yolu, yalnızca bir numaraya dönüştürmektir, yani

float(x)

Bu, x'in bir sayıya dönüştürülemediği durumları reddetmelidir; fakat aynı zamanda geçerli olabilecek diğer sayı benzeri yapıları, örneğin karmaşık sayıları reddedebilir.


0

Bağımsız değişken türüne (türlerine) bağlı olarak farklı yöntemler çağırmak istiyorsanız, bakın multipledispatch.

Örneğin, bir vektör sınıfı yazdığınızı varsayalım. Başka bir vektör verilirse, iç çarpımı bulmak istersiniz. Skaler verilirse, tüm vektörü ölçeklendirmek istersiniz.

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]

Ne yazık ki, (bildiğim kadarıyla) @dispatch(Vector)türü hala tanımladığımız için yazamıyoruz Vector, bu nedenle bu tür adı henüz tanımlanmadı. Bunun yerine, lista Vectorve a'nın iç çarpımını bile bulmanızı sağlayan temel türü kullanıyorum list.


0

Kısa ve basit yol:

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

Çıktı :

True

Nesne bir dizeyse, 'Yanlış' döndürülür:

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

Çıktı :

False

0

Bir veri öğeniz var, rec_daybir dosyaya yazıldığında bir float. Ancak program işleme sırasında float, intya da strtür olabilir ( stryeni bir kayıt başlatılırken kullanılır ve sahte bir bayrak değeri içerir).

Daha sonra bununla bir numaranız olup olmadığını kontrol edebilirsiniz.

                type(rec_day) != str 

Bu şekilde bir python programı yapılandırdım ve bunu sayısal bir kontrol olarak kullanarak 'bakım yaması' ekledim. Pythonic yolu mu? COBOL'da programlama yaptığımdan beri büyük olasılıkla değil.


-1

İsdigit () işlevini kullanabilirsiniz.

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

"01234" bir sayı değil, bir karakter dizisidir.
Alexey
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.