Nesnenin türü belirlensin mi?


1791

Bir değişkenin liste, sözlük veya başka bir şey olup olmadığını belirlemenin basit bir yolu var mı? Ben her iki tip olabilir bir nesne geri alıyorum ve farkı söyleyebilmek gerekir.


44
Genel olarak size katılıyorum, ancak bilmenin yararlı olduğu durumlar var. Bu özel durumda, sonunda geri aldığım bazı hızlı kesmek oldu, bu yüzden bu sefer haklısın. Ancak bazı durumlarda - örneğin yansıma kullanırken - ne tür bir nesne ile uğraştığınızı bilmek önemlidir.
Justin Ethier

67
@ S.Lott Buna katılmıyorum; türü bilerek, bazı oldukça değişken girdilerle başa çıkabilir ve hala doğru olanı yapabilirsiniz. Saf ördek yazma (örneğin, bir Ağaç üzerindeki .bark () yöntemi, bir Köpeğe göre tamamen farklı bir şey anlamına gelir.) bir dizeyi (örn. yol), yol nesnesini veya listeyi kabul eden dosya. Hepsinin farklı arayüzleri vardır, ancak nihai sonuç aynıdır: bu dosya üzerinde bazı işlemler yapın.
Robert P

22
@ S.Lott Bunun tartışmalı bir örnek olacağının açık olacağını umuyordum; Bununla birlikte, ördek yazmanın önemli bir başarısız noktası ve tryyardım etmeyen bir nokta . Örneğin, bir kullanıcının bir dizeye veya diziye geçebileceğini biliyorsanız, her ikisi de dizine eklenebilir, ancak bu dizin tamamen farklı bir şey anlamına gelir. Bu durumlarda bir deneme yakalamaya güvenmek, beklenmedik ve garip yollarla başarısız olacaktır. Bir çözüm ayrı bir yöntem yapmak, diğeri küçük bir tip kontrolü eklemek. Ben şahsen hemen hemen aynı şeyi yapan birden fazla yöntem üzerinde polimorfik davranışı tercih ederim ... ama bu sadece ben :)
Robert P

22
@ S.Lott, birim testi ne olacak? Bazen testlerinizin bir işlevin doğru türde bir şey döndürdüğünü doğrulamasını istersiniz. Çok gerçek bir örnek, sınıf fabrikanız olduğunda.
Elliot Cameron

17
Daha az tartışmalı bir örnek için, bir serileştirici / serisini kaldırmayı düşünün. Tanımı gereği, kullanıcı tarafından sağlanan nesneler ile serileştirilmiş gösterim arasında dönüştürme yapıyorsunuz. Serileştiricinin geçtiğiniz nesnenin türünü belirlemesi gerekir ve çalışma zamanını sormadan serileştirilmiş türü belirlemek için yeterli bilgiye sahip olmayabilirsiniz (veya en azından, kötü verileri girmeden önce sağlık kontrolü için buna ihtiyacınız olabilir. sisteminiz!)
Karl

Yanıtlar:


1976

Bir nesnenin türünü tanımlamanıza yardımcı olan iki yerleşik işlev vardır. Sen kullanabilirsiniz type() Eğer bir nesnenin tam tür gerekiyorsa ve isinstance()hiç kontrol şeye karşı bir nesnenin türü. Genellikle, isistance()çok sağlam olduğu ve aynı zamanda tip mirasını desteklediği için çoğu zaman kullanmak istersiniz .


Bir nesnenin gerçek türünü elde etmek için yerleşik type()işlevi kullanırsınız. Bir nesneyi tek parametre olarak iletmek, o nesnenin tür nesnesini döndürür:

>>> type([]) is list
True
>>> type({}) is dict
True
>>> type('') is str
True
>>> type(0) is int
True

Bu elbette özel tipler için de geçerlidir:

>>> class Test1 (object):
        pass
>>> class Test2 (Test1):
        pass
>>> a = Test1()
>>> b = Test2()
>>> type(a) is Test1
True
>>> type(b) is Test2
True

Not type()sadece nesnenin hemen türünü dönecek ama tip miras hakkında söylemek mümkün olmayacaktır.

