Bir nesnenin bir liste veya grup olup olmadığını nasıl kontrol edebilirim (ancak dize değil)?


444

Girişin a list/ tuple- olduğunu değil, ancak a olduğunu belirlemek için normalde yaptığım budur str. Çünkü çoğu zaman bir fonksiyonun bir strnesneyi yanlışlıkla geçtiği hatalara rastladım ve hedef fonksiyon bunun aslında bir ya da for x in lstolduğunu varsayar .lstlisttuple

assert isinstance(lst, (list, tuple))

Sorum şu: Bunu başarmanın daha iyi bir yolu var mı?


9
tür (lst) liste mi?
jackalope

1
isinstance (key, six.string_types)
wyx

Yanıtlar:


332

Yalnızca python 2'de (python 3 değil):

assert not isinstance(lst, basestring)

Eğer istediğiniz aksi takdirde listeleri gibi hareket bir çok şey kaçırmış olacak, fakat alt sınıfları değil aslında ne listya tuple.


91
Evet, doğru cevap bu. Python 3'te, basestringgitti ve sadece kontrol et isinstance(lst, str).
steveha

5
Listeler gibi yineleyebileceğiniz birçok şey vardır, örneğin set, jeneratör ifadeleri, yineleyiciler. Egzotik şeyler var, listeler gibi davranan mmapdaha az egzotik şeyler arrayve muhtemelen unuttuğum çok daha fazlası.
Nick Craig-Wood

50
lstOrijinalin yaptığı gibi, bunun yinelenebilir olduğunu garanti etmediğini belirtmek gerekir (örneğin, bir int bu kontrolü geçecektir)
Peter Gibson

11
@PeterGibson - İkisinin bir kombinasyonu geçerli, daha kısıtlayıcı bir kontrol sağlayacak ve 1) lst'in tekrarlanabilir, 2) lst bir dize olmamasını sağlayacaktır. assert isinstance(lst, (list, tuple)) and assert not isinstance(lst, basestring)
strongMA

4
Peki, bu çözüm yalnızca dizeden türetilen türleri kontrol eder, ancak tamsayılar, çiftler veya diğer yinelenemeyen türler ne olacak?
Eneko Alonso

171

Python'da "ördek yazmayı" kullanmak istediğimizi unutmayın. Dolayısıyla liste gibi davranan her şey liste olarak kabul edilebilir. Bu nedenle, bir listenin türünü kontrol etmeyin, liste olarak davranıp davranmadığına bakın.

Ancak dizeler de bir liste gibi davranır ve genellikle istediğimiz bu değildir. Hatta bir sorun olduğu zamanlar var! Bu nedenle, açıkça bir dize olup olmadığını kontrol edin, ancak ördek yazmayı kullanın.

İşte eğlence için yazdığım bir fonksiyon. repr()Köşeli parantez içindeki herhangi bir diziyi ('<', '>') basan özel bir sürümüdür .

def srepr(arg):
    if isinstance(arg, basestring): # Python 3: isinstance(arg, str)
        return repr(arg)
    try:
        return '<' + ", ".join(srepr(x) for x in arg) + '>'
    except TypeError: # catch when for loop fails
        return repr(arg) # not a sequence so just return repr

Bu genel olarak temiz ve zarif. Ama bu isinstance()çek orada ne yapıyor? Bu bir tür hack. Ancak bu çok önemlidir.

Bu işlev, liste gibi davranan her şeyi özyinelemeli olarak çağırır. Dizeyi özel olarak ele almazsak, bir liste gibi ele alınır ve her seferinde bir karakter bölünür. Ama sonra özyinelemeli çağrı her karaktere bir liste gibi davranmaya çalışır - ve işe yarar! Tek karakterli bir dize bile liste olarak çalışır! Yığın taşana kadar işlev özyinelemeli olarak çağrılmaya devam eder.

Yapılacak olan işi parçalayan her özyinelemeli çağrıya bağlı olan bu gibi işlevler, özel durum dizeleri gerektirir - çünkü tek karakterli bir dize ve hatta bir dizenin seviyesinin altındaki bir dizeyi parçalayamazsınız. -character string bir liste gibi davranır.

