Python'da türü kontrol etmenin standart yolu nedir?


1277

Belirli bir nesnenin belirli bir türde olup olmadığını kontrol etmenin en iyi yolu nedir? Nesnenin belirli bir türden miras alıp almadığını kontrol etmeye ne dersiniz?

Diyelim ki bir nesnem var o. Bunun olup olmadığını nasıl kontrol edebilirim str?


7
Python'daki kanonik yaklaşım, türü hiç kontrol etmemek (hata ayıklamadığınız sürece). Genellikle onu bir dize olarak kullanmaya çalışırsınız (örneğin, diğer dizelerle birleştirin, konsola yazdırın vb.); başarısız olabileceğini düşünüyorsanız, try / hariç veya hasattr kullanın. Bununla birlikte, kabul edilen cevap, Python dünyasında genel olarak "yapmamanız" gereken şeyi yapmanın kanonik yoludur. Daha fazla bilgi için google "Python ördek yazıyor " veya bunları okuyun: voidspace.org.uk/python/articles/duck_typing.shtml stackoverflow.com/questions/610883/…
Jon Coombs

9
Bay Coombs'un JSON olmayan serileştirilebilir sınıflar gibi örneklere baktığını düşünüyorum. Eğer büyük bir veri parçasını bir işlev (kodunu etkileyemeyen) içine koyarsanız, bu verilerin belirli parçalarını örneğin geçmeden önce bir <str> biçimine dönüştürmek isteyebilirsiniz. En azından ben bu sayfaya bu şekilde geldim ...
John Carrell

2
Bunu sormanın en yaygın nedeni, dizeler ve dizelerin yinelemeleri arasında ayrım yapmak istemesidir. Dizeleri, çünkü bu zor bir sorudur olan dizeleri Iterables - tek karakter dizesi bile başlı başına bir dizi (- biri muhtemelen ona bağlı olmamalıdır son baktığımda) 'dir. Ama kimse ip gibi bir şey için kullanır mıydı? Evet . Bu yüzden "Dizeleri ve diğer dizeleri tekrar edebilmek için ne yapmalıyım?" düzgün bir şekilde: "Ne yapmaya çalıştığınıza bağlıdır". :-D
clacke

2
Python tipi ek açıklamalar artık bir şey. Mypy'ye
Sheena

Yanıtlar:


1522

Alt sınıfının obir örneği mi, stryoksa herhangi bir alt sınıfı mı olduğunu kontrol etmek striçin isinstance işlevini kullanın (bu "standart" yol olacaktır):

if isinstance(o, str):

Türünün otam olarak olup olmadığını kontrol etmek için str(alt sınıfları hariç tutun):

if type(o) is str:

Aşağıdakiler de çalışır ve bazı durumlarda yararlı olabilir:

if issubclass(type(o), str):

İlgili bilgiler için Python Kütüphanesi Referansındaki Yerleşik İşlevler konusuna bakın .

Bir not daha: bu durumda, Python 2 kullanıyorsanız, aslında aşağıdakileri kullanmak isteyebilirsiniz:

if isinstance(o, basestring):

Bu da Unicode dizeleri yakalayacak (çünkü unicodebir alt sınıfı değildir str; hem strve unicodealt sınıflarıdır basestring). Dizelerin ( ) ve ikili verilerin ( ) sıkı bir şekilde ayrıldığıbasestring Python 3'te artık mevcut olmadığını unutmayın .strbytes

Alternatif olarak, isinstancebir grup dersi kabul eder. Bu dönecektir Trueeğer oherhangi herhangi alt sınıfının bir örneğidir (str, unicode):

if isinstance(o, (str, unicode)):

31
str .__ subclasses __ () yalnızca str'nin doğrudan alt sınıflarını döndürür ve issubclass () veya isinstance () ile aynı şeyi yapmaz. (Bunu yapmak için, tekrar tekrar .__ subclasses __ () öğesini çağırmanız gerekir.
Thomas Wouters

15
Bu iyi bir cevap, ama bence bunu genellikle Python'da yapmamanız gerektiğine dair bir uyarı ile başlamalıdır. Olduğu gibi, bunun "Python'da yapılacak kanonik bir şey" olduğu varsayımını doğrulamaktadır.
Jon Coombs