>>> type(b) is Test1
False

Bunu örtmek için isinstanceişlevi kullanmalısınız . Bu elbette yerleşik tipler için de çalışır:

>>> isinstance(b, Test1)
True
>>> isinstance(b, Test2)
True
>>> isinstance(a, Test1)
True
>>> isinstance(a, Test2)
False
>>> isinstance([], list)
True
>>> isinstance({}, dict)
True

isinstance()türetilmiş türleri de kabul edeceği için genellikle bir nesnenin türünü sağlamanın tercih edilen yoludur. Bu nedenle, tür nesnesine gerçekten ihtiyacınız olmadığı sürece (herhangi bir nedenle), kullanmak isinstance()tercih edilir type().

İkinci parametre isinstance()aynı zamanda bir tür grubu kabul eder, bu nedenle aynı anda birden fazla türü kontrol etmek mümkündür. isinstancenesne bu türlerden birindeyse true değerini döndürür:

>>> isinstance([], (tuple, list, set))
True

68
Bence türleri singletons isyerine kullanmak daha net==
John La Rooy

18
@gnibbler, durumlarda, (genel olarak başlangıçta yapmamalıyız olan) typechecking olacağını isinstancetercih formu böylece ne, nasıl olsa edilir ==veya iskullanılabilir gerekmez.
Mike Graham

23
@Mike Graham, typeen iyi cevabın ne zaman olduğu. Zamanlar vardır isinstanceen iyi cevaptır ve ördek yazarak iyi cevaptır zamanlar vardır. Durum için hangisinin daha uygun olduğunu seçebilmeniz için tüm seçenekleri bilmek önemlidir.
John La Rooy

6
@gnibbler, Bu olabilir, ancak henüz type(foo) is SomeTypedaha iyi olacağı duruma girmedim isinstance(foo, SomeType).
Mike Graham

5
@poke: PEP8 hakkında tamamen katılıyorum, ancak burada bir strawman'a saldırıyorsunuz: Sven'in argümanının önemli kısmı PEP8 değildi, ancak usecase'iniz isinstanceiçin de kullanabilirsiniz (bir dizi türü kontrol etmek için) ve bir sözdizimini de temizler, bu da alt sınıfları yakalayabilmeniz için büyük bir avantaja sahiptir. kullanan biri OrderedDictkodunuzun başarısız olması için nefret eder, çünkü sadece saf diktleri kabul eder.
uçan koyun

165

Bunu kullanarak şunları yapabilirsiniz type():

>>> a = []
>>> type(a)
<type 'list'>
>>> f = ()
>>> type(f)
<type 'tuple'>

40

Bir try... exceptbloğu kullanmak daha Pythonic olabilir . Bu şekilde, bir liste gibi ya da bir diksiyon gibi poz veren bir sınıfınız varsa, türünün gerçekte ne olduğuna bakılmaksızın düzgün davranacaktır .

Açıklığa kavuşturmak için, değişken türleri arasındaki "farkı anlatmak" için tercih edilen yöntem, ördek yazma adı verilen bir şeydir : bir değişkenin yanıtladığı yöntemler (ve dönüş türleri), alt rutininizin beklediği gibi olduğu sürece, beklediğiniz gibi davranın olmak. Örneğin, parantez operatörlerini aşırı yükleyen getattrve setattrbazı komik iç şema kullanan bir sınıfınız varsa, taklit etmeye çalıştığı şeyse sözlük olarak davranması uygun olur.

Kontrolün diğer problemi type(A) is type(B), eğer Abir alt sınıf ise , programlı Bolarak falsene zaman olacağını umuyorsunuz true. Bir nesne bir listenin alt sınıfı ise, liste gibi çalışmalıdır: diğer cevapta sunulan türü kontrol etmek bunu engelleyecektir. ( isinstanceancak çalışacaktır).


16
Ördek yazmak gerçekten farkı anlatmakla ilgili değil. Ortak bir arayüz kullanmakla ilgilidir.
Justin Ethier