Not: try/ except, niyetlerimizi ifade etmenin en temiz yoludur. Ancak bu kod bir şekilde zaman açısından kritikse, argbir dizi olup olmadığını görmek için bir tür testle değiştirmek isteyebiliriz . Türü test etmek yerine, muhtemelen davranışları test etmeliyiz. Bir .strip()yöntemi varsa, bir dizedir, bu yüzden bunu bir dizi olarak düşünmeyin; aksi takdirde, dizine eklenebilir veya yinelenebilirse, bu bir sekanstır:

def is_sequence(arg):
    return (not hasattr(arg, "strip") and
            hasattr(arg, "__getitem__") or
            hasattr(arg, "__iter__"))

def srepr(arg):
    if is_sequence(arg):
        return '<' + ", ".join(srepr(x) for x in arg) + '>'
    return repr(arg)

EDIT: Başlangıçta yukarıda bir çek ile yazdım __getslice__()ama collectionsmodül belgelerinde ilginç yöntemin olduğunu fark ettim __getitem__(); bu mantıklıdır, bir nesneyi bu şekilde endekslersiniz. __getslice__()Yukarıdakileri değiştirdiğimden daha temel görünüyor .


2
@stantonk, bunu söylediğin için teşekkür ederim, ama bunu yazarken zaten kabul edilmiş bir cevap olduğunu düşünüyorum ve kabul edilen cevabın değişmesini beklemiyorum.
steveha

@steveha: sreprçok ilginç bir fikir. Ama özel durum gerekip gerekmediği konusunda sizden farklı bir görüşüm var str. Evet, strsonsuz bir tekrarlamaya neden olabilecek en açık ve en yaygın yinelemedir srepr. Ancak, aynı şekilde davranan (iyi bir sebeple veya nedensiz) kullanıcı tanımlı yinelemeleri kolayca hayal edebiliyorum. Özel durumdan ziyade str, bu yaklaşımın sonsuz bir özyineleme ile karşılaşabileceğini ve bununla başa çıkmanın bir yolunu kabul etmeliyiz. Önerimi bir cevapta göndereceğim.
en fazla

1
Bence bu kesinlikle doğru yol. Ancak, özel durum (bu senaryoda dize) ele almak için, "bir insan farkı nasıl söyler?" Sorusunu sormak daha iyi olduğunu düşünüyorum. Örneğin, e-posta adreslerinin listesi veya tek bir e-posta adresi olabilen bir işlev bağımsız değişkenini düşünün (bir dizenin yalnızca bir karakter listesi olduğunu unutmayın). Bu değişkeni bir insana verin. Hangisi olduğunu nasıl anlayabilirim? Düşünebileceğim en kolay yol, listenin her öğesinde kaç karakter olduğunu görmek. 1'den büyükse, argüman kesinlikle bir karakter listesi olamaz.
Josh

1
Bunu biraz düşündüm ve birkaç başka insanla tartıştım ve sanırım srepr()olduğu gibi iyi. Başka bir listenin içine yerleştirilmiş bir liste gibi şeyleri işlemek için özyinelemeli bir işleve ihtiyacımız var; ancak dizgiler için onları "foo"olduğu gibi yazdırmayı tercih ederiz <'f', 'o', 'o'>. Yani bir dize için açık bir kontrol burada mantıklı. Ayrıca, yinelemenin her zaman yinelemeli döndüğü ve yinelemenin her zaman yığın taşmasına neden olacağı başka veri türü örnekleri yoktur, bu nedenle bunu test etmek için özel bir özelliğe ihtiyacımız yoktur ("Pratiklik saflığı yener").
steveha

1
Bu, Python 3'te çalışmaz, çünkü dizelerin Python 3'te bir __iter__()yöntemi vardır, ancak Python 2'de yoktur. İçinde parantez eksik is_sequence(), şunu okumalıdır:return (not hasattr(arg, "strip") and (hasattr(arg, "__getitem__") or hasattr(arg, "__iter__")))
MiniQuark

124
H = "Hello"

if type(H) is list or type(H) is tuple:
    ## Do Something.
else
    ## Do Something.

11
Diğer yorumcular işaret ettiği gibi ördek yazmanın Python deyimini kullanmazsa da (soruyu doğrudan ve temiz bir şekilde cevaplasa da).
Soren Bjornstad