4
Bunlar python2 cevaplarıdır. Örneğin, python3'te bir basestring yoktur.
dfrankow

4
Örnek ve "tam olarak" arasındaki fark nedir? Eğer type(a) is Objectöyleyse doğru değil isinstance(a, Object). Ancak, type(a) is SubClassOfObjectöyleyse type(a) is Object == False, ama isinstance(a, Object) == True. Sağ?
mavavilj

1
@mavavilj - a is ba ve b'nin aynı şey olduğu, yani bellekteki aynı varlığa gönderme yaptığı anlamına gelir. Yani ave balt sınıflar değil, aynı sınıf olmak zorundaydı isinstance(). Örneğin, bkz. Stackoverflow.com/a/133024/1072212
Terry Brown

196

En nesnenin türünü kontrol etmek Pythonictir yolu bunu kontrol etmek değil ... olduğunu.

Python Ördek Yazmayı teşvik ettiğinden , yalnızca try...exceptnesnenin yöntemlerini kullanmak istediğiniz şekilde kullanmalısınız. Bu nedenle, işleviniz yazılabilir bir dosya nesnesi arıyorsa , bunun bir alt sınıfı olup olmadığını kontrol etmeyinfile , sadece .write()yöntemini kullanmayı deneyin !

Tabii ki, bazen bu güzel soyutlamalar yıkılır ve isinstance(obj, cls)ihtiyacınız olan şeydir. Ancak idareli kullanın.


75
IMHO, en Pythonic yolu verilen argümanla başa çıkmaktır. Kodumda genellikle bir nesneyi mi yoksa nesne dizisini mi aldığımı bilemiyorum ve tek bir nesneyi tek elemanlı bir listeye dönüştürmek için dahili olarak denetlemeyi kullanıyorum.
sastanin

14
Daha sonra sadece yazma yöntemini kullanmaya çalışırken, bir istisna yaratmadan bunu yapmak istediğiniz zamanlar vardır. Bu durumda yapabilirsin ... if hasattr(ob, "write") and callable(ob.write): Ya da bazı dict erişimini func = getattr(ob, "write", None) if callable(func): ...
koru

142
Ördek yazmak bir kütüphane kullanmakla ilgilidir . Yazım denetimi bir kütüphane yazmakla ilgilidir . Aynı sorun etki alanı değil.
RickyA

16
@RickyA, katılmıyorum. Ördek yazımı, iyi bilinen anlambilim ile arayüzler kullanarak nesnelerle etkileşim kurmakla ilgilidir. Bu, kütüphane koduna veya böyle bir kütüphaneyi kullanan koda uygulanabilir.
Dan Lenski

6
@ nyuszika7h, In Python3 hasattryalnızca bir AttributeError'u bastırır
ideasman42 26:14

57

isinstance(o, str)dönecektir Trueeğer obir olduğunu strya da devralır bir tiptedir str.

type(o) is strTrueyalnızca ostr ise geri döner . Bu dönecektir Falseeğer oo devralır bir tiptedir str.


6
Tabii ki, nesne 'str' örneği değil, bunun yerine dize benzeri bir şeyin örneği ise başarısız olur. Unicode, mmap, UserString veya kullanıcı tanımlı herhangi bir tür gibi. Python'daki genel yaklaşım daktilo kontrolleri yapmak değildir.
Thomas Wouters

6
Özür dilemek zorunda değilsiniz, kendi sorunuza cevap vermek sorun değil. SO cevaplar içindir, karma değil.
Eli Bendersky

2
Bu çok yardımcı. Arasındaki fark nedeniyle isinstanceve type(var) == type('')açık değildir.
sastanin

30

Soru sorulup cevaplandıktan sonra, Python'a tip ipuçları eklendi . Python'daki tip ipuçları tiplerin kontrol edilmesine izin verir, ancak statik olarak yazılan dillerden çok farklı bir şekilde. Python dilinde yaz ipuçları fonksiyonları ile ilişkili çalışma zamanı erişilebilir veri olarak fonksiyonları ile argümanlar beklenen türlerini ilişkilendirmek ve bu izin verir tipleri kontrol edilecek için. Tip ipucu sözdizimi örneği:

def foo(i: int):
    return i

foo(5)
foo('oops')

Bu durumda foo('oops'), argümanın açıklamalı türü olduğundan bir hatanın tetiklenmesini istiyoruz int. Eklenen tür ipucu , komut dosyası normal olarak çalıştırıldığında bir hata oluşmasına neden olmaz . Ancak, diğer programların sorgulayabileceği ve tür hatalarını denetlemek için kullanabileceği beklenen türleri açıklayan işleve öznitelikler ekler.

Tür hatasını bulmak için kullanılabilecek bu diğer programlardan biri mypy:

mypy script.py
script.py:12: error: Argument 1 to "foo" has incompatible type "str"; expected "int"

( mypyPaket yöneticinizden yüklemeniz gerekebilir . CPython ile geldiğini düşünmüyorum, ancak bir miktar "resmiyet" var gibi görünüyor.)

Bu yolla yazım denetimi, statik olarak yazılan derlenmiş dillerde yazım denetiminden farklıdır. Python'da türler dinamik olduğu için, tür denetlemesi çalışma zamanında yapılmalıdır, bu da her fırsatta ısrar edersek - doğru programlarda bile - bir maliyet oluşturur. Açık tür denetimleri de gerekenden daha kısıtlayıcı olabilir ve gereksiz hatalara neden olabilir (örneğin, argümanın gerçekten tam olarak olması gerekiyor listmu veya yeterli bir şey var mı?).

Açık tip denetiminin tersi, hataları daha erken yakalayabilmesi ve ördek yazımından daha net hata mesajları verebilmesidir. Ördek tipinin kesin gereklilikleri sadece harici belgelerle (umarım eksiksiz ve doğrudur) ifade edilebilir ve uyumsuz tiplerden kaynaklanan hatalar kaynaklandıkları yerden uzakta meydana gelebilir.

Python'un tip ipuçları, tiplerin belirtilebileceği ve kontrol edilebileceği bir uzlaşma sunmak içindir, ancak normal kod yürütme sırasında ek bir maliyet yoktur.

typingTipi ipuçları kullanılabilecek paket teklifler tip değişkenler belirli türleri gerektirmeden gerekli davranışları ifade etmek. Örneğin, bu davranışlara sahip herhangi bir türe duyulan ihtiyacı belirtmek için Iterableve gibi Callableipuçları içeren değişkenler içerir .

Tür ipuçları türleri kontrol etmenin en Pythonic yolu olsa da, türleri hiç kontrol etmemek ve ördek yazımına güvenmek genellikle daha da Pythonic'tir. Tip ipuçları nispeten yenidir ve jüri en Pythonic çözümü olduklarında hala açıktır. Nispeten tartışmalı fakat çok genel bir karşılaştırma: Tür ipuçları, uygulanabilecek, kodun daha erken ve daha kolay anlaşılabilecek hatalar üretmesine izin veren, ördek yazmanın yapamayacağı hataları yakalayabilen ve statik olarak (olağandışı bir şekilde) kontrol edilebilen bir belge biçimi sağlar ama yine de çalışma zamanının dışında). Öte yandan, ördek yazmak uzun zamandır Pythonic yoludur, statik yazmanın bilişsel yükünü dayatmaz, daha az ayrıntılıdır ve tüm uygulanabilir türleri ve sonra bazılarını kabul eder.


2
-1: mypy özellikle "statik tip denetleyicisi" olarak adlandırır, bu yüzden "tip denetimi çalışma zamanında yapılmalıdır" dan nereden aldığınızdan emin değilim.
Kevin

@Kevin Geçmişe bakılırsa, bu gereksiz bir kazıdır, ancak daha fazla girmek için Python'un tip ipuçları çalışma zamanı verilerine dönüştürülür ve bu verilere erişmek için mypykullanılan bir Python modülüdür importlib. Bunun "statik tip kontrolü" olup olmadığı felsefi bir sorudur, ancak normal dil tercümanı ve ithalat makineleri dahil edildiğinden en çok beklenenden farklıdır.
Praxeolitic