5
Dikkatli olun - çoğu kodlama stili kılavuzları, kodun normal kontrol akışının bir parçası olarak istisna işlemeyi kullanmamanızı önerir, çünkü genellikle kodu okumayı zorlaştırır. try... excepthatalarla uğraşmak istediğinizde iyi bir çözümdür, ancak türe dayalı davranışa karar verirken değil.
Rens van der Heijden

34

Nesne örneklerinde şunlara da sahipsiniz:

__class__

özniteliği. İşte Python 3.3 konsolundan alınan bir örnek

>>> str = "str"
>>> str.__class__
<class 'str'>
>>> i = 2
>>> i.__class__
<class 'int'>
>>> class Test():
...     pass
...
>>> a = Test()
>>> a.__class__
<class '__main__.Test'>

Python 3.x ve New-Style sınıflarında (isteğe bağlı olarak Python 2.6'dan aviable) sınıf ve türün birleştirildiğine ve bu durumun bazen beklenmedik sonuçlara yol açabileceğine dikkat edin. Temelde bu nedenle türleri / sınıfları test etmenin en sevdiğim yolu, yerleşik işlevdeki isinstance .


2
Sonunda ne demek istediğin çok önemli. type (obj) Sınıf düzgün çalışmıyor, ama isinstance hile yaptı. Yine de isinstance'ın tercih edildiğini anlıyorum, ancak kabul edilen cevapta önerildiği gibi, türetilmiş türleri kontrol etmekten daha faydalıdır.
mstbaum

__class__Python 2.x'te çoğunlukla sorun yok, Python'da __class__niteliği olmayan tek nesne eski tip AFAIK sınıflarıdır. Bu arada Python 3 endişenizi anlamıyorum - böyle bir versiyonda, sadece her nesnenin __class__uygun sınıfa işaret eden bir niteliği vardır.
Alan Franzoni

21

Python nesnesinin türünü belirleme

İle bir nesnenin türünü belirleme type

>>> obj = object()
>>> type(obj)
<class 'object'>

Çalışmasına rağmen, __class__semantik olarak herkese açık olmayan çift ​​alt çizgi özelliklerinden kaçının ve belki de bu durumda olmasa da yerleşik işlevler genellikle daha iyi davranışa sahiptir.

>>> obj.__class__ # avoid this!
<class 'object'>

tip kontrolü

Bir değişkenin liste, sözlük veya başka bir şey olup olmadığını belirlemenin basit bir yolu var mı? Ben her iki tip olabilir bir nesne geri alıyorum ve farkı söyleyebilmek gerekir.

Bu farklı bir soru, türü kullanma - kullan isinstance:

def foo(obj):
    """given a string with items separated by spaces, 
    or a list or tuple, 
    do something sensible
    """
    if isinstance(obj, str):
        obj = str.split()
    return _foo_handles_only_lists_or_tuples(obj)

Bu str, Liskov İkame ilkesine göre, alt sınıf örneklerini kodunuzu kırmadan kullanabilmeniz için, alt sınıflama ile kullanıcılarınızın akıllı veya mantıklı bir şey yapıyor olabileceği durumu kapsar ve bunu isinstancedestekler.

Soyutlamaları Kullan

Daha da iyisi, aşağıdakilerden collectionsveya belirli bir Özet Temel Sınıfı arayabilirsiniz numbers:

from collections import Iterable
from numbers import Number

def bar(obj):
    """does something sensible with an iterable of numbers, 
    or just one number
    """
    if isinstance(obj, Number): # make it a 1-tuple
        obj = (obj,)
    if not isinstance(obj, Iterable):
        raise TypeError('obj must be either a number or iterable of numbers')
    return _bar_sensible_with_iterable(obj)

Veya Açıkça Yazmayın

Ya da belki de en iyisi, ördek yazmayı kullanın ve kodunuzu açıkça yazmayın. Ördek yazma, daha şık ve daha az ayrıntı ile Liskov İkame özelliğini destekler.

def baz(obj):
    """given an obj, a dict (or anything with an .items method) 
    do something sensible with each key-value pair
    """
    for key, value in obj.items():
        _baz_something_sensible(key, value)

Sonuç

  • typeBir örneğin sınıfını almak için kullanın .
  • isinstanceGerçek alt sınıfları veya kayıtlı soyutlamaları açıkça kontrol etmek için kullanın .
  • Ve mantıklı olduğu yerde tip kontrolünden kaçının.

Her zaman try/ exceptaçıkça kontrol etmek yerine vardır .
toonarmycaptain

Muhtemelen kullanıcı, geçecekleri türler hakkında emin değilse yapacaklar. İstisna ile çok iyi bir şey yapmadıkça, istisna işleme ile doğru bir uygulamayı karıştırmak istemiyorum. Ortaya çıkan istisna, kullanıcıyı kullanımlarını düzeltmeleri gerektiğini bildirmek için yeterli olmalıdır.
Aaron Hall

13

Sen kullanabilir type()ya isinstance().

>>> type([]) is list
True

listAynı adın geçerli kapsamına bir değişken atayarak clobber veya başka bir tür kullanabileceğiniz konusunda uyarınız .

>>> the_d = {}
>>> t = lambda x: "aight" if type(x) is dict else "NOPE"
>>> t(the_d) 'aight'
>>> dict = "dude."
>>> t(the_d) 'NOPE'

Yukarıda dict, bir dizeye yeniden atanacağını görüyoruz , bu nedenle test:

type({}) is dict

... başarısız olur.

Bu type()sorunu aşmak ve daha dikkatli kullanmak için :

>>> import __builtin__
>>> the_d = {}
>>> type({}) is dict
True
>>> dict =""
>>> type({}) is dict
False
>>> type({}) is __builtin__.dict
True

2
Yerleşik bir veri türünün adını gölgelendirmenin bu durum için kötü olduğunu belirtmek gerektiğinden emin değilim. Sizin dictdize ayrıca gibi diğer kod sürü başarısız olur dict([("key1", "value1"), ("key2", "value2")]). Bu tür sorunların cevabı "O zaman bunu yapma" . Yerleşik tür adlarını gölgelemeyin ve işlerin düzgün çalışmasını beklemeyin.
Blckknght

3
"Bunu yapma" bölümünde sana katılıyorum. Ama aslında birisine bir şey yapmamasını söylemek en azından neden olmasın diye açıklamalısınız ve bunun sadece bunu yapmak için uygun bir fırsat olduğunu düşündüm. Temkinli yöntemin çirkin görünmesini ve neden bunu yapmak istemediklerini göstermesini ve karar vermelerini sağlamayı amaçladım.
deed02392

type (), klasik örnekler için Python 2.x'te beklendiği gibi çalışmaz.
Alan Franzoni

5

Sorular oldukça eski olsa da, kendime uygun bir yol bulurken bununla karşılaştım ve en azından Python 2.x için hala açıklığa ihtiyacı olduğunu düşünüyorum (Python 3'ü kontrol etmedim, ancak sorun klasik sınıflarla ortaya çıktığından beri bu sürümde yapılmış olan, muhtemelen önemli değil).

Burada başlığın sorusunu cevaplamaya çalışıyorum: keyfi bir nesnenin türünü nasıl belirleyebilirim ? İsinstance kullanmama veya kullanmama ile ilgili diğer öneriler birçok yorum ve cevapta iyidir, ancak bu endişeleri ele almıyorum.

type()Yaklaşımdaki ana sorun, eski stil örnekleriyle düzgün çalışmamasıdır :

class One:
    pass

class Two:
    pass


o = One()
t = Two()

o_type = type(o)
t_type = type(t)

print "Are o and t instances of the same class?", o_type is t_type

Bu pasajı yürütmek aşağıdakileri sağlar:

Are o and t instances of the same class? True

Tartıştığım gibi, çoğu insanın beklediği şey bu değil.

__class__Yaklaşım doğruluğu en yakın, ama bir önemli davada değil çalışma olacak: Bir eski tarz geçirildi, nesne olduğunda sınıf (! Değil bir örneği), bu cisimler böyle niteliğini eksik olur.

Bu, bu meşru soruyu tutarlı bir şekilde tatmin ettiğini düşünebileceğim en küçük kod snippet'i:

#!/usr/bin/env python
from types import ClassType
#we adopt the null object pattern in the (unlikely) case
#that __class__ is None for some strange reason
_NO_CLASS=object()
def get_object_type(obj):
    obj_type = getattr(obj, "__class__", _NO_CLASS)
    if obj_type is not _NO_CLASS:
        return obj_type
    # AFAIK the only situation where this happens is an old-style class
    obj_type = type(obj)
    if obj_type is not ClassType:
        raise ValueError("Could not determine object '{}' type.".format(obj_type))
    return obj_type

5

isinstance kullanmaya dikkat et

isinstance(True, bool)
True
>>> isinstance(True, int)
True

ama yazın

type(True) == bool
True
>>> type(True) == int
False

3

Önceki cevapların yanı sıra, varlığını collections.abcördek yazmayı tamamlayan birkaç soyut temel sınıf (ABC) içeren varlığından bahsetmeye değer .

Örneğin, bir şeyin aşağıdakilerle birlikte bir liste olup olmadığını açıkça kontrol etmek yerine:

isinstance(my_obj, list)

yalnızca sahip olduğunuz nesnenin öğelerin alınmasına izin verip vermediğini görmek istiyorsanız şunları kullanın collections.abc.Sequence:

from collections.abc import Sequence
isinstance(my_obj, Sequence) 

öğelerin alınmasına, ayarlanmasına ve silinmesine izin veren nesnelerle (örn. değiştirilebilir diziler) kesinlikle ilgileniyorsanız , bunu tercih edersiniz collections.abc.MutableSequence.

Diğer birçok ABC, orada tarif edildiği Mappingharitaları olarak kullanılabilir nesneler için Iterable, Callable, vesaire. Bunların tam listesi için belgelerde görülebilir .collections.abc


1

Genel olarak, sınıf adına sahip nesneden bir dize ayıklayabilirsiniz,

str_class = object.__class__.__name__

ve karşılaştırma için kullanmak,

if str_class == 'dict':
    # blablabla..
elif str_class == 'customclass':
    # blebleble..

1

Birçok pratik durumda kullanmak yerine typeya isinstanceda kullanabilirsiniz @functools.singledispatch, bu da genel fonksiyonları tanımlamak için kullanılır ( farklı türler için aynı işlemi uygulayan birden fazla fonksiyondan oluşan fonksiyon ).

Başka bir deyişle, aşağıdakine benzer bir kodunuz olduğunda kullanmak istersiniz:

def do_something(arg):
    if isinstance(arg, int):
        ... # some code specific to processing integers
    if isinstance(arg, str):
        ... # some code specific to processing strings
    if isinstance(arg, list):
        ... # some code specific to processing lists
    ...  # etc

İşte nasıl çalıştığına dair küçük bir örnek:

from functools import singledispatch


@singledispatch
def say_type(arg):
    raise NotImplementedError(f"I don't work with {type(arg)}")


@say_type.register
def _(arg: int):
    print(f"{arg} is an integer")


@say_type.register
def _(arg: bool):
    print(f"{arg} is a boolean")
>>> say_type(0)
0 is an integer
>>> say_type(False)
False is a boolean
>>> say_type(dict())
# long error traceback ending with:
NotImplementedError: I don't work with <class 'dict'>

Ayrıca, aynı anda birkaç türü kapsamak için soyut sınıfları kullanabiliriz :

from collections.abc import Sequence


@say_type.register
def _(arg: Sequence):
    print(f"{arg} is a sequence!")
>>> say_type([0, 1, 2])
[0, 1, 2] is a sequence!
>>> say_type((1, 2, 3))
(1, 2, 3) is a sequence!

0

type()daha iyi bir çözüm isinstance(), özellikle için, booleans:

Trueve Falsesadece anahtar ortalama o vardır 1ve 0içinde piton. Böylece,

isinstance(True, int)

ve

isinstance(False, int)

her ikisi de geri döner True. Her iki boole da bir tamsayı örneğidir. type()ancak, daha zekidir:

type(True) == int

döner False.

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.