7
Ördek yazmaya izin vermediği ve basit bir alt sınıflama durumunda başarısız olduğu için bu cevap diğerlerinden daha az kabul edilebilir (tipik bir örnek adlandırılmış sınıf sınıfıdır).
Philippe Gauthier

11
"Ördek yazmasına izin vermemek", özellikle bu cevabın soruyu gerçekten cevapladığı göz önüne alındığında, cevabı daha az kabul edilebilir yapmaz.
Petri

4
Bu cevabı iptal ettim, ancak if isinstance( H, (list, tuple) ): ...daha kısa ve net.
shahar_m

2
Alternatif sözdizimi:if type(H) in [list, tuple]:
Štefan Schindler

77

Python 3 için:

import collections.abc

if isinstance(obj, collections.abc.Sequence) and not isinstance(obj, str):
    print("obj is a sequence (list, tuple, etc) but not a string")

3.3 sürümünde değiştirildi: Koleksiyonlar Özet Temel Sınıflar collections.abc modülüne taşındı. Geriye dönük uyumluluk için, çalışmayı durduracağı 3.8 sürümüne kadar bu modülde de görünmeye devam edecektir.

Python 2 için:

import collections

if isinstance(obj, collections.Sequence) and not isinstance(obj, basestring):
    print "obj is a sequence (list, tuple, etc) but not a string or unicode"

5
Vaov! Bu gerçekten iyi çalışıyor ve diğer doğru cevapların hepsinden çok daha kısa ve öz. Yerleşik tiplerin miras aldığı konusunda hiçbir fikrim yoktu collections.Sequenceama test ettim ve gördüklerini görüyorum. Aynen öyle xrange. Daha da iyisi, bu test dicthem __getitem__ve hem de hariç tutulur __iter__.
Neil Mayhew

Sonucu neden inspect.getmro(list)içermiyor Sequence? Her şeyi göstermediğinde isinstancene yapabileceğimizi nasıl anlamalıyız getmro?
Steve Jorgensen

@SteveJorgensen Yöntem Çözüm Sırası, Python tarafından sınıflarda kullanılacak doğru yöntemi aramak için kullanılan sınıf arama yolunu tanımlar. Sequencesoyut bir sınıftır.
suzanshakya

3
Python3'te isinstance'ı (obj, basestring) isinstance (obj, str) ile değiştirebilirsiniz; bu işe yaramalıdır.
Adrian Keister

2
Python 3'te isinstance (obj, bytes) değil, ihtiyacınız var ... şeylerin bir listesini istiyorsanız ve sadece baytları numaralandırmak için değil ...
Erik Aronesty

35

PHP tadında Python:

def is_array(var):
    return isinstance(var, (list, tuple))

6
Python ördek tipi bir dildir, bu yüzden var'ın özniteliği olup olmadığını gerçekten kontrol etmelisiniz __getitem__. Ayrıca dizi modülü olduğu için isim yanıltıcıdır. Ve var ayrıca bir numpy.ndarray veya başka bir tür olabilir __getitem__. Doğru cevap için stackoverflow.com/a/1835259/470560 adresine bakın .
peterhil

9
@peterhil str, __getitem__bu nedenle çekiniz hariç str
tutulmadı

9
Bir dikte de öyle. Kontrol __getitem__kötü tavsiye burada.
Petri

10

Genel olarak konuşursak, bir nesne üzerinde yinelenen bir fonksiyonun, dizeler, tuples ve listeler üzerinde çalışması, hatadan daha fazla özelliktir. Kesinlikle edebilirsiniz kullanmak isinstanceveya yazarak ördek bir tartışma kontrol etmek, ama neden olmasın?

Bu retorik bir soru gibi geliyor, ama değil. "Neden argümanın türünü kontrol etmeliyim?" muhtemelen algılanan soruna değil, gerçek soruna bir çözüm önerecektir. Bir dize işleve iletildiğinde neden bir hata oluştu? Ayrıca: bu işleve bir dize geçirildiğinde bir hata ise, başka bir liste dışı / tuple yinelenebilir geçirilmesi de bir hata mıdır? Neden ya da neden olmasın?