4
Bu da doğru değil. Bu kullanır ki kendisi typed_ast ast sadece bir klon ekstra özelliklere sahip. ast modülleri içe aktarmaz; onları soyut bir sözdizimi ağacına ayrıştırır.
Kevin

18

Ördek yazmanın neden tehlikeli olduğunu bilmeden kötü olduğu bir örnek. Örneğin: İşte Python kodu (muhtemelen uygun girintiyi atlayan), gerçekten bir ördeğe ihtiyaç duyduğunuzda bomba alamadığınızdan emin olmak için isinstance ve issubclassof işlevlerine dikkat ederek bu durumun önlenebileceğini unutmayın.

class Bomb:
    def __init__(self):
        ""

    def talk(self):
        self.explode()

    def explode(self):
        print "BOOM!, The bomb explodes."

class Duck:
    def __init__(self):
        ""
    def talk(self):
        print "I am a duck, I will not blow up if you ask me to talk."    

class Kid:
    kids_duck = None

    def __init__(self):
        print "Kid comes around a corner and asks you for money so he could buy a duck."

    def takeDuck(self, duck):
        self.kids_duck = duck
        print "The kid accepts the duck, and happily skips along"

    def doYourThing(self):
        print "The kid tries to get the duck to talk"
        self.kids_duck.talk()

myKid = Kid()
myBomb = Bomb()
myKid.takeDuck(myBomb)
myKid.doYourThing()

36
Tür denetlemesinde bile, bir class EvilDuck(Duck)konuşma oluşturabilir ve geçersiz kılmaya başlayabilirsiniz (). Ya da daha büyük olasılıkla, class ChineseCancerDuck(Duck)yıllar sonra ortaya çıkmayan kötü bir yan etki ile. Sadece çocuğunuzu denetlemek daha iyi olurdu (ve oyuncaklarını iyice test edin :)
Brett Thomas

36
Bombalar konuşmuyor. Saçma yöntemler eklemeyin, bu olmayacak.
rightfold

7
@Dmitry, bu Duck Typing'in yaygın eleştirisidir: en.wikipedia.org/wiki/Duck_typing#Criticism ... temel olarak anlambilimin dil tarafından uygulanmadığı herhangi bir arabirimin kötü olduğunu söylüyorsunuz. Bunun Java'nın yaklaşımı olduğuna inanıyorum. Python'un ördek yazmasının tüm amacı, yalnızca belirli arayüzlerin ne anlama geldiği konusunda yaygın olarak yükseltilmiş bir kural olduğunda işe yaramasıdır. Örneğin, __file__başka bir şey ifade etmek için özniteliği (genellikle dosya benzeri nesneleri tanımlamak için kullanılır) geçersiz kılarak birçok Python kodunu kullanabilirsiniz.
Dan Lenski

2
Bütün bunlar eski şakaya geliyor "Doktor, bunu yaptığımda acıyor." ... "Öyleyse yapma." "Eğer derlerse, çalışırsa" alışkın olan biri için tatmin edici değildir, ama bu yüzden test takıntısı dinamik dil dünyasından büyüdü.
clacke

1
@clacke temel olarak, çalışma zamanında türleri zorlamak için çok pahalıdır, çünkü HER ŞEY bir nesne olmalıdır (dizeden herhangi bir türle eşleştirmek için) ve ördek yavrusu yapamayacak kadar uygun çünkü ördek yavrusu, gerçekten güçlü prototipleme tekniklerine izin veriyor rijit arayüzlerle normalde çok zordur. Ayrıca, herhangi bir statik dil, dinamik kütüphaneler, değerlendirme ve dizgi oluşturma veya arayüzler aracılığıyla ördek yazmayı yaratması gereken bir nokta ile karşı karşıyadır ve bunlar doğal olarak kötülük yapmaz, sadece çok güçlü değildir.
Dmitry

12
isinstance(o, str)

Dokümanlara bağlantı


1
Bu bağlantı soruyu cevaplayabilse de, cevabın temel kısımlarını buraya eklemek ve bağlantıyı referans olarak sağlamak daha iyidir. Bağlantı verilen sayfa değişirse, yalnızca bağlantı yanıtları geçersiz olabilir.
EKons

7

Bence Python gibi dinamik bir dil kullanmanın en güzel yanı, gerçekten böyle bir şeyi kontrol etmek zorunda olmamanızdır.