Sorunun en yaygın cevabının, muhtemelen yazan geliştiricilerin f("abc")işlevin yazmış gibi davranmasını bekledikleri olduğunu düşünüyorum f(["abc"]). Muhtemelen geliştiricileri kendilerinden korumanın, bir karakter dizisindeki karakterler arasında yineleme kullanma durumunu desteklemekten daha mantıklı olduğu durumlar vardır. Ama önce uzun ve sert düşünürdüm.


16
"Ama önce uzun ve sert düşünürdüm." Yapmazdım. Eğer işlevin bir liste-y işlevi olması gerekiyorsa , o zaman evet, onlara aynı şekilde davranmalıdır (yani, bir liste verildiğinde, geriye doğru tükürün, bunun gibi şeyler). Ancak, bağımsız değişkenlerden birinin bir dize veya bir dize listesi olabileceği bir işlevse (bu oldukça yaygın bir ihtiyaçtır), o zaman bu işlevi kullanan geliştiriciyi parametrelerini her zaman bir dizinin içine girmeye zorlamak biraz fazla görünüyor . Ayrıca, JSON girdisini nasıl ele alacağınızı da düşünün. Kesinlikle bir dizeden farklı nesnelerin listesini ele almak istersiniz.
Jordan Reiter

8

Okunabilirlik ve en iyi uygulamalar için bunu deneyin:

Python2

import types
if isinstance(lst, types.ListType) or isinstance(lst, types.TupleType):
    # Do something

Python3

import typing
if isinstance(lst, typing.List) or isinstance(lst, typing.Tuple):
    # Do something

Umarım yardımcı olur.


Python 3.6.5:AttributeError: module 'types' has no attribute 'ListType'
Juha Untinen

1
Python 3'te: from typing import List-> isinstance([1, 2, 3], List= Trueve isinstance("asd", List)= False
Juha Untinen

5

strNesne bir yok __iter__niteliği

>>> hasattr('', '__iter__')
False 

böylece bir çek yapabilirsin

assert hasattr(x, '__iter__')

ve bu aynı zamanda AssertionErrortekrarlanamayan diğer nesneler için de hoş bir artış sağlayacaktır .

Düzenleme: Tim yorumlarda bahsedildiği gibi, bu sadece python 2.x, 3.x çalışır


8
Dikkat: Python 3'te hasattr('','__iter__')geri döner True. Ve elbette bir ip üzerinden yineleyebileceğiniz için mantıklı.
Tim Pietzcker

1
Gerçekten mi? Bunu bilmiyordum. Her zaman sorunun zarif bir çözümü olduğunu düşünmüştüm.
Moe,

1
Bu test pyodbc üzerinde çalışmadı. Hiçbir iter __ () yok ama az çok bir liste gibi davranıyor (hatta "__setitem " tanımlıyor ). Elemanlarını yineleyebilirsiniz. Len () işlevi çalışır ve öğelerini endeksleyebilirsiniz. Tüm liste türlerini yakalayan ancak dizeleri hariç tutan doğru kombinasyonu bulmak için uğraşıyorum. Ben açıkça bazestring hariç iken " getitem " ve " len " üzerinde bir kontrol için yerleşmek düşünüyorum .
haridsv

5

Bu doğrudan OP'ye cevap vermek amaçlı değil, ancak ilgili bazı fikirleri paylaşmak istedim.

Ördek yazmanın kırıldığı bir örnek vermiş gibi görünen yukarıdaki @steveha cevabı çok ilgimi çekti. Düşündüm de, ancak, onun örnek o ördek yazarak uygun zordur önerir, ancak bu gelmez değil önermek strherhangi özel işlem hak ediyor.

Sonuçta, strtür olmayan (örneğin, bazı karmaşık özyinelemeli yapıları koruyan kullanıcı tanımlı bir tür) @steveha sreprişlevinin sonsuz özyinelemeye neden olmasına neden olabilir. Kuşkusuz bu pek olası olmasa da, bu olasılığı görmezden gelemeyiz. Bu nedenle, özel Kılıf yerine strde srepr, ne istediğimizi açıklamak gerekir sreprzaman sonsuz tekrarlama sonuçları yapmak.

Makul bir yaklaşımın şu sreprandaki özyinelemeyi kırmak olduğu anlaşılabilir list(arg) == [arg]. Bu, aslında, problemi str, hiç olmadan , tamamen çözecektir isinstance.

Bununla birlikte, gerçekten karmaşık bir özyinelemeli yapı, list(arg) == [arg]asla gerçekleşmeyen yerlerde sonsuz bir döngüye neden olabilir . Bu nedenle, yukarıdaki kontrol yararlı olsa da, yeterli değildir. Özyineleme derinliğinde zor bir sınır gibi bir şeye ihtiyacımız var.

Demek istediğim, rastgele argüman türlerini ele almayı planlıyorsanız, strördek yazarak işlem yapmak (teorik olarak) karşılaşabileceğiniz daha genel türlerden çok daha kolay. Dolayısıyla str, örnekleri dışlama gereksinimi duyuyorsanız, bunun yerine bağımsız değişkenin açıkça belirttiğiniz birkaç türden birinin örneği olmasını talep etmelisiniz.


1
Hmm, düşünme şeklini seviyorum. Kodumun pratik olduğunu iddia edemezsiniz: Tam olarak bir ortak durum strvar, özel durum kodunun işlediği. Ama belki de kodun inceleyebileceği yeni bir standart özellik olmalı, .__atomic__diyelim ki bir şey daha fazla parçalanamaz. atomic()Python'a başka bir yerleşik işlev eklemek için muhtemelen çok geç , ama belki ekleyebiliriz from collections import atomicveya bir şey.
steveha

5

Tensorflow'da is_sequence adlı böyle bir işlev buluyorum .

def is_sequence(seq):
  """Returns a true if its input is a collections.Sequence (except strings).
  Args:
    seq: an input sequence.
  Returns:
    True if the sequence is a not a string and is a collections.Sequence.
  """
  return (isinstance(seq, collections.Sequence)
and not isinstance(seq, six.string_types))

Ve ihtiyaçlarınızı karşıladığını doğruladım.


2

Bunu testcaslarımda yapıyorum.

def assertIsIterable(self, item):
    #add types here you don't want to mistake as iterables
    if isinstance(item, basestring): 
        raise AssertionError("type %s is not iterable" % type(item))

    #Fake an iteration.
    try:
        for x in item:
            break;
    except TypeError:
        raise AssertionError("type %s is not iterable" % type(item))

Jeneratörlerde test edilmediğinde, bir jeneratöre aktarılırsa bir sonraki 'verimde' kaldığınızı düşünüyorum, bu da işleri aşağı doğru vidalayabilir. Ama sonra tekrar, bu bir 'unittest'


2

"Ördek yazarak" şekilde, nasıl olur

try:
    lst = lst + []
except TypeError:
    #it's not a list

veya

try:
    lst = lst + ()
except TypeError:
    #it's not a tuple

sırasıyla. Bu, isinstance/ hasattrintrospection öğelerini önler .

Bunun tersini de kontrol edebilirsiniz:

try:
    lst = lst + ''
except TypeError:
    #it's not (base)string

Tüm varyantlar aslında değişkenin içeriğini değiştirmez, ancak yeniden atama anlamına gelir. Bazı durumlarda bunun istenmeyen olup olmayacağından emin değilim.

İlginçtir ki, "yerinde" atama ile +=hiçbir TypeErroreğer her durumda zam yapılacak lstbir olan liste (bir tanımlama grubu ). Bu yüzden görev bu şekilde yapılır. Belki birisi bunun nedenine ışık tutabilir.


1

en basit yol ... kullanarak anyveisinstance

>>> console_routers = 'x'
>>> any([isinstance(console_routers, list), isinstance(console_routers, tuple)])
False
>>>
>>> console_routers = ('x',)
>>> any([isinstance(console_routers, list), isinstance(console_routers, tuple)])
True
>>> console_routers = list('x',)
>>> any([isinstance(console_routers, list), isinstance(console_routers, tuple)])
True

1

Dizgi benzeri nesneleri diğer dizi benzeri nesnelerden ayırmaya yardımcı olan ördek yazmanın başka bir sürümü.

Dize benzeri nesnelerin dize temsili dizenin kendisidir, böylece stryapıcıdan eşit bir nesne alıp almadığınızı kontrol edebilirsiniz :