Sadece nesne üzerinde gerekli yöntemleri çağırmak ve bir yakalamak AttributeError. Daha sonra bu, test etmek için bir nesneyle alay etmek gibi farklı görevleri yerine getirmek için yöntemlerinizi diğer (görünüşte ilgisiz) nesnelerle çağırmanıza izin verecektir.

Bunu web gibi veri alırken urllib2.urlopen()hangi nesneyi dosya gibi döndürür çok kullandım . Bu, read()gerçek bir dosyayla aynı yöntemi uyguladığından, bir dosyadan okunan hemen hemen her yönteme geçirilebilir .

Ama eminim kullanmak için bir zaman ve yer var isinstance(), aksi takdirde muhtemelen orada olmaz :)


Ne zaman kullanmanız gerektiğine iyi bir örnek, dinamik bir json nesnesini ayrıştırıyorsanız. Bir alanın bir dize mi yoksa sözlük mi olduğunu önceden bilmiyorsunuz.
Gri

6

Daha karmaşık tür doğrulamaları için , python türü ipucu ek açıklamalarına dayanarak tipguard'ın doğrulama yaklaşımını seviyorum :

from typeguard import check_type
from typing import List

try:
    check_type('mylist', [1, 2], List[int])
except TypeError as e:
    print(e)

Çok temiz ve okunaklı bir şekilde çok karmaşık doğrulama işlemleri yapabilirsiniz.

check_type('foo', [1, 3.14], List[Union[int, float]])
# vs
isinstance(foo, list) and all(isinstance(a, (int, float)) for a in foo) 

6

Bir değişkenin __name__ işlevini kullanarak bir değişkenin türünü kontrol edebilirsiniz.

Ör:

>>> a = [1,2,3,4]  
>>> b = 1  
>>> type(a).__name__
'list'
>>> type(a).__name__ == 'list'
True
>>> type(b).__name__ == 'list'
False
>>> type(b).__name__
'int'

Teşekkürler, bu kullanıcıya geri bildirim olarak görüntülerken istediğim gizli kod. Bunu bulmak için çok uzun
sürdüm

5

Hugo'ya:

Muhtemelen listbunun yerine demek istediniz array, ancak bu, tür kontrolüyle ilgili tüm soruna işaret ediyor - söz konusu nesnenin bir liste olup olmadığını bilmek istemiyorsunuz, bunun bir tür sekans mı yoksa tek bir nesne mi olduğunu bilmek istiyorsunuz. Yani bir dizi gibi kullanmaya çalışın.

Nesneyi mevcut bir diziye eklemek istediğinizi veya bir nesne dizisi varsa hepsini ekleyin

try:
   my_sequence.extend(o)
except TypeError:
  my_sequence.append(o)

Bununla ilgili bir hile, dizeler ve / veya diziler dizileriyle çalışıyorsanız - bu bir tizdir, çünkü bir dize genellikle tek bir nesne olarak düşünülür, ancak aynı zamanda bir karakter dizisidir. Bundan daha da kötüsü, gerçekten tek uzunlukta dizelerden oluşan bir dizi.

Genellikle API'mı yalnızca tek bir değeri veya bir diziyi kabul edecek şekilde tasarlamayı tercih ederim - işleri kolaylaştırır. [ ]Gerekirse onu geçtiğinizde tek değerinizin etrafına koymak zor değildir .

(Bu diziler gibi hatalara neden olabilir, ancak diziler gibi görünürler.)


0

Türü kontrol etmenin basit bir yolu, türünü bildiğiniz bir şeyle karşılaştırmaktır.

>>> a  = 1
>>> type(a) == type(1)
True
>>> b = 'abc'
>>> type(b) == type('')
True


-7

Verilen değerin hangi karakter tipinde olduğunu kontrol etmek için aşağıdaki satırla kontrol edebilirsiniz:

def chr_type(chrx):
    if chrx.isalpha()==True:
        return 'alpha'
    elif chrx.isdigit()==True:
        return 'numeric'
    else:
        return 'nothing'

chr_type("12)

3
Bu cevabı @Venkatesan silmek istemediğinizden emin misiniz?
Gri
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.