# If a string was passed, convert it to a single-element sequence
if var == str(var):
    my_list = [var]

# All other iterables
else: 
    my_list = list(var)

Bu, strher tür yinelenebilir nesne ile uyumlu ve tüm nesneler için çalışmalıdır .


0

Python 3'de şunlar bulunur:

from typing import List

def isit(value):
    return isinstance(value, List)

isit([1, 2, 3])  # True
isit("test")  # False
isit({"Hello": "Mars"})  # False
isit((1, 2))  # False

Hem Listeleri hem de Tuples'ı kontrol etmek için:

from typing import List, Tuple

def isit(value):
    return isinstance(value, List) or isinstance(value, Tuple)

0
assert (type(lst) == list) | (type(lst) == tuple), "Not a valid lst type, cannot be string"

2
Bunu yapmanın iyi bir yolu var mı?
ersh

1
SO hoş geldiniz. Bu kodun soruyu neden yanıtladığına dair bir açıklama yardımcı olacaktır.
Nick

Tabii ki, boru bir ya da bu şekilde işlendiği için buna benzer yöntemler kullanıyorum, böylece türün hata işleme için özel bir mesaj hatası veren bir liste veya tip tuple olması gerektiğini iddia ediyorsunuz. Soruyu cevapladığına inanıyorum, ama yine de en iyi şekilde kod yazmayı asmaya çalıştığım için bunu yapmanın etkili bir yolu olduğunu merak ettim. Ancak bu kod, listeler / tuples gibi davranabilir, ancak kabul edilen cevap bu olasılığı ele alır gibi, ya da alt sınıfları olmayan şeyleri özlüyorsa emin değilim. Teşekkürler!
ersh

-1

Sadece bunu yap

if type(lst) in (list, tuple):
    # Do stuff

5
isinstance (lst, (list, tuple))
Davi Lima

@DaviLima Tamam, bu başka bir yol. Ancak yerleşik türler ve türler için isinstance için type () önerilir.
ATOzTOA

-1

pitonda> 3.6

import collections
isinstance(set(),collections.abc.Container)
True
isinstance([],collections.abc.Container)
True
isinstance({},collections.abc.Container)
True
isinstance((),collections.abc.Container)
True
isinstance(str,collections.abc.Container)
False

2
Son kontrolde strbir dize değil, bir tür kullanırsınız . Deneyin isinstance('my_string', collections.abc.Container)ve geri döneceğini göreceksiniz True. Bunun nedeni abc.Container, __contains__yöntemi tedarik etmesidir ve dizelerde elbette vardır.
Georgy

-6

Bunu yapma eğilimindeyim (gerçekten, gerçekten yapmam gerekiyorsa):

for i in some_var:
   if type(i) == type(list()):
       #do something with a list
   elif type(i) == type(tuple()):
       #do something with a tuple
   elif type(i) == type(str()):
       #here's your string

5
Bunu neredeyse hiç yapmamalısın. some_varBir alt sınıf olan bir sınıf örneğiysem ne olur list()? "Liste ile bir şeyler yap" kodu altında mükemmel bir şekilde çalışmasına rağmen, kodunuzun onunla ne yapacağına dair hiçbir fikri olmayacaktır. Ve nadiren bir liste ve bir demet arasındaki farkı önemsemeniz gerekir. Üzgünüm -1.
steveha

1
Yazmaya gerek yok type(tuple())- tupleyapacak. Liste için aynı. Ayrıca, gerçek dize türü olan hem strve unicodeuzat basestring, bunun yerine bunu kontrol etmek istiyorsunuz.
Modlarınıza iyi davranın

@DrBloodmoney: Kazara inme. Lütfen (önemsiz olarak) aşağı oyu kaldırmamı sağlamak için yanıtınızı düzenleyin.
SabreWolfy

Eşitlik benim için tipler için anlamlı bir karşılaştırma gibi görünmüyor. Bunun yerine kimlik test ediyorum: type(i) is list. Ayrıca, type(list())sadece listkendisi ... Son olarak, bu alt sınıflarla zarif bir şekilde çalışmaz. Eğer iAslında ve OrderedDict veya namedtuple çeşit olduğunu, tedavi edecek bu kod bir dize gibidir.
bukzor
